I have been using blocks for some time now, but I feel that there are things I miss about memory management in both ARC and non-ARC environments. I feel that deeper understanding will make me void many memory leaks.
AFNetworking is my main use of Blocks in a particular application. Most of the time, inside a completion handler of an operation, I do something like "[self.myArray addObject]".
In both ARC and non-ARC enabled environments, "self" will be retained according to this article from Apple.
That means that whenever a completion block of an AFNetworking network operation is called, self is retained inside that block, and released when that block goes out of scope. I believe that this applies to both ARC and non-ARC. I have ran both the Leaks tool and the Static Analyzer so that I may find any memory leaks. None showed any.
However, it wasn't until recently that I stumbled upon a warning which I couldn't figure out. 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 do 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 calls the failureBlock, with the message "Capturing "self" strongly in this block is likely to result in a retain cycle. I believe Xcode is right: the failure block retains self, and self holds its own copy of the block, so none of the two will be deallocated.
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 that? Is this a memory leak the compiler misses?
2) In general, what is the best practice to work with blocks in both ARC and non-ARC enabled environments when using blocks that are instance variables? Seems that in the case of completion and failure blocks in AFNetworking, those two blocks are not instance variables so they probably don't fall into the category of retain cycles that I described above. But when using progress blocks into AFNetworking, what can be done to avoid retain cycles like the one above?
I would love to hear other people's thoughts on ARC and non-ARC with blocks and issues/solutions with memory management. I find these situations error-prone and I feel some discussion on this is necessary to clear things up.
I don't know if it matters, but I use Xcode 4.4 with the latest LLVM.
1) If i change _failureBlock(error) to "self.failureBlock(error)"
(without quotes) the compiler stops complaining. Why is that? Is this
a memory leak the compiler misses?
The retain cycle exists in both cases. If you're targeting iOS 5+, you can pass in a weak reference to self:
__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 self won't be retained, and if it's deallocated before the callback is invoked, the callback is a no-op. However, it's possible that it could be undergoing deallocation while the callback is invoked on a background thread, so this pattern can create occasional crashes.
2) In general, what is the best practice to work with blocks in both
ARC and non-ARC enabled environments when using blocks that are
instance variables? Seems that in the case of completion and failure
blocks in AFNetworking, those two blocks are not instance variables so
they probably don't fall into the category of retain cycles that I
described above. But when using progress blocks into AFNetworking,
what can be done to avoid retain cycles like the one above?
Most of the time, I think it's better not to store blocks in instance variables. If you instead return the block from a method in your class, you'll still have a retain cycle, but it only exists from the time the method is invoked to the time the block is released. So it will prevent your instance from being deallocated during the block execution, but the retain 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]
];
That means that whenever a completion block of an AFNetworking network
operation is called, self is retained inside that block, and released
when that block goes out of scope.
No, self is retained by the block when the block is created. And it is released when the block is deallocated.
I believe Xcode is right: the failure block retains self, and self
holds its own copy of the block, so none of the two will be
deallocated.
The block in question that retains self is the completion block passed to setCompletionBlockWithSuccess. self does not hold a reference to this block. Rather, self.operation (presumably some kind of NSOperation) retains the blocks while it is executing. So there is temporarily a cycle. However, when the operation is done executing, the cycle will be broken.
1) If i change _failureBlock(error) to "self.failureBlock(error)"
(without quotes) the compiler stops complaining. Why is that? Is this
a memory leak the compiler misses?
There should be no difference. self is captured in both cases. The compiler is not guaranteed to catch all cases of retain cycles.
Related
I noticed Apple's documentation saying we need to avoid strong reference cycles when capturing self.
The block in the example is a property of self.
But what if I put a block as a local variable in a dispatch_async statement?
In this case, it will not create a retain cycle even if I directly call a method of self, right?
Is the following code generating weakSelf required in this article?
// your code
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doThis];
[strongSelf doThat];
[Manager.sharedInstance updateSuccessCount];
}
});
// more code
In the example given, using dispatch_async, there would be no retain cycle, so it would be safe to capture self strongly here. The only difference would be that if self were released by everything else between when this dispatch_async was called and when the block actually ran, it would slightly delay the actual deallocation of self for that brief time, and could affect which thread the deallocation actually occurs on as well. But in general there's no harm in doing it either way when using dispatch_async.
One case where you may want to do it the way your example is written above is if the code run in the block is somewhat expensive and would only want to be done if absolutely necessary, and that it's unnecessary if self had already been released by everything else.
Another case may be if self uses a large amount of memory and gets deallocated normally right before something else starts consuming a lot of memory. In that case you may not want those two instances to be allocated at the same time.
Is there ever a case where purposely creating a retain cycle to prevent deallocation, then cleaning it up later, is the best solution to a problem?
If so, are there examples of this in the Cocoa Touch or NextStep frameworks?
I intend this question to be specific to Objective C with ARC since Objective C with GC or other languages with GC may behave differently.
Sure. It's actually not all that uncommon, though you may not realize it.
For example, let's assume that my controller is making a network request, and I really need to make sure I handle the response, even if the user has navigated away from that controller.
I might do something like this:
- (void)doNetworkThing {
__block MyController *blockSelf = self;
NSURLRequest *request = // some request
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:
^(NSURLResponse *response, NSData *data, NSError *error) {
// Handle the response here
[blockSelf doThingWithResponse:response];
}];
}
This introduces a trivial retain cycle where self has caused itself to be retained by assigning itself to the strong pointer blockSelf. Until blockSelf goes out of scope, self will not be deallocated.
Note that often, you would use a weak pointer in this situation. But if you really need the controller to be around to handle it, using a strong pointer works too. As soon as the handler block is deallocated, its reference to blockSelf will go away. Since the the stack reference to blockSelf is also gone self will then be deallocated if nobody else is holding on to it.
So basically, blockSelf caused a temporary retain cycle, which was useful in ensuring that deallocation could not happen until the request finished. Because ARC automatically cleans up the retain counts when the __block variable goes out of scope, it doesn't look much like a retain cycle. But nevertheless, that's what it is.
Sure, there are several. Most notably NSURLConnection generates a retain loop with its delegate to ensure that the delegate cannot go away before the connection completes. Somewhat less ideally, NSTimer generates a retain loop with its target. This unfortunately causes problems for repeating timers (see RNTimer for one of many attempts to work around this issue).
Generally speaking, if a delegating object has a very short lifespan compared to its delegate, it is natural for it to retain its delegate in order to create a beneficial retain loop.
It is much less common, but still done, for certain kinds of objects to "self retain." For example, if you had an object that you wanted to create and have some operation perform, and then destroy itself, then self retain can solve that problem. Typically it is better to avoid that and let the caller retain the object, but it still has its uses (I've used it for logging and other non-critical operations that I want to fire-and-forget).
That said, it is not uncommon in my code to intentionally create short-lived retain loops with blocks to ensure that the calling object cannot vanish before the block finishes.
I'm using the Allocations instrument in Xcode to track an object that is being created but not released. I can see the object count increase as the code executes, and I can see where the objects are being created using the call stack, but I can't tell where the object references are being held. I think they're being held in a 3rd-party library, but the developer says "no, it must be your code". I'm setting the references in my code to nil, but the objects remain.
I don't think there exists a tool that will tell you where each reference is at any point in time. As you're using ARC, in my experience, there are two fairly common ways to get yourself caught in a reference cycle and leak memory:
Class A has a strong reference to an instance of class B, which has a strongly reference to the same instance of class A.
Blocks implicitly retain references to objects it captures. So one obvious gotcha is when your object retains a block which retains a reference to self.
Example:
self.retainedBlock = ^{
[self doSomething];
};
Fix:
__weak id weakSelf = self;
self.retainedBlock = ^{
id strongSelf = weakSelf;
[strongSelf doSomething];
};
It's a desperation ploy but what you could do is:
disable ARC for the affected object (or work around it; see below);
add a logging retain, autorelease and release;
trace a known over-retained object through your logs.
The simplest example:
- (void)release
{
NSLog(#"%# : %#", self, [NSThread callStackSymbols]);
[super release];
}
(though, in practice, it's probably a lot smarter to keep an array of [NSThread callStackSymbols] for the memory management calls in a dictionary indexed by e.g. [NSValue valueWithPointer:self], then to access that either from lldb or by writing it to disk; why bother effectively indexing the data by hand?)
Probably the easiest way to work around ARC is to write a single class with ARC disabled that can method swizzle your logging memory management methods onto the objects you want to track.
Examine the following code, and assume it was compiled under ARC:
- (void)foo {
NSOperationQueue *oq = [[NSOperationQueue alloc] init];
[oq addOperationWithBlock:^{
// Pretend that we have a long-running operation here.
}];
}
Although the operation queue is declared as a local variable, its lifetime continues beyond the scope of the method as long as it has running operations.
How is this achieved?
UPDATE:
I appreciate Rob Mayoff's well-thought-out comments, but I think I did not ask my question correctly. I am not asking a specific question about NSOperationQueue, but rather a general question about object lifetime in ARC. Specifically, my question is this:
How, under ARC, can an object participate in the management of its own lifetime?
I've been a programmer for a very long time, and I'm well aware of the pitfalls of such a thing. I am not looking to be lectured as to whether this is a good or bad idea. I think in general it is a bad one. Rather, my question is academic: Whether it's a good or bad idea or not, how would one do this in ARC and what is the specific syntax to do so?
As a general case you can keep a reference to yourself. E.g.:
#implementation MasterOfMyOwnDestiny
{
MasterOfMyOwnDestiny *alsoMe;
}
- (void) lifeIsGood
{
alsoMe = self;
}
- (void) woeIsMe
{
alsoMe = nil;
}
...
#end
Here are a few possibilities:
The NSOperationQueue retains itself until it is empty, then releases itself.
The NSOperationQueue causes some other object to retain it. For example, since NSOperationQueue uses GCD, perhaps addOperationWithBlock: looks something like this:
- (void)addOperationWithBlock:(void (^)(void))block {
void (^wrapperBlock)(void) = ^{
block();
[self executeNextBlock];
};
if (self.isCurrentlyExecuting) {
[self.queuedBlocks addObject:wrapperBlock];
} else {
self.isCurrentlyExecuting = YES;
dispatch_async(self.dispatchQueue, wrapperBlock);
}
}
In that code, the wrapperBlock contains a strong reference to the NSOperationQueue, so (assuming ARC), it retains the NSOperationQueue. (The real addOperationWithBlock: is more complex than this, because it is thread-safe and supports executing multiple blocks concurrently.)
The NSOperationQueue doesn't live past the scope of your foo method. Maybe by the time addOperationWithBlock: returns, your long-running block has already been submitted to a GCD queue. Since you don't keep a strong reference to oq, there is no reason why oq shouldn't be deallocated.
In the example code give, under ARC, the NSOperationQueue, being local to the enclosing lexical scope of the block, is captured is captured by the block. Basically, the block saves the value of the pointer so it can be accessed from within the block later. This actually happens regardless of whether you're using ARC or not; the difference is that under ARC, object variables are automatically retained and released as the block is copied and released.
The section "Object and Block Variables" in the Blocks Programming Topics guide is a good reference for this stuff.
The simplest thing I can think of would be to have a global NSMutableArray (or set, or whatever) that the object adds itself to and removes itself from. Another idea would be to put the (as you've already admitted) oddly-memory-managed code in a category in a non-ARC file and just use -retain and -release directly.
When compiling with ARC, method arguments often appear to be retained at the beginning of the method and released at the end. This retain/release pair seems superfluous, and contradicts the idea that ARC "produces the code you would have written anyway". Nobody in those dark, pre-ARC days performed an extra retain/release on all method arguments just to be on the safe side, did they?
Consider:
#interface Test : NSObject
#end
#implementation Test
- (void)testARC:(NSString *)s
{
[s length]; // no extra retain/release here.
}
- (void)testARC2:(NSString *)s
{
// ARC inserts [s retain]
[s length];
[s length];
// ARC inserts [s release]
}
- (void)testARC3:(__unsafe_unretained NSString *)s
{
// no retain -- we used __unsafe_unretained
[s length];
[s length];
// no release -- we used __unsafe_unretained
}
#end
When compiled with Xcode 4.3.2 in release mode, the assembly (such that I'm able to understand it) contained calls to objc_retain and objc_release at the start and end of the second method. What's going on?
This is not a huge problem, but this extra retain/release traffic does show up when using Instruments to profile performance-sensitive code. It seems you can decorate method arguments with __unsafe_unretained to avoid this extra retain/release, as I've done in the third example, but doing so feels quite disgusting.
See this reply from the Objc-language mailing list:
When the compiler doesn't know anything about the
memory management behavior of a function or method (and this happens a
lot), then the compiler must assume:
1) That the function or method might completely rearrange or replace
the entire object graph of the application (it probably won't, but it
could). 2) That the caller might be manual reference counted code, and
therefore the lifetime of passed in parameters is not realistically
knowable.
Given #1 and #2; and given that ARC must never allow an object to be
prematurely deallocated, then these two assumptions force the compiler
to retain passed in objects more often than not.
I think that the main problem is that your method’s body might lead to the arguments being released, so that ARC has to act defensively and retain them:
- (void) processItems
{
[self setItems:[NSArray arrayWithObject:[NSNumber numberWithInt:0]]];
[self doSomethingSillyWith:[items lastObject]];
}
- (void) doSomethingSillyWith: (id) foo
{
[self setItems:nil];
NSLog(#"%#", foo); // if ARC did not retain foo, you could be in trouble
}
That might also be the reason that you don’t see the extra retain when there’s just a single call in your method.
Passing as a parameter does not, in general, increase the retain count. However, if you're passing it to something like NSThread, it is specifically documented that it will retain the parameter for the new thread.
So without an example of how you're intending to start this new thread, I can't give a definitive answer. In general, though, you should be fine.
Even the answer of soul is correct, it is a bit deeper than it should be:
It is retained, because the passed reference is assigned to a strong variable, the parameter variable. This and only this is the reason for the retain/release pair. (Set the parameter var to __weak and what happens?)
One could optimize it away? It would be like optimizing every retain/release pairs on local variables away, because parameters are local variables. This can be done, if the compiler understands the hole code inside the method including all messages sent and functions calls. This can be applied that rarely that clang even does not try to do it. (Imagine that the arg points to a person (only) belonging to a group and the group is dealloc'd: the person would be dealloc'd, too.)
And yes, not to retain args in MRC was a kind of dangerous, but typically developers know their code that good, that they optimized the retain/release away without thinking about it.
It will not increment behind the scenes. Under ARC if the object is Strong it will simply remain alive until there are no more strong pointers to it. But this really has nothing to do with the object being passed as a parameter or not.