Home > Software design >  Will weak pointer in a block become dangling when I refer it with stong pointer in OC?
Will weak pointer in a block become dangling when I refer it with stong pointer in OC?

Time:07-21

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

CodePudding user response:

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.)

CodePudding user response:

According to the Apple Documentation the weak self pointer will not become dangling:

By capturing the weak pointer to self, the block won’t maintain a strong relationship back to the XYZBlockKeeper object. If that object is deallocated before the block is called, the weakSelf pointer will simply be set to nil.

The block in the sample code captures a strong reference to self so if sSelf is non-nil then wSelf should remain non-nil for the life of the block. However, after capturing a strong reference to self, it's typically preferable to exclusively use the strong reference inside the block.

  • Related