According to the Xcode instruments, my code has a memory leak (at #3). But I get the feeling I'm missing something in my mental model of what's going on, so I have a few questions about the following logic:
__block MyType *blockObject = object; //1
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.selectedObjects containsObject:blockObject]) { //2
[self.selectedObjects removeObject:blockObject];
[[NSNotificationCenter defaultCenter] postNotificationName:ObjectDeselectionNotification object:blockObject]; //3
} else {
[self.selectedObjects addObject:blockCart];
[[NSNotificationCenter defaultCenter] postNotificationName:ObjectSelectionNotification object:blockCart];
}
});
1) I'm using a __block reference because I'm executing this code async and don't want a reference to this variable copied to the heap. Is this a valid usage of __block even though I'm not mutating the variable?
2) Calling self.selectedObjects will create a retain on self. Does the block automatically release this after it has exited?
3) I apparently have a leak at this point, but I'm not exactly sure why. Is NotificationCenter retaining my __block object that is supposed to be disposed of after my block exits?
From the code you've shown, I don't see any problems...
1) Your object would not be "copied" onto the heap - it is already on the heap being an alloc'd object. Rather, it's reference count would be incremented by 1 as it is now owned by the block. You do not need the __block reference as you are not assigning anything to the pointer. In fact, you do not need blockObject at all and can just pass object.
2.) self should be released once the block is done. However, post a notification is synchronous (this block will not finish until all the objects responding to the notification are done).
3.) I'm not sure what the exact implementation that NSNotificationCenter uses, but it doesn't really matter because the posting is synchronous. It will call every observer of your notification and the selectors they want to receive your notification on
It seems as though you are running all this code within another block - can you paste the full method?
Please remove this answer if incorrect (you've already accepted) but I'm not sure you accepted because the answer worked for you.
I don't think you should be referencing self in that block - you will be creating a retain cycle.
__weak YourClass *weakSelf = self;
use weakSelf instead and cut the tie between the creator and the block floating on the dispatch queue?
Related
consider the following method snippet.
- (void) closeSocket {
...
dispatch_async(dispatch_get_main_queue(), ^{
// last message before actual disconnection:
[self.connectionListenerDelegate connectionDisconnected:self];
}
self.connectionListenerDelegate = nil;
...
}
This method of my "socket" implementation class can be called by external object, in some arbitrary thread (main, or other). I wish to only notify my delegate once, on the main thread, and remove the delegate so that other background events and possible incoming data won't reach it.
In other words, I want to make sure connectionDisconnected: is the last call from the socket to the delegate.
I know code blocks capture their environment's variables etc. But will the block capture and retain the self.connectionListenerDelegate when created?
If closeSocket is being called on some background thread, and dispatches the connectionDisconnected: asynchronously on the main thread, and I nullify my weak reference to my delegate right away - maybe the block will have a nil object and won't send its message?
What is the right way to go about this?
I guess I could use the old
[self.connectionListenerDelegate performSelectorOnMainThread:#selector(connectionDisconnected:) withObject:self waitUntilDone:NO];
which retains both the receiver and parameter object (self), but I prefer GCD dispatch_async and I'd like to better understand blocks.
If closeSocket is being called on some background thread, and dispatches the connectionDisconnected: asynchronously on the main thread, and I nullify my weak reference to my delegate right away - maybe the block will have a nil object and won't send its message?
I think that after self.connectionListenerDelegate = nil; runs, all methods in the dispatch method will get the nil reference when accessing the connectionListenerDelegate.
So, the best way to go about this would be to transfer the delegate reference to a temporary object for use inside the block:
id<ConnectionListenerDelegate> *tempDelegateRef = self.connectionListenerDelegate;
dispatch_async(dispatch_get_main_queue(), ^{
// last message before actual disconnection:
[tempDelegateRef connectionDisconnected:self];
}
self.connectionListenerDelegate = nil;
I'm not sure if you need strong/weak references or something like that.
I recently stumbled upon The Deallocation Problem in some Objective-C code. This topic was discussed before on Stack Overflow in Block_release deallocating UI objects on a background thread. I think I understand the problem and its implications, but to be sure I wanted to reproduce it in a little test project. I first created my own SOUnsafeObject (= an object which should always be deallocated on the main thread).
#interface SOUnsafeObject : NSObject
#property (strong) NSString *title;
- (void)reloadDataInBackground;
#end
#implementation SOUnsafeObject
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = #"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], #"Object should always be deallocated on the main thread");
}
#end}]
Now, as expected, if I put [[[SOUnsafeObject alloc] init] reloadDataInBackground]; inside application:didFinishLaunching.. the app crashes after 3 seconds due to the failed assertion. The proposed fix seems to work. I.e. the app doesn't crash anymore if I change the implementation of reloadDataInBackground to:
__block SOUnsafeObject *safeSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
safeSelf.title = #"Retrieved data";
safeSelf = nil;
});
sleep(3);
});
Okay, so it seems like my understanding about the problem and how it can be solved under ARC is correct. But just to be 100% sure.. Let's try the same with an UIViewController (since an UIViewController will probably fill in the role of SOUnsafeObject in real life). The implementation is almost identical to that of the SOUnsafeObject:
#interface SODemoViewController : UIViewController
- (void)reloadDataInBackground;
#end
#implementation SODemoViewController
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = #"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], #"UI objects should always be deallocated on the main thread");
NSLog(#"I'm deallocated!");
}
#end
Now, let's put [[SODemoViewController alloc] init] reloadDataInBackground]; inside application:didFinishLaunching... Hmm, the assertion doesn't fail.. The message I'm deallocated! is printed to the console after 3 seconds so I'm pretty sure the view controller is getting deallocated.
Why is the view controller deallocated on the main thread while the unsafe object is deallocated on a background thread? The code is nearly identical. Does UIKit do some fancy stuff behind the scenes to make sure an UIViewController is always deallocated on the main thread? I'm starting to suspect this since the following snippet also doesn't break my assertion:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
SODemoViewController()
});
If so, is this behavior documented somewhere? Can this behavior be relied upon? Or am I just totally wrong and is there something obvious I'm missing here?
Notes: I'm fully aware of the fact that I can use a __weak reference here, but let's assume the view controller should still be alive to execute our completion code on the main thread. Also, I'm trying to understand the core of the problem here before I circumvent it. I converted the code to Swift and got the same results as in Objective-C (the fix for SOUnsafeObject is there syntactically even uglier).
tl;dr - While I can find no official documentation, the current implementation does indeed ensure that dealloc for UIViewController happens on the main thread.
I guess I could just give a simple answer, but maybe I can do a little "teach a man to fish" today.
OK. I can't find documentation for this anywhere, and I don't remember it ever being said publicly either. In fact, I have always gone out of my way to make sure view controllers were deallocated on the main thread, and this is the first time I've ever seen someone indicate that UIViewController objects get automatically deallocated on the main thread.
Maybe someone else can find an official statement, but I couldn't find one.
However, I do have some evidence to prove that it does indeed happen. Actually, at first, I thought you were not properly handling your blocks or reference counts, and somehow a reference was being retained on the main thread.
However, after a cursory look, I was interested enough to try it for myself. To satisfy my curiosity, I made a class similar to yours that inherited from UIViewController. Its dealloc ran on the main thread.
So, I just changed the base class to UIResponder, which is the base class of UIViewController, and ran it again. This time its dealloc ran on the background thread.
Hmmm. Maybe there is something going on behind closed doors. We have lots of debugging tricks. The answer always lies with the last one you try, but I figured I'd try my usual bag of tricks for this kind of stuff.
Log Notifications
One of my favorite tools to find out how things are implemented is to log all notifications.
[[NSNotificationCenter defaultCenter]
addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *note) { NSLog(#"%#", note); }];
I then ran using both classes, and didn't see anything unexpected or different between the two. I didn't expect to, but that little trick is very simple, and it has helped me tremendously in discovering how a lot of other things worked, so it's usually first.
Log Method/Message Sends
My second trick it to enable method logging. However, I don't want to log all methods, just what happens between the time the last block executes, and the call to dealloc. So, turned on method logging by adding this as the last line of the "sleeping" block.
instrumentObjcMessageSends(YES);
And I turned logging back off, with this as the first line of the dealloc method.
instrumentObjcMessageSends(NO);
Now, this C function can't be readily found in any headers that I know of, so you need to declare it at the top of your file.
extern void instrumentObjcMessageSends(BOOL);
The logs go into a unique file in /tmp, named msgSends-.
The files for the two runs contained the following output.
$ cat msgSends-72013
- __NSMallocBlock__ __NSMallocBlock release
- SOUnsafeObject SOUnsafeObject dealloc
$ cat msgSends-72057
- __NSMallocBlock__ __NSMallocBlock release
- SOUnsafeObject UIViewController release
- SOUnsafeObject SOUnsafeObject dealloc
There is not too much surprising about that. However, the presence of UIViewController release indicates that UIViewController has a special override implementation for the +release method. I wonder why? Could it be to specifically transfer the call to dealloc to the main thread?
Debugger
Yes, this is the first thing I thought of, but I had no evidence that there was an override in UIViewController so I went through my normal process. I have found when I skip steps, it typically takes longer.
Anyway, now that we know what we are looking for, I put a breakpoint on the last line of the "sleeping" block and made the class inherit from UIViewController.
When I hit the breakpoint, I added a manual breakpoint...
(lldb) b [UIViewController release]
Breakpoint 3: where = UIKit`-[UIViewController release], address = 0x000000010e814d1a
After continuing, I was greeted with this awesome assembly, which confirms visually what is happening.
pthread_main_np is a function that tells you if you are running on the main thread. Stepping through the assembly instructions confirmed that we are not running on the main thread.
Stepping further, we get to line 27, where we jump over the call to dealloc, and instead run what you can easily see is code to run a dealloc-helper on the main thread.
Can You Count on This Going Forward?
Since I can't find it documented, I don't know that I would count on this happening all the time, but it is very convenient, and obviously something they intentionally put into the code.
I have a set of tests that I run every time Apple releases a new version of iOS and OSX. I assume most developers do something very similar. I think what I would do is write a unit test, and add it to that set. Thus, if they ever change it back, I'll know as soon as it comes out.
Otherwise, I tend to think this may be one of those things that can safely be assumed.
However, be aware that subclasses may choose to override release (if they are compiled with ARC disabled), and if they do not call the base class implementation, you will not get this behavior.
Thus, you may want to write tests for any third-party view controller classes you use.
My Details
I only tested this with XCode 6.4, Deployment target 8.4, simulating iPhone 6. I'll leave testing with other versions as an exercise for the reader.
BTW, if you don't mind, what are the details for your posted example?
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(self.downloadQueue, ^{
NSData *data = [self downloadFromRemoteService:service];
dispatch_async(self.storeQueue, ^{
int img = [self.imageStore addImage:data];
dispatch_saync(self.renderQueue, ^{
[self renderThumbnail:img];
dispatch_async(dispatch_get_main_queue(), ^{
[[self thumbnailViewForId:img] setNeedsDisplay:YES];
});
});
});
});
}
this is the code from Apple WWDC2012 《Asynchronous Design Patterns with Blocks, GCD, and》,'self' as strong reference in blocks, Is this code all right? or how to avoid leaks in this situation?
No, self will not leak. self will however retained until after the last block has been executed. When the last block finished, the block gets deallocated which in turn releases self. At that time, and only IFF there are no other strong references to self, it will be deallocated.
Edit:
I could not resist to mention this (because the sample is from Apple himself -- take it with a grain if salt ;) )
So, at the very top there is the method downloadFromRemoteService. It's glaringly obvious that this is a network request. Network requests are inherently _asynchronous_.
One attribute of an asynchronous operation is that this operation cannot be made "synchronous" in a truly manner anymore. Once asynchronous - always asynchronous.
What's also obvious from the code sample, that the network request is oddly enough synchronous, Ohps!
What happens when wrapping a asynchronous task into a synchronous wrapper? Well, its at least "suboptimal": the calling thread will be blocked immediately until the result is available, then just return the result. That's a quite big amount of waste for resources (threads are limited and are costly to create and require a quite amount of RAM).
So, this code has a "code smell". It's a "bad programming practice". We should make this better. ;)
Objects automaticaly retained when mentioned in block. They got released when block deallocated. So this code is all right. Problems occur when your's self-object takes ownership of such blocks with self inside.
So you just need to release block when you don't need it any more.
There is a retain cycle in this code, as self retains self.downloadQueue (and the other queues), which retains all the blocks dispatched to it, including the block here, which in turn retains self when it is copied (which happens when it is dispatched to the queue).
However, it is a temporary retain cycle, because once the block is executed on the queue, the queue will (hopefully) release it, breaking the cycle.
This question already has an answer here:
Blocks retain cycle from naming convention?
(1 answer)
Closed 8 years ago.
I've written the following category for NSOperationBlock
#implementation NSOperationQueue (Extensions)
-(void)addAsynchronousOperationWithBlock:(void (^)(block))operationBlock
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
block signal = ^ {
dispatch_semaphore_signal(semaphore);
};
[self addOperationWithBlock:^{
operationBlock(signal);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}];
}
#end
it seems to work properly but when I call it (as shown in the following snippet) I get a warning:
block is likely to lead a retain cycle
[_queue addAsynchronousOperationWithBlock:^(block signal) {
[self foo:nil];
signal();
}];
foo is a method of the class that uses this category.
The same code with addOperationWithBlock: (from NSOperationQueue) doesn't show the warning:
[_queue addOperationWithBlock:^ {
[self foo:nil];
}];
I really don't understand it.
Particularly what I don't understand is:
should I actually use the weak pointer in both the cases? will actually the two snippet bring to a retain cycle in case I don't use the weak pointer?
When you use self within a block, it is captured by the block and could lead to a retain cycle. To cycle occurs when self (or something it has a strong reference to) has a strong reference to the block. To avoid the potential cycle, declare a weak pointer and use that in the block instead:
YourClassName * __weak weakSelf = self;
[_queue addAsynchronousOperationWithBlock:^(block signal) {
[weakSelf foo:nil];
}];
To distill what others have written here:
Neither code example creates a long-lasting retain cycle of the sort
that will strand memory.
Xcode complains about your addAsynchronousOperationWithBlock method because it has a suspicious name. It doesn't complain about addOperationWithBlock because it has special knowledge about addOperationWithBlock that overrides its suspicions.
To get rid of the warning, use __weak (see the answers by jszumski and matt) or rename addAsynchronousOperationWithBlock to not start with "add" or "set".
To elaborate a bit on these:
If self owns _queue, you will have a short-lived retain cycle. self will own _queue, which will own the blocks, and the block that calls [self foo:] will own self. But once the blocks have finished running, _queue will release them, and the cycle will be broken.
The static analyzer has been programmed to be suspicious of method names starting with "set" and "add". Those names suggest that the method may retain the passed block permanently, possibly creating a permanent retain cycle. Thus the warning about your method. It doesn't complain about -[NSOperationQueue addOperationWithBlock:] because it's been told not to, by someone who knows that NSOperationQueue releases blocks after running them.
If you use __weak the analyzer won't complain because there won't be the possibility of a retain cycle. If you rename your method the analyzer won't complain because it won't have any reason to suspect your method of permanently retaining the block passed to it.
The answer from jszumski is correct in essence, but it is important to get the form of the "weak self" dance correct. The form (building on his code) is:
YourClassName * __weak weakSelf = self;
[_queue addAsynchronousOperationWithBlock:^(block signal) {
YourClassName * strongSelf = weakSelf;
if (strongSelf)
[weakSelf foo:nil];
}];
Thus we capture weakSelf through a strong reference. If you don't do that, weakSelf can go out of existence while you are in the middle of using it (because your reference to it is weak).
See my book for the dance, and for other things you can do about potential retain cycles caused by blocks:
http://www.apeth.com/iOSBook/ch12.html#_unusual_memory_management_situations
I'm pretty sure this is 100% safe, but I don't want to miss anything. I have the following code
- (void) scheduleControlSurfaceProcess {
[self.operationQueueForMessageProcessing addOperationWithBlock:^{
// do something
[self scheduleControlSurfaceProcess];
}];
}
where self is a Singleton. The block works splendidly as a non-main-thread thread. I do not see any memory problems in the profiler (which I don't trust much).
So, may I ignore the warning, "Block will be retained by an object strongly retained by the captured object?" If not, how can I insist that the block to get released (with ARC)? Getting the warning to go away is easy enough, but assigning id what = self seems like it would not solve the problem.
EDIT: as I realized quite late in this question, the real problem here was that I am rescheduling from within the block itself. This is obviously problematic, because each block retains the next.
NOTE: I am aware that there are lots of questions on this topic, but I'm not expert enough to know which, if any, situations are similar to this one.
- (void) scheduleControlSurfaceProcess {
__weak id SELF = self;
[self.operationQueueForMessageProcessing addOperationWithBlock:^{
id strongSelf = SELF; //Guarantee self doesn't go away in the meantime
// do something
[self.operationQueueForMessageProcessing addOperationWithBlock:^{
[strongSelf scheduleControlSurfaceProcess];
}];
}];
}
That would guarantee you won't have a cycle here. The warning is completely valid, self retains the operation queue, the queue retains the block, the block retains self. And round and round we go.
In my modified example the block will capture SELF and store it into 'strongSelf'. The strongSelf step isn't strictly necessary, but it will make sure the reference to self doesn't get niled during execution of the block.