@@ -56,8 +56,16 @@ public struct RenderItem<T> {
5656 /// The frame of the renderable.
5757 public var frame : CGRect
5858
59+ /// The renderable's behavior: the lifecycle callbacks plus the reuse / transition / animation configuration.
60+ ///
61+ /// This is boxed in a reference type so that copying a `RenderItem` — which the render walk does at every container
62+ /// level — is a single retain instead of copying ~10 escaping closures, and so the value type stays small (cheaper to
63+ /// store in, and tear down, the per-pass render dictionaries). `id` and `frame` stay inline because they are mutated
64+ /// per render pass.
65+ private let storage : Storage
66+
5967 /// The block to create a renderable.
60- public let make : ( RenderableMakeContext ) -> T
68+ public var make : ( RenderableMakeContext ) -> T { storage . make }
6169
6270 /// The block to be called when the renderable is just made and is about to be inserted into the renderable hierarchy.
6371 ///
@@ -66,7 +74,7 @@ public struct RenderItem<T> {
6674 /// after the insertion.
6775 ///
6876 /// This is guaranteed to be the first call for the renderable's lifecycle in the renderable hierarchy.
69- public let willInsert : ( ( T , RenderableInsertContext ) -> Void ) ?
77+ public var willInsert : ( ( T , RenderableInsertContext ) -> Void ) ? { storage . willInsert }
7078
7179 /// The block to be called when the renderable is just inserted into the renderable hierarchy.
7280 ///
@@ -76,14 +84,14 @@ public struct RenderItem<T> {
7684 /// same as the new frame, because during the transition animation, the renderable maybe updated for additional changes.
7785 ///
7886 /// This is not guaranteed to be called before the `update` block is called.
79- public let didInsert : ( ( T , RenderableInsertContext ) -> Void ) ?
87+ public var didInsert : ( ( T , RenderableInsertContext ) -> Void ) ? { storage . didInsert }
8088
8189 /// The block to be called when the renderable is about to be updated.
8290 ///
8391 /// At this point, the renderable might be not inserted into the renderable hierarchy yet. The renderable's properties, including its
8492 /// frame, are not updated yet. You can use this block to get the properties of the renderable before the update and use the
8593 /// information to help renderable content update. For example, to get the old properties for animating the renderable's changes.
86- public let willUpdate : ( ( T , RenderableUpdateContext ) -> Void ) ?
94+ public var willUpdate : ( ( T , RenderableUpdateContext ) -> Void ) ? { storage . willUpdate }
8795
8896 /// The block to be called when the renderable's frame is just updated and is ready to be updated for additional changes.
8997 ///
@@ -96,21 +104,21 @@ public struct RenderItem<T> {
96104 /// Note that it is possible that when a renderable is being inserted into the renderable hierarchy with a transition animation, a
97105 /// new update, which is triggered by a `refresh()`, is called on the renderable. In this case, an update call with
98106 /// `RenderableUpdateType.refresh` type will be called before the update call with `RenderableUpdateType.insert` type.
99- public let update : ( T , RenderableUpdateContext ) -> Void
107+ public var update : ( T , RenderableUpdateContext ) -> Void { storage . update }
100108
101109 /// The block to be called when the renderable is about to be removed from the renderable hierarchy.
102110 ///
103111 /// At this point, the renderable is still in the renderable hierarchy, but it is about to be removed, before the transition
104112 /// animation if has one.
105- public let willRemove : ( ( T , RenderableRemoveContext ) -> Void ) ?
113+ public var willRemove : ( ( T , RenderableRemoveContext ) -> Void ) ? { storage . willRemove }
106114
107115 /// The block to be called when the renderable is just removed from the renderable hierarchy.
108116 ///
109117 /// At this point, the renderable is already removed from the renderable hierarchy with its transition animation completed.
110118 ///
111119 /// Note that it is possible that a removing renderable, during its removal transition animation, is re-inserted into the
112120 /// renderable hierarchy. In this case, the `didRemove` block won't be called.
113- public let didRemove : ( ( T , RenderableRemoveContext ) -> Void ) ?
121+ public var didRemove : ( ( T , RenderableRemoveContext ) -> Void ) ? { storage . didRemove }
114122
115123 /// An optional reuse identifier that opts the renderable into the recycle pool.
116124 ///
@@ -120,20 +128,67 @@ public struct RenderItem<T> {
120128 ///
121129 /// To ensure a reused renderable is in the same state a freshly made one would have, use the `resetForReuse` block to
122130 /// reset the renderable's modified properties.
123- let reuseId : ReuseId ?
131+ var reuseId : ReuseId ? { storage . reuseId }
124132
125133 /// The concrete renderable type generated by `ObjectIdentifier(T.self)`.
126- let renderableType : ObjectIdentifier ?
134+ var renderableType : ObjectIdentifier ? { storage . renderableType }
127135
128136 /// The block to be called when the renderable is about to be added to the reuse pool, to reset any modified state so
129137 /// the reused renderable is clean (freshly-made-equivalent) for its next use.
130- public let resetForReuse : ( ( T ) -> Void ) ?
138+ public var resetForReuse : ( ( T ) -> Void ) ? { storage . resetForReuse }
131139
132140 /// The transition of the renderable. The transition is used to animate the renderable's insertion and removal.
133- public let transition : RenderableTransition ?
141+ public var transition : RenderableTransition ? { storage . transition }
134142
135143 /// The animation timing of the renderable. The animation timing is used to animate the renderable's changes.
136- public let animationTiming : AnimationTiming ?
144+ public var animationTiming : AnimationTiming ? { storage. animationTiming }
145+
146+ /// The boxed behavior backing a `RenderItem`. See `storage`.
147+ ///
148+ /// Holds everything except `id` and `frame`. Because all fields are immutable, the box can be shared freely across
149+ /// the copies a render pass makes (containers copy child items to re-position them), so each copy is one retain.
150+ private final class Storage {
151+
152+ let make : ( RenderableMakeContext ) -> T
153+ let willInsert : ( ( T , RenderableInsertContext ) -> Void ) ?
154+ let didInsert : ( ( T , RenderableInsertContext ) -> Void ) ?
155+ let willUpdate : ( ( T , RenderableUpdateContext ) -> Void ) ?
156+ let update : ( T , RenderableUpdateContext ) -> Void
157+ let willRemove : ( ( T , RenderableRemoveContext ) -> Void ) ?
158+ let didRemove : ( ( T , RenderableRemoveContext ) -> Void ) ?
159+ let reuseId : ReuseId ?
160+ let renderableType : ObjectIdentifier ?
161+ let resetForReuse : ( ( T ) -> Void ) ?
162+ let transition : RenderableTransition ?
163+ let animationTiming : AnimationTiming ?
164+
165+ init ( make: @escaping ( RenderableMakeContext ) -> T ,
166+ willInsert: ( ( T , RenderableInsertContext ) -> Void ) ? ,
167+ didInsert: ( ( T , RenderableInsertContext ) -> Void ) ? ,
168+ willUpdate: ( ( T , RenderableUpdateContext ) -> Void ) ? ,
169+ update: @escaping ( T , RenderableUpdateContext ) -> Void ,
170+ willRemove: ( ( T , RenderableRemoveContext ) -> Void ) ? ,
171+ didRemove: ( ( T , RenderableRemoveContext ) -> Void ) ? ,
172+ reuseId: ReuseId ? ,
173+ renderableType: ObjectIdentifier ? ,
174+ resetForReuse: ( ( T ) -> Void ) ? ,
175+ transition: RenderableTransition ? ,
176+ animationTiming: AnimationTiming ? )
177+ {
178+ self . make = make
179+ self . willInsert = willInsert
180+ self . didInsert = didInsert
181+ self . willUpdate = willUpdate
182+ self . update = update
183+ self . willRemove = willRemove
184+ self . didRemove = didRemove
185+ self . reuseId = reuseId
186+ self . renderableType = renderableType
187+ self . resetForReuse = resetForReuse
188+ self . transition = transition
189+ self . animationTiming = animationTiming
190+ }
191+ }
137192
138193 public init ( id: ComposeNodeId ,
139194 frame: CGRect ,
@@ -151,18 +206,20 @@ public struct RenderItem<T> {
151206 {
152207 self . id = id
153208 self . frame = frame
154- self . make = make
155- self . willInsert = willInsert
156- self . didInsert = didInsert
157- self . willUpdate = willUpdate
158- self . update = update
159- self . willRemove = willRemove
160- self . didRemove = didRemove
161- self . reuseId = reuseId. map { ReuseId ( namespace: . user, id: $0) } // external callers can only set a user-provided identifier
162- self . renderableType = ObjectIdentifier ( T . self)
163- self . resetForReuse = resetForReuse
164- self . transition = transition
165- self . animationTiming = animationTiming
209+ self . storage = Storage (
210+ make: make,
211+ willInsert: willInsert,
212+ didInsert: didInsert,
213+ willUpdate: willUpdate,
214+ update: update,
215+ willRemove: willRemove,
216+ didRemove: didRemove,
217+ reuseId: reuseId. map { ReuseId ( namespace: . user, id: $0) } , // external callers can only set a user-provided identifier
218+ renderableType: ObjectIdentifier ( T . self) ,
219+ resetForReuse: resetForReuse,
220+ transition: transition,
221+ animationTiming: animationTiming
222+ )
166223 }
167224
168225 /// Internal initializer for creating a renderable item with a specified renderable type and reuse identifier.
@@ -184,18 +241,20 @@ public struct RenderItem<T> {
184241 {
185242 self . id = id
186243 self . frame = frame
187- self . make = make
188- self . willInsert = willInsert
189- self . didInsert = didInsert
190- self . willUpdate = willUpdate
191- self . update = update
192- self . willRemove = willRemove
193- self . didRemove = didRemove
194- self . reuseId = reuseId
195- self . renderableType = renderableType
196- self . resetForReuse = resetForReuse
197- self . transition = transition
198- self . animationTiming = animationTiming
244+ self . storage = Storage (
245+ make: make,
246+ willInsert: willInsert,
247+ didInsert: didInsert,
248+ willUpdate: willUpdate,
249+ update: update,
250+ willRemove: willRemove,
251+ didRemove: didRemove,
252+ reuseId: reuseId,
253+ renderableType: renderableType,
254+ resetForReuse: resetForReuse,
255+ transition: transition,
256+ animationTiming: animationTiming
257+ )
199258 }
200259
201260 /// Add an additional will insert block to the renderable item.
0 commit comments