as the question described, will the wSelf in block become dangling?
1 void bindDefaultCallbacks {
2 __weak typeof(self) wSelf = self;
2 [sessionManager setDataTaskDidReceiveDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSData * _Nonnull data) {
4 __strong typeof(self) sSelf = wSelf;
5 if (!sSelf) {
6 return;
7 }
8
9 // will wSelf here become dangling ?
10 APIRequest *apiRequest = [wSelf apiRequestForTask:dataTask];
11 NSURLResponse *response = dataTask.response;
12 apiRequest.urlResponse = response;
13 }];
14 }
crash info:
Exception Codes: SEGV_ACCERR at 0x0000000000000010
Exception Type: SIGSEGV
0 XXX ___73-[XXXX bindDefaultCallbacks:withSessionManager:]_block_invoke.251 (in XXX) 420
1 XXX -[AFURLSessionManager URLSession:dataTask:didReceiveData:] (in XXX) (AFURLSessionManager.m:1156) 20
At line 10, you will not have a dangling pointer. ARC will ensure that the pointer is valid or nil (Chris's reference explains this). However, it is possible that at line 10, the pointer will be nil. There is no promise that the sSelf
reference will continue after line 5 since it is not used after that.
There is also no promise that that wSelf
will be nil at line 10. Either situation is valid. It is only promised that it is either a valid pointer to self
or nil.
The formal definition of this rule comes from the Clang documentation discussion of Precise Lifetime Semantics (emphasis added):
In general, ARC maintains an invariant that a retainable object pointer held in a
__strong
object will be retained for the full formal lifetime of the object. Objects subject to this invariant have precise lifetime semantics.By default, local variables of automatic storage duration do not have precise lifetime semantics. Such objects are simply strong references which hold values of retainable object pointer type, and these values are still fully subject to the optimizations on values under local control.
A local variable of retainable object owner type and automatic storage duration may be annotated with the
objc_precise_lifetime
attribute to indicate that it should be considered to be an object with precise lifetime semantics.
See the surrounding section on "Object liveness" for more explanation.
While it would be very difficult for this particular code to fail (the object would need to be deallocated on a different thread during a very brief window between line 5 and line 10), this general situation is actually a fairly common cause of bugs. In debug builds, strong references tend to live until the end of their scope, but in optimized builds, they tend to be released as quickly as possible. So code that works fine during development can fail (or even crash if you're using raw pointers into objects you believe are being retained) when shipped.
This bug was so common that there are annotations in UIKit to help prevent it. If you look at UIColor.h, you'll see the CGColor
property defined this way:
@property(nonatomic,readonly) CGColorRef CGColor;
- (CGColorRef)CGColor NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
The NS_RETURNS_INNER_POINTER
is a warning to ARC that this pointer should cause the object's lifetime to be extended even though normally ARC wouldn't require it. (This was an obnoxious and common cause of crashes in the early days of ARC.)