Why not always use weakSelf in a block? - objective-c

I understand WHY we would use weakSelf in a block, just not so much when.
I am converting a codebase to ARC which gives a lot of retain cycle warnings with blocks. From the documentation I've gathered that I need to change this:
[self.selectedAsset addToFavoritesWithCompletion:^(NSError *error) {
self.selectedAsset.isFavorite = YES;
[self updateIsFavoriteButton];
}];
to this:
__weak MyViewController* weakSelf = self;
[self.selectedAsset addToFavoritesWithCompletion:^(NSError *error) {
self.selectedAsset.isFavorite = YES;
[weakSelf updateIsFavoriteButton];
}];
To make the compiler happy and avoid retain loops. My question is why isn't it necessary to change the line:
self.selectedAsset.isFavorite = YES;
to use weakSelf? Doesn't it evaluate to a method call as well? Why doesn't the compiler warn about lines in this format?
[[self selectedAsset]setIsFavorite:YES];
EDIT: I just update to XCode 4.6, and it now generates compiler warnings for just this situation. Funny timing :)

My question is why isn't it necessary to change the line:
self.selectedAsset.isFavorite = YES; to use weakSelf? Doesn't it
evaluate to a method call as well? Why doesn't the compiler warn about
lines in this format?
[[self selectedAsset]setIsFavorite:YES];
Yes, it is exactly a method call. And it does cause a strong reference to self. And it IS necessary to change it to weakSelf if you want it to not retain self.
Compiler warnings do not catch everything.

__weak MyViewController* weakSelf = self;
[self.selectedAsset addToFavoritesWithCompletion:^(NSError *error) {
self.selectedAsset.isFavorite = YES;
[weakSelf updateIsFavoriteButton];
}];
is indeed daft. The completion block referenced self twice, which would create a strong reference. It still references self once, which still creates a strong reference. And weak references are pointless if you are using strong references as well. This should be
__weak MyViewController* weakSelf = self;
[self.selectedAsset addToFavoritesWithCompletion:^(NSError *error) {
weakSelf.selectedAsset.isFavorite = YES;
[weakSelf updateIsFavoriteButton];
}];
or better (because safer)
__weak typeof (self) weakSelf = self;
[self.selectedAsset addToFavoritesWithCompletion:^(NSError *error) {
typeof (self) strongSelf = weakSelf;
strongSelf.selectedAsset.isFavorite = YES;
[strongSelf updateIsFavoriteButton];
}];

Related

weakself inside dispatch_async queue(dispatch_get_main_queue(), ^{})

Below code snippet is in objective C
__weak MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
});
Will the weakSelf always available/valid since it is inside the main
queue?
Do we need to declare strongSelf only when block is other
than the main queue?
Your code snippet is too small to answer on your questions fully.
weakSelf can be both nil or non-nil. The keyword weak means that variable weakSelf can become nil in some cases. For example, if your controller has the following property:
#property (retain) MyView* myView;
In some cases you dismiss this controller and after that you call the method f for myView:
[self dismissViewControllerAnimated:YES completion:^{
[self.myView f];
}];
Code of the method f is based on code snipped you provided in this question:
-(void)f {
[self removeFromSuperview];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
__weak MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
});
});
}
I guess you will see in debugger that the weakSelf will be nil when you will try to call stopAnimating for activityIndicatorView. And I guess you can easily reproduce the situation when weakSelf will be not cleared. It means that the answer on your first question is "No, the weakSelf will be not always available/valid and the main thread does not protect you from nil in this variable"
You need to use strongSelf (__strong instead of __weak) if you don't want to loose a reference to variable inside block. For example, if in the class MyView there is a method log that logs some debug information:
-(void)log {
NSLog(#"LOG");
}
And if you want to log information always after the code in your code snippet will called use the following version of the method f:
-(void)f {
[self removeFromSuperview];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
__strong MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
[weakSelf log];
});
});
}
So, the answer, on your second question is "No, you need to use __strong based on your application, the block can be completed in different threads".

Capturing Self in a double block with ARC

I recently learned this trick for when I have to reference self within a block.
__weak MyObject *safeSelf = self;
[self doWithCompletionBlock:^{
HMFInventoryBatchItemsController *strongSelf = safeSelf;
if (strongSelf) {
[strongSelf doSomethingElse];
}
}];
But I've been wondering, what if I have a block within a block? Do I need to do the same thing again?
__weak MyObject *safeSelf = self;
[self doWithCompletionBlock:^{
HMFInventoryBatchItemsController *strongSelf = safeSelf;
if (strongSelf) {
__weak MyObject *saferSelf = strongSelf;
[strongSelf doAnotherThingWithCompletionBlock:^{
HMFInventoryBatchItemsController *strongerSelf = saferSelf;
if (strongerSelf) {
[strongerSelf doSomethingElse];
}
}];
}
}];
Or is this fine
__weak MyObject *safeSelf = self;
[self doWithCompletionBlock:^{
HMFInventoryBatchItemsController *strongSelf = safeSelf;
if (strongSelf) {
[strongSelf doAnotherThingWithCompletionBlock:^{
[strongSelf doSomethingElse];
}];
}
}];
The good answer is, it depends. It's in general unsafe to do:
__weak MyObject *safeSelf = self;
[self doWithCompletionBlock:^{
[safeSelf doAnotherThingWithCompletionBlock:^{
[safeSelf doSomethingElse];
}];
}];
The reason for that, is that when you call -doAnotherThingWithCompletionBlock if that method thinks it holds a reference on self, which a selector usually assumes, and it dereferences self to access ivars, then you will crash, because you actually don't hold a reference.
So whether you need to take a new weak reference or not depends on the lifetime semantics you need/want.
EDIT:
By the way, clang even has a warning about that issue that you can enable in Xcode with the CLANG_WARN_OBJC_RECEIVER_WEAK setting (CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK is also useful while we're at it)
__block MyObject *safeSelf = self;
[self doWithCompletionBlock:^{
[safeSelf doAnotherThingWithCompletionBlock:^{
[safeSelf doSomethingElse];
}];
}];
Hope it will do what it should normally does. Tell the compiler not to retain the __weak MyObject* no matter in which block scope it is. The fact is i didn't tested. Meanwhile I'll be surprised if it will actually retain MyObject *

How to implement a class method with a block completion handler

I'm trying to implement a fire-and-forget class method similar to
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
in the NSURLConnection, but I'm slightly confused about the memory management (I'm NOT using ARC at the moment).
My current code goes like this:
#interface StuffInfoDownloader() <UIAlertViewDelegate>
typedef void (^StuffInfoDownloaderCompletionBlock)(NSArray *stuffs);
- (id)initStuffsWithIdentifiers:(NSSet *)identifiers
completionHandler:(void (^)(NSArray *stuffs))handler;
#property (retain, nonatomic) StuffInfoDownloaderCompletionBlock completionHandler;
#property (retain, nonatomic) NSSet *identifiers;
#end
#implementation StuffInfoDownloader
#synthesize completionHandler = _completionHandler;
#synthesize identifiers = _identifiers;
+ (void)loadAsynchronouslyWithIdentifiers:(NSSet *)identifiers
completionHandler:(void (^)(NSArray *stuffs))handler
{
StuffInfoDownloader *downloader = [[StuffInfoDownloader alloc] initStuffsWithIdentifiers:identifiers completionHandler:handler];
[downloader downloadStuffs];
[downloader release]; // will retain itself
}
- (id)initStuffsWithIdentifiers:(NSSet *)identifiers
completionHandler:(void (^)(NSArray *stuffs))handler
{
if (!(self = [super init])) {
return nil;
}
[self retain];
_completionHandler = handler;
_identifiers = identifiers;
return self;
}
- (void)downloadStuffs
{
__block StuffInfoDownloader *me = self; // avoid reference cycle between self and the block
[StuffsConnection loadAsynchronouslyWithIdentifiers:self.identifiers completionHandler:
^(NSArray *stuffs, NSError *error) {
if(error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Connection Failed."
message:#"TODO do localised string"
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alert show];
[alert release];
} else {
me.completionHandler(stuffs);
[self release];
}
}];
}
#pragma mark UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
#pragma unused(alertView, buttonIndex)
// try again
[self downloadStuffs];
}
- (void)dealloc
{
[_completionHandler release];
[_identifiers release];
[super dealloc];
}
Basically, I'm passing ownership of the object to itself, and releasing it in the handler. Any problems with that?
There are so many things wrong with this code. Besides the block property needing to be copy. You shouldn't do the [self retain]; and [self release]; (p.s. you missed a [self release] in the error case). That completely goes against the memory management rules. They are completely unnecessary if you do things right. Memory management in Cocoa is completely local -- a function or method needs only care what it does, not what any other code does. init has no reason to do [self retain], and does not have to "worry" about what any other code does. Period.
Then the _completionHandler = handler; _identifiers = identifiers; are wrong. The block needs to be copied if you are storing it in an instance variable; and the set needs to be retained or copied. You need to do either _completionHandler = [handler copy]; _identifiers = [identifiers retain]; or use the setter self.completionHandler = handler; self.identifiers = identifiers;.
Then, there is no issue of "retain cycle". A retain cycle requires a cycle -- A retains B, and B retains A. The block retains self, but does self retain the block? I don't see that anywhere. You are simply calling a class method of another class on this block. So you shouldn't do the weak reference. The weak reference is not correct anyway, since there is no guarantee that the current object will be valid by the time the block executes.
It seems that you (incorrectly) did the whole [self retain] thing, all in order to deal with the fact that you (also incorrectly) did not allow the block to retain self, as it should. Just get rid of this weak reference stuff, and get rid of the [self retain] stuff, and then it will not only follow the memory management rules, be more robust, but also look cleaner, simpler, and more understandable.
#property (nonatomic, copy) StuffInfoDownloaderCompletionBlock
completionHandler;
then in init:
self.completionHandler = handler;
You should never retain block if u haven't copied it before, that doesn't make sense .
By the way
if ((self = [super init])) {
/* initialization stuff*/
}
return self;
Seems that your code has lot of retainCycle flaws design

How to clean up memory when custom init fails

When my custom initializer fails, I am supposed to return nil. What is the convention for cleaning up any memory I've allocated in my initializer, that I was expecting would be cleaned up in dealloc?
Here is a contrived example:
- (id)init
{
if ((self = [super init])) {
instanceVar1 = [[NSString alloc] initWithString:#"blah"];
if (bad_thing_oh_noes) {
return nil;
}
}
return self;
}
- (void)dealloc
{
[instanceVar1 release];
[super dealloc];
}
A more realistic circumstance where I can't efficiently check every error condition before I do allocations would be deserializing a complex object containing arrays and the like.
Anyway, do I clean up the allocated memory before returning nil, do I send a dealloc message to self before returning nil, or is all of this managed for me magically?
If an error occurs during an initializer, you should call release on self and return nil.
if (bad_thing_oh_noes) {
[self release];
return nil;
}
Also, you must make sure that it is safe to call dealloc on a partially initialized object.
You should call release only at the point of failure. If you get nil back from the superclass’s initializer, you should not call release.
Normally, you should not throw an exception upon initialization failure.
An example from Handling Initialization Failure:
- (id)initWithURL:(NSURL *)aURL error:(NSError **)errorPtr {
self = [super init];
if (self) {
NSData *data = [[NSData alloc] initWithContentsOfURL:aURL
options:NSUncachedRead error:errorPtr];
if (data == nil) {
// In this case the error object is created in the NSData initializer
[self release];
return nil;
}
// implementation continues...

Retain-Release in Objective-C

I am new to Objective-C and I am confused about this retain-release thing. Are parameters retained automatically? Do I need to release them?
Here is my code. Did I do the retain-release thing (and everything else) correctly?
#import "ACStringTokenizer.h"
#implementation ACStringTokenizer
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (id)initWithStr:(NSString *)theString
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = #" ";
doesReturnDelims = NO;
}
return self;
}
- (id)initWithStr:(NSString *)theString andDelims:(NSString *)theDelimiters
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = [theDelimiters retain];
doesReturnDelims = NO;
}
return self;
}
- (id)initWithStr:(NSString *)theString andDelims:(NSString *)theDelimiters andDoesReturnDelims:(BOOL)returnDelims
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = [theDelimiters retain];
doesReturnDelims = returnDelims;
}
return self;
}
- (int)countTokens
{
return numberOfTokens;
}
- (BOOL)hasMoreTokens
{
return ![queue isEmpty];
}
- (NSString *)nextToken
{
return [queue remove];
}
- (void)dealloc
{
[string release];
[delimiters release];
[queue release];
[super dealloc];
}
#end
Thanks in advance.
P.S. How do I make init with no parameters invalid?
This might be better suited to http://codereview.stackexchange.com?
Anyway, a few points:
You should read up on the concept of Designated Initializer. In your case you'd probably make initWithStr:andDelims:andDoesReturnDelims: the designated initializer. Only this initializer may call [super init]. All other initializers call [self initWithStr:andDelims:andDoesReturnDelims:] instead of [super init].
There are some more elaborate ways to make init invalid, but if you want to disable it I'd simply make it return nil. However, I don't really see a reason why you'd want to do this in this particular case.
The retains in your init methods and the dealloc method seem to be alright. Method parameters are valid until the end of the method, if you want to keep them beyond that, e.g. in instance variables, you need to retain them (which you seem to have done correctly).
However, there's a whole lot of code omitted in your example so obviously I'm only commenting on what you've posted.
A parameter variable is not retained automatically, you only get the object by reference. To keep them you have to retain them, as you did. For me it looks perfectly fine what you did there.