Objective-C memory management with blocks, ARC, and non-ARC

I have been using blocks for some time, but I feel that there are things that I am missing due to memory management in both ARC and non-ARC environments. I feel that a deeper understanding will make me lose many memory leaks.

AFNetworking is my main use of Blocks in a particular application. In most cases, inside the operation completion handler, I do something like "[self.myArray addObject]".

In environments with ARC support and without ARC, "self" will be retained in accordance with this article from Apple .

This means that whenever the AFNetworking network operation completion block is called, self is stored inside this block and freed when this block goes out of scope. I believe this applies to both ARC and non-ARC. I ran the Leaks tool and the Static Analyzer tool to find memory leaks. No one showed.

However, only recently I came across a warning that I could not understand. I am using ARC in this particular example.

I have two instance variables that indicate the completion and failure of a network operation.

@property (nonatomic, readwrite, copy) SFCompletionBlock completionBlock; @property (nonatomic, readwrite, copy) SFFailureBlock failureBlock; @synthesize failureBlock = _failureBlock; @synthesize operation = _operation; 

Somewhere in the code, I am doing this:

 [self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}]; _failureBlock(error); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"nothing"); }]; 

Xcode complains about the line that causes failBlock, because the message "Capturing" self "strongly" in this block can lead to a save loop. I believe Xcode is right: the failure block retains self, and self holds its own copy of the block, so none of them will be freed.

However, I have the following questions / observations.

1) If I change _failureBlock (error) to "self.failureBlock (error)" (without quotes), the compiler stops complaining. Why is this? Is this a memory leak that the compiler is missing?

2) In general, what is the best way to work with blocks in environments with ARC support and without ARC when using blocks that are instance variables ? It seems that in the case of AFNetworking termination and failure blocks, these two blocks are not instance variables, so they probably do not fall into the category of retention cycles described above. But when using progress blocks in AFNetworking, what can be done to avoid saving loops like the ones above?

I would like to hear other people's thoughts on ARC and non-ARC with blocks and problems / solutions with memory management. I find these situations error-prone, and I feel that a discussion of this is necessary in order to clarify the situation.

I don't know if this matters, but I am using Xcode 4.4 with the latest LLVM.

+6
source share
2 answers

1) If I change _failureBlock (error) to "self.failureBlock (error)" (without quotes), the compiler stops complaining. Why is this? Is this a memory leak that the compiler is missing?

In both cases, a retention cycle is maintained. If you focus on iOS 5+, you can pass a weak link to yourself:

 __weak MyClass *weakSelf; [self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}]; if (weakSelf.failureBlock) weakSelf.failureBlock(error); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"nothing"); }]; 

Now it will not be saved, and if it is released before the callback is called, the callback will be absent. However, it is possible that it may undergo a release while the callback is called in the background thread, so this pattern may cause random failures.

2) In general, what is best suited for working with blocks in both ARCs and non-ARC environments when using blocks that have instance variables? It seems that in the case of block completion and failure in AFNetworking, these two blocks are not instance variables, so they probably do not fall into the category of hold cycles that I described above. But when using progress blocks in AFNetworking, what can be done to avoid saving cycles like the one above?

In most cases, I think it’s better not to store blocks in instance variables . If you instead return a block from a method in your class, you will still have a save cycle, but it exists only from the moment the method is called until the block is released. Thus, this will prevent the release of your instance during block execution, but the save cycle ends when the block is released:

 -(SFCompletionBlock)completionBlock { return ^(AFHTTPRequestOperation *operation , id responseObject ) { [self doSomethingWithOperation:operation]; }; } [self.operation setCompletionBlockWithSuccess:[self completionBlock] failure:[self failureBlock] ]; 
+6
source

This means that whenever an AFNetworking network termination block is invoked, self is stored inside this block and freed when this block is out of scope.

No, self saved by the block when the block is created. And he is freed when the block is freed.

I believe that Xcode is right: the failure block retains itself, and itself has its own copy of the block, so none of them will be redistributed.

This block, which saves self , is the completion block passed to setCompletionBlockWithSuccess . self does not contain a link to this block. Rather, self.operation (presumably some kind of NSOperation ) saves blocks at runtime. So there is a time cycle. However, when the operation is performed, the cycle will be broken.

1) If I change _failureBlock (error) to "self.failureBlock (error)" (without quotes), the compiler stops complaining. Why is this? Is this a memory leak that the compiler is missing?

There should be no difference. self is fixed in both cases. The compiler does not guarantee that it will catch all cases of save cycles.

+2
source

Source: https://habr.com/ru/post/921611/


All Articles