Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 77745c5

Browse files
author
Scott Goodson
committed
Properly support operating with nil asyncDelegate for Table & Collection.
1 parent 67cf6d3 commit 77745c5

File tree

2 files changed

+34
-21
lines changed

2 files changed

+34
-21
lines changed

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,14 @@ static BOOL _isInterceptedSelector(SEL sel)
6161
*/
6262
@interface _ASCollectionViewProxy : NSProxy
6363
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor;
64+
@property (nonatomic, weak) id<NSObject> target;
6465
@end
6566

6667
@implementation _ASCollectionViewProxy {
6768
id<NSObject> __weak _target;
6869
ASCollectionView * __weak _interceptor;
6970
}
71+
@synthesize target = _target;
7072

7173
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor
7274
{
@@ -202,6 +204,14 @@ - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionVi
202204
// and should not trigger a relayout.
203205
_ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero);
204206

207+
// Set up the delegate / dataSource proxy now, so we recieve key method calls from UITableView even if
208+
// our owner never sets up asyncDelegate (technically the dataSource is required)
209+
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:[NSNull null] interceptor:self];
210+
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
211+
212+
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:[NSNull null] interceptor:self];
213+
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
214+
205215
self.backgroundColor = [UIColor whiteColor];
206216

207217
[self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"];
@@ -252,22 +262,21 @@ - (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
252262
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
253263
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
254264
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
255-
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
265+
// _proxyDataSource.target in this case because calls to _ASCollectionViewProxy will start failing and cause crashes.
256266

257267
if (asyncDataSource == nil) {
258-
super.dataSource = nil;
268+
_proxyDataSource.target = nil;
259269
_asyncDataSource = nil;
260-
_proxyDataSource = nil;
261270
_asyncDataSourceImplementsConstrainedSizeForNode = NO;
262271
} else {
272+
_proxyDataSource.target = asyncDataSource;
263273
_asyncDataSource = asyncDataSource;
274+
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
275+
264276
// TODO: Support supplementary views with ASCollectionView.
265277
if ([_asyncDataSource respondsToSelector:@selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)]) {
266278
ASDisplayNodeAssert(NO, @"ASCollectionView is planned to support supplementary views by September 2015. You can work around this issue by using standard items.");
267279
}
268-
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
269-
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
270-
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
271280
}
272281
}
273282

@@ -276,19 +285,17 @@ - (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
276285
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
277286
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
278287
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
279-
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
288+
// _proxyDelegate.target in this case because calls to _ASCollectionViewProxy will start failing and cause crashes.
280289

281290
if (asyncDelegate == nil) {
282291
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
283292
// in UIScrollViewAccessibility.
284-
super.delegate = nil;
293+
_proxyDelegate.target = nil;
285294
_asyncDelegate = nil;
286-
_proxyDelegate = nil;
287295
_asyncDelegateImplementsInsetSection = NO;
288296
} else {
297+
_proxyDelegate.target = asyncDelegate;
289298
_asyncDelegate = asyncDelegate;
290-
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
291-
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
292299
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
293300
}
294301
}

AsyncDisplayKit/ASTableView.mm

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ static BOOL _isInterceptedSelector(SEL sel)
5959
*/
6060
@interface _ASTableViewProxy : NSProxy
6161
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor;
62+
@property (nonatomic, weak) id<NSObject> target;
6263
@end
6364

6465
@implementation _ASTableViewProxy {
6566
id<NSObject> __weak _target;
6667
ASTableView * __weak _interceptor;
6768
}
69+
@synthesize target = _target;
6870

6971
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor
7072
{
@@ -218,6 +220,14 @@ - (void)configureWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
218220
// If the initial size is 0, expect a size change very soon which is part of the initial configuration
219221
// and should not trigger a relayout.
220222
_ignoreMaxWidthChange = (_maxWidthForNodesConstrainedSize == 0);
223+
224+
// Set up the delegate / dataSource proxy now, so we recieve key method calls from UITableView even if
225+
// our owner never sets up asyncDelegate (technically the dataSource is required)
226+
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:[NSNull null] interceptor:self];
227+
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
228+
229+
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:[NSNull null] interceptor:self];
230+
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
221231
}
222232

223233
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
@@ -277,16 +287,14 @@ - (void)setAsyncDataSource:(id<ASTableViewDataSource>)asyncDataSource
277287
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
278288
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
279289
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
280-
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
290+
// _proxyDataSource.target in this case because calls to _ASTableViewProxy will start failing and cause crashes.
281291

282292
if (asyncDataSource == nil) {
283-
super.dataSource = nil;
293+
_proxyDataSource.target = nil;
284294
_asyncDataSource = nil;
285-
_proxyDataSource = nil;
286295
} else {
296+
_proxyDataSource.target = asyncDataSource;
287297
_asyncDataSource = asyncDataSource;
288-
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
289-
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
290298
}
291299
}
292300

@@ -295,18 +303,16 @@ - (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
295303
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
296304
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
297305
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
298-
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
306+
// _proxyDelegate.target in this case because calls to _ASTableViewProxy will start failing and cause crashes.
299307

300308
if (asyncDelegate == nil) {
301309
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
302310
// in UIScrollViewAccessibility.
303-
super.delegate = nil;
311+
_proxyDelegate.target = nil;
304312
_asyncDelegate = nil;
305-
_proxyDelegate = nil;
306313
} else {
314+
_proxyDelegate.target = asyncDelegate;
307315
_asyncDelegate = asyncDelegate;
308-
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:asyncDelegate interceptor:self];
309-
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
310316
}
311317
}
312318

0 commit comments

Comments
 (0)