Dealloc Not Running When Dismissing Modal View from Block - objective-c

Strange one here, dealloc is not being called when dismissed from inside a block. Code:
[[NSNotificationCenter defaultCenter] addObserverForName:#"user.login" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
[self dismissModalViewControllerAnimated:YES];
}];
Anyone know why this would be the case? How can i dismiss from inside the block and run dealloc at the same time?
I have tried self performselector but this did not make any difference.
Thanks

(1) Your code is wrong (incomplete). When you issue addObserverForName: you must capture the returned value; this is the observer token. You store this somewhere (e.g. an instance variable):
self->observer = [[NSNotificationCenter defaultCenter]
addObserverForName:#"woohoo" object:nil queue:nil
usingBlock:^(NSNotification *note)
{
//whatever
}];
Later, when you're going out of existence, you remove that observer token from the notification center by calling removeObserver: with that token as argument. If you don't do that, you can crash later.
[[NSNotificationCenter defaultCenter] removeObserver:self->observer];
(2) But wait, there's more! Under ARC, when the block is copied, you'll get a retain cycle. This is because the stored observer token contains the block and is itself retaining self. I will give you three ways to break this retain cycle:
(a) Store the observer token as a weak reference:
__weak id observer;
(b) Store the observer token as a strong reference, but explicitly release it (by nilifying it) when you remove the observer:
[[NSNotificationCenter defaultCenter] removeObserver:self->observer];
self->observer = nil; // crucial
(c) Do the "weak-strong dance", like this, when you create the block (I am pretending that self is a FlipsideViewController):
__weak FlipsideViewController* wself = self;
observer = [[NSNotificationCenter defaultCenter]
addObserverForName:#"user.login"
object:nil queue:nil usingBlock:^(NSNotification *note) {
FlipsideViewController* sself = wself;
[sself dismissModalViewControllerAnimated:YES];
}];
Now, you might think that the "weak-strong dance" is an extreme approach, as one of my commenters implies. But it has one huge advantage: it is the only one of these three solutions that allows you to remove the observer in dealloc. With the other two solutions, dealloc will never be called until after you have called removeObserver: - and finding a better place to call it may not be easy.

I refer you to: Reference Counting of self in Blocks
The block will retain self until the block is released. So to dealloc self, you'll need to remove the observer.

Could this be related to UIKit being not completely thread-safe? UIKit should be only used on the main thread...
If so, I would suggest using:
performSelectorOnMainThread:withObject:waitUntilDone:
(reference)

Related

When Do Blocks Cause Retain Cycles When Using Self?

I am having some memory issue leaks in my app and I am wondering if blocks may have to do with it. I have read that using Self in a block can cause retain cycles, however I have read conflicting information on when this occurs.
From what I understand, doing something like this:
dispatch_async(dispatch_get_main_queue(), ^{
self.text = #"test";
[self doSomething];
});
OR
[self.dataArrayOfDictionaries enumerateObjectsUsingBlock:^(NSDictionary *vd, NSUInteger idx, BOOL *stop) {
[self doSomethingWithDictionary:vd];
}];
Would retain self until do Something is complete, and something like this:
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) {
NSError *err;
self.myDataArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
Would retain self until the download is complete and the table view is reloaded. So, while this code may retain Self longer than is needed, it wouldn't retain it indefinitely, right?
Additionally, I assume this WOULD cause a retain cycle if self is referenced in the block complete, because the block is being retained by self...right?
#interface MyViewController
#property (strong, nonatomic) void (^complete)(NSData *results);
#end
#implementaiton MyViewController
[self doStuffWithCompletion:self.complete];
Where I am not sure about retain cycles is in something like this.
[[NSNotificationCenter defaultCenter]addObserverForName:#"thingDone" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
self.dataObject = note.object;
}];
I have heard that NSNotificationCenter does NOT retain its observers, but because this function would never really be "completed" like the first two I feel like it would potentially retain Self. Additionally, if i used a weakSelf instead, wouldn't the app crash if the notification is received and self has been deallocated, because weakSelf would be nil?
I have a view controller in a navigation controller that is not deallocating when tapping back/popping it, and I think this may because of a retain cycle. If someone could clarify my understanding on when using Self in a block is acceptable, that would greatly help my code.
Where I am not sure about retain cycles is in something like this
[[NSNotificationCenter defaultCenter]addObserverForName: //...
Yes, there can be memory management issues associated with calling addObserverForName:. As I explain in my book:
The observer token returned from the call to addObserverForName:object:queue:usingBlock: is retained by the notification center until you unregister it.
The observer token may also be retaining you through the block. If so, then until you unregister the observer token from the notification center, the notification center is retaining you. This means that you will leak until you unregister. But you cannot unregister from the notification center in dealloc, because dealloc isn’t going to be called so long as you are registered.
In addition, if you also retain the observer token, then if the observer token is retaining you, you have a retain cycle on your hands.
You might want to read the rest of the discussion in my book for actual examples and solutions.

Capturing a variable in a Block when the Block is in the initializer

Consider this:
id observer = [[NSNotificationCenter defaultCenter]
addObserverForName:MyNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[[NSNotificationCenter defaultCenter]
removeObserver:observer
name:MyNotification
object:nil
];
// do other stuff here...
}
];
I'm using this pattern to observe a notification once and then stop observing it. But LLVM tells me (under ARC) that Variable 'observer' is uninitialized when captured by block.
How can I fix this, since the block necessarily captures the variable before initialization, it being part of the initializer? Will using the __block qualifier on observer do the trick?
As explained in the answers to
Why doesn't Remove Observer from NSNotificationCenter:addObserverForName:usingBlock get called,
you have to
add __block, so that the block will refer to the initialized variable, AND
add __weak, to avoid a retain cycle. (The latter applies only to ARC. Without ARC,
the block does not create a strong reference to a __block variable.)
Therefore:
__block __weak id observer = [[NSNotificationCenter defaultCenter] ...
yes, I think declaring observer beforehand as __block id observer; should work.
Yes, using __block will solve the problem.
Without it, the Block gets a copy of the variable's value at the time the Block is created. (Which is "uninitialized" in this case.) With it, the Block (in essence) gets the variable itself, so that the value can be changed from within the Block. Thus it will also "track" changes made from outside.

Prevent a weakly assigned variable from being dealloc'ed without creating a retain cycle

I have an odd case involving ARC, NSNotificationCenter and a block. The following is a simplified example of the code. From testing it seems that the memory management of didSaveObserver is performing as desired, i.e. it is not creating a retain cycle and it is not being niled before removeObserver:.
However, my understanding of ARC makes me think that this is just a fluke/quirk and ARC could nil didSaveObserver before removeObserver:. Seeing as didSaveObserver is never retained (the only assignment is to a weak variable), then ARC could/(should?) instantly dealloc it.
Have I understood the ARC rules correctly? If so then how do I ensure that the didSaveObserver is retained so that it can be unobserved but not create a retain cycle?
self.willSaveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextWillSaveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
id preSaveState = ...; //Store some interesting state about a Core Data object (the details aren't significant to this question).
__weak __block id didSaveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
//Unobserve the did save block. This is the tricky bit! didSaveObserver must be __weak to avoid a retain cycle, but doing so also means that the block may be dealloced before it can be unobsered.
[[NSNotificationCenter defaultCenter] removeObserver:didSaveObserver];
id postSaveState = ...;
//Perform work that uses pre & post save states.
}];
}];
More details:
If __weak is not added (so defaults to __strong) Instruments reports that there's a retain cycle.
There is a comment in NSNotification.h that explains why didSaveObserver isn't dealloced in this particular case:
// The return value is retained by the system, and should be held onto by the caller in
// order to remove the observer with removeObserver: later, to stop observation.
Of course, that only explains this specific case.
First, why do you think it would create a retain cycle? The block would retain didSaveObserver, yes. Would didSaveObserver retain the block? Nothing is documented about the returned observer object other than it can be used in removeObserver: to remove the added observation. It is possible that it somehow retains the block or is the block itself, in which case it would create a retain cycle. If you want to be safe, yes, you could use weak to refer to the observer in the block.
Seeing as didSaveObserver is never retained (the only assignment is to
a weak variable), then ARC could/(should?) instantly dealloc it.
Unless it is retained by the notification center before returning to you.

NSNotificationCenter is not working?

as i told here i am using NSNotificationCenter .
on class A (observer) on the init method i have got :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getSensorsData:) name:#"HotSpotTouched" object:nil];
on classB i have got :
//FILL NSDICTIONARY WITH DATA
[dict setObject:#"SPOT1" forKey:[array objectAtIndex:0]];
[dict setObject:#"SPOT2" forKey:[array objectAtIndex:1]];
[dict setObject:#"SPOT3" forKey:[array objectAtIndex:2]];
[dict setObject:#"SPOT4" forKey:[array objectAtIndex:3]];
[dict setObject:#"SPOT5" forKey:[array objectAtIndex:4]];
[[NSNotificationCenter defaultCenter] postNotificationName:#"HotSpotTouched" object:dict];
the function in class A getSensorsData is not being called.
whats wrong here ??
thanks !
You are passing in dict as notificationSender when posting the notification, and nil when you add the observer. This way your notification is filtered out because the senders mismatch.
Update:
As pointed out by joerick in the comments, passing nil when adding an observer will disable sender filtering. So this isn't the problem here.
I just created a small sample project and for me notifications are delivered.
#Rant: If you want to pass arbitrary data along with your notification, you should use the userInfo dictionary (as pointed out by Cyrille in the comment).
The calls to the notification center look correct. I suspect the problem is due to the liffe cycle of object A. You say that you're registering for the notification in the init method. Have you correctly assigned self?:
-(id)init
{
//self does not have a meaningful value prior to the call to [super init]
self = [super init];
if (self != nil)
{
//ensure addObserver is called in the if code block
}
return self;
}
Also, it's good practice to use constants for notification names as they mitigate against typos. See Constants in Objective C.
problem solved:
if you passing a null argument, the observer is not getting the call !
my NSDictionary argument was null( because a reason i still dont know), so the call is not fired.

NSNotificationCenter selector won't work with its NSNotification

I'm working on a Cocoa project with some C in it (I know, objc contains C...) and am trying to understand NSNotificationCenters. Here's the situation:
I have a struct declared as typedef struct {/*code here*/} structName;
In my - (id)init method, I have
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selName:) name:#"notName" object:nil];
I have a callback function:
int callback(/*args*/) {
structName *f = ...
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:#"notName" object:[[NSValue valueWithPointer:f] retain]];
[autoreleasepool release];
}
And then for my selector:
- (void)selName:(NSNotification *)note
{
NSLog(#"here");
NSLog(#"note is %#", note);
}
Now, if I comment out that second NSLog, everything seems to work (i.e. "here" is printed). But if I leave it in, nothing about the NSNotification seems to work. But this seems to defeat the purpose of the object, userInfo, etc. of the NSNotification.
What am I doing wrong and how can I fix it so I can have access to my structName f?
#Nathan
Okay, so now I have
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:f] forKey:#"fkey"];//f, not &f. I had a typo in the OP which I fixed.
[[NSNotificationCenter defaultCenter] postNotificationName:#"notName" object:nil userInfo:[dict retain]];
...but the problem remains. Any chance this has to do with the typo I fixed?
Edit:
The problem continues even with changing the two lines above to
[[NSNotificationCenter defaultCenter] postNotificationName:#"notName" object:nil userInfo:[NSDictionary dictionaryWithObject:[NSData dataWithBytes:f length:sizeof(structName)] forKey:#"fkey"]];
Works for me. What are you doing differently?
You should be using +notificationWithName:object:userInfo: not +notificationWithName:object:.
The object parameter is the object sending the notification. Normally this would be self for an object posting the notification but since your calling this from a C function it should be nil.
The userInfo parameter is an NSDictionary so add the NSValue to a dictionary and send that.
Then in your selName: method get the -userInfo dict from the NSNotification and pull your info out from there.
Note: You are creating a leak by retaining the NSValue when you shouldn't.
Edit:
How long does the struct exist? NSValue will not copy the contents of the pointer so maybe it's being deallocated? Try using NSData's dataWithBytes:length: instead.
Also make sure to check the console for runtime errors (in Xcode:Run > Console).
And you do not need to retain dict. You may want to (re)read the Cocoa memory management docs.
NSValue needs a pointer to your struct, not the struct itself:
[NSValue valueWithPointer:&f]
You said it worked if you comment out the second NSLog. And that second NSLog appears to be incorrect:
- (void)selName:(NSNotification *)note
{
NSLog(#"here");
NSLog(#"note is %#", note);
}
%# format is to print an NSString but "note" is not a NSString, it's an NSNotification object. NSNotification has a name attribute that returns an NSString. Try changing the second NSLog to this:
NSLog(#"note is %#", [note name]);
Instead, you can use the following:
- (void)selName:(NSNotification *)note
{
NSLog(#"here");
NSLog(#"note is %#", [note userInfo]);
}