Last active
September 5, 2015 00:29
-
-
Save adamkaplan/bac888e9a0e126086eee to your computer and use it in GitHub Desktop.
Exploratory examination of a way to prevent accidental retain cycles in blocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// YFObjCUtils.c | |
// | |
// Created by Adam Kaplan on 7/31/15. | |
// Licensed under the MIT license. | |
// | |
@implementation UsageExample | |
- (void)usageMethod { | |
// Self must not be captured strongly by the handler block. To do so would create a | |
// retain cycle since the block is retained by self. If self were captured stronly | |
// the desctructor (-dealloc) might never get called. | |
@weakify(self); | |
self.handler = ^{ | |
@strictStrongify(self); | |
// Traditional @strongify(self); would still work when needed. | |
strong_self.showsUsage = YES; // ok! Explicit use of strong reference. | |
self.showsUsage = YES; // exception (except in release builds) | |
}); | |
} | |
@end | |
/* | |
2015-09-04 20:12:16.782 Y! Finance *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Improper use of weakified self-reference sentinel.' | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// YFObjCUtils.h | |
// | |
// Created by Adam Kaplan on 7/31/15. | |
// Licensed under the MIT license. | |
// | |
@weakify(self); | |
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
@strictStrongify(self); | |
strong_self.pollingEnabled = YES; // ok | |
self.pollingEnabled = YES; // exception | |
}); | |
#import "EXTScope.h" | |
#ifndef YFObjCUtils_h | |
#define YFObjCUtils_h | |
extern id sharedSentinelObject(void); | |
#define strictStrongify(...) \ | |
yfmacro_preamble \ | |
metamacro_foreach(yfmacro_strongify_,, __VA_ARGS__) \ | |
metamacro_foreach(yfmacro_setSentinel_,, __VA_ARGS__) \ | |
yfmacro_epilogue | |
#if !defined(NS_BLOCK_ASSERTIONS) | |
#define yfmacro_setSentinel_(INDEX, REF) \ | |
yfmacro_pragma("clang diagnostic push") \ | |
yfmacro_pragma("clang diagnostic ignored \"-Wshadow\"") \ | |
__unsafe_unretained __typeof__(REF) REF = sharedSentinelObject();\ | |
yfmacro_pragma("clang diagnostic push") \ | |
#else // NS_BLOCK_ASSERTIONS | |
// Disable sentinels whenever assertions are disabled. | |
#define yfmacro_setSentinel_(INDEX, REF) \ | |
__strong __typeof__(REF) REF = metamacro_concat(REF, _weak_); | |
#endif // NS_BLOCK_ASSERTIONS | |
#define yfmacro_preamble try {} @catch(...) {} | |
#define yfmacro_epilogue do {} while (0) | |
#define yfmacro_pragma(A) _Pragma (#A) | |
#define yfmacro_concat(A,B) A ## B | |
#define yfmacro_strongify_(INDEX, VAR) \ | |
__strong __typeof__(VAR) metamacro_concat(strong_, VAR) = metamacro_concat(VAR, _weak_); | |
#endif // Pods_YFObjCUtils_h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// YFObjCUtils.c | |
// | |
// Created by Adam Kaplan on 7/31/15. | |
// Licensed under the MIT license. | |
// | |
@interface ProhibitedReference : NSObject | |
+ (instancetype)sharedInstance; | |
@end | |
static ProhibitedReference *sharedInstance; | |
@implementation ProhibitedReference | |
+ (void)load { | |
sharedInstance = [self new]; | |
} | |
+ (instancetype)sharedInstance { | |
return sharedInstance; | |
} | |
- (void)doesNotRecognizeSelector:(SEL)aSelector { | |
NSAssert(false, @"Improper use of weakified self-reference sentinel."); | |
} | |
@end | |
id sharedSentinelObject(void) { | |
return [ProhibitedReference sharedInstance]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The underlying concept here is that being explicit about the your intentions while coding can help identify error cases. A sloppy merge conflict is an easy way for a strong reference to self sneak in :-/