-
Notifications
You must be signed in to change notification settings - Fork 235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bk/performance improvements #272
Conversation
return itemModel | ||
} | ||
private lazy var defaultMonthHeaderItemProvider: (Month) -> AnyCalendarItemModel = | ||
{ [unowned self] month in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be impossible to hit cc @calda
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@calda I mean the unowned should be "safe" / there's no way to crash here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps -- although not sure I understand the motivation to change from capturing individual properties (guaranteed to be safe) to using unowned
instead (potentially unsafe).
You could feasibly construct a case that crashes by doing something like this:
let content = CalendarViewContent(...)
let monthHeaderItemProvider = content.monthHeaderItemProvider
/// ... later, in a different scope, so content itself has been deallocated
monthBackgroundItemProvider(month) // 💥 crash because content no longer exists but there was an unowned reference to it
so the safety depends on the usage of the content.monthHeaderItemProvider
property
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would only be possible internally though, since CalendarViewContent
doesn't expose its item provider closures publicly.
Capturing the individual properties here causes them to be created immediately (which is expensive for date formatters).
lazy var expensiveProperty = ExpensiveObject()
let foo = { [expensiveProperty] in
expensiveProperty.doSomething()
}
// later...
foo()
In this example, expensiveProperty
is initialized when foo
is initialized, not when foo
is called later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense -- I guess in a lot of typical usage we'd be initializing the monthHeaderDateFormatter
for the defaultMonthHeaderItemProvider
but then never even calling the default provider (because the consumer immediately provides a custom provider)
@@ -258,6 +258,10 @@ public final class CalendarView: UIView { | |||
invalidateIntrinsicContentSize() | |||
} | |||
|
|||
// Clear this so that we don't return early in our next layout pass even though our layout | |||
// region might not have changed. | |||
layoutRegion = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we clear this in setContent
, since we want updateVisibleViews(...)
to run on the next layout pass even if our layout region hasn't changed
if currentVisibleItemsDetails.layoutRegion != layoutRegion { | ||
updateVisibleViews(withVisibleItems: currentVisibleItemsDetails.visibleItems) | ||
} | ||
|
||
visibleItemsDetails = currentVisibleItemsDetails | ||
layoutRegion = currentVisibleItemsDetails.layoutRegion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's the big optimization - when we're scrolling, we only need to updateVisibleViews(...)
if we've scrolled far enough to change our "layout region" (month headers, days, etc. enter / exit the viewport)
This reverts commit 343cd82.
Details
This PR makes 2 performance improvements:
DateFormatter
s from getting recreated on everyCalendarViewContent
creationupdateVisibleViews(...)
from getting called if the calendar hasn't scrolled far enough for the visible set of views to have changedRelated Issue
N/A
Motivation and Context
Optimizing host calendar in the Airbnb app
How Has This Been Tested
Example app, profiling.
Types of changes
Checklist