Reputation: 1922
Okay so I might have not fully grasped WHEN I should use weakSelf
in blocks. I know that it is done to prevent retain cycles and what not, but I heard there are some exceptions to this rule.
In the following code, I check to see if an API call failed due to the login session expiring, and then I attempt to re-authenticate the user and retry the API request that failed because of this issue by calling [self sendTask:request successCallback:success errorCallback:errorCallback];
in the re-authentication method's success block:
/*!
* @brief sends a request as an NSHTTPURLResponse. This method is private.
* @param request The request to send.
* @param success A block to be called if the request is successful.
* @param error A block to be called if the request fails.
*/
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
[self parseResponse:response data:data successCallback:success errorCallback:^(NSString *error) {
//if login session expired and getting "not authenticated" error (status 401)
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 401) {
NSLog(@"NOT AUTHENTICATED THO");
AuthenticationHelper* auth = [AuthenticationHelper sharedInstance];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
//attempt to re-authenticate the user
[AuthenticationHelper loginUser:[defaults stringForKey:@"username"] password:[defaults stringForKey:@"password"] successCallback:^(User* u)
{
auth.loggedInUser = u;
NSLog(@"RE-AUTHENTICATION BUG FIX SUCCEEDED");
//attempt to re-try the request that failed due to
[self sendTask:request successCallback:success errorCallback:errorCallback];
} errorCallback:^(NSString *error) {
NSLog(@"RE-AUTHENTICATION BUG FIX FAILED");
}];
}
else {
errorCallback(error);
}
}];
}];
[task resume];
}
Is this bad practice in that it can lead to a retain cycle? If so, why? Should I instead be using [weakSelf sendTask:request successCallback:success errorCallback:errorCallback];
assuming that I do MyAPIInterface *__weak weakSelf = self;
beforehand?
Upvotes: 1
Views: 331
Reputation: 17958
A retain cycle is certainly one risk of strong references though I don't think it applies in this case. A strong reference to self
in these callbacks might extend the life of the self
object until they complete but it looks like the blocks will be deallocated once called or when requests fail at which point they would release their strong references and the self
object could be deallocated.
You may however still see unexpected behavior resulting from these strong references. These strong references could keep this object alive until these requests complete. That could result in an object you expected to have been deallocated remaining in memory and changing application state far in the future when the request finishes.
As one possible example; suppose you start one of these requests and then the user signed out and signed into a different account. The request might fail and then be retried using the second user account's credentials. That's probably not the result the user wanted.
Perhaps that's actually not possible in this application but if you use strong references like this you'll need to consider it, and reconsider it every time you change the way the application interacts with this object. That leads to a class being hard or even dangerous to reuse. If you instead make careful use of strong
and weak
you can write a class which does not become so coupled to the state of the rest of the application.
Correct use of a weak reference in this case might be a little trickier than you expect. Creating a weakly retained version of self
is certainly the right first step:
__weak __typeof__(self) weakSelf = self
That gives us a weakSelf
which will be nil if self
was deallocated before the completion block executes however depending on your concurrency model it might be possible for self
to be deallocated during the execution of the completion block. We therefore usually want to create a strong reference as our first operation within the block so that if self
still exists when the block begins executing it will remain until the block is finished and this strong reference is discarded:
__strong __typeof__(self) strongSelf = weakSelf
A number of objective-c libraries and projects extract these statements into weakify
and strongify
macros which can be helpful.
Upvotes: 1
Reputation: 7552
In this example there would not be a retain cycle because self
isn't holding a reference to the block. When the block completes, it will release its hold on self
, and if that was the last reference, the instance will be deallocated, just as you would expect.
Upvotes: 2