NSNotificationCenter selector won't work with its NSNotification - objective-c

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]);
}

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.

Objective-C Object Instantiation - Address getting overwritten by another class (intermittent)

I was trying to prepare a simple demo for using NSNotificationCenter when I got unexpected crashes, but only sometimes. I have two classes, Scout and Shouter. On -init, Shouter posts a notification to NSNotificationCenter, and Scout adds itself as an observer for the same notification on its respective -init.
When the application crashes, it has "unrecognized selector sent to instance 0x100109480" (address changes of course). This led me to check the addresses of the objects to see what happens. It appears that sometimes a Shouter init after a Scout init is overwriting the address for the Scout object.
Code is below. I'm hoping to understand why this happens. I've tried removing the id/idCounter mechanism and using a hard-coded int in case my understanding of static was wrong.
Relevant code for Shouter:
#implementation Shouter{
int _id; //An ID-code to recognize different instances of Shouter.
}
-(id)init{
NSLog(#"init test Shouter - what is self? %#", self);
if(self=[super init]){
static int _idCounter = 0;
_id = _idCounter++;
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:_id] forKey:#"ID-code"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"sample" object:nil userInfo:dictionary];
}
return self;
}
#end
Relevant code for Scout:
#implementation Scout
-(id)init{
NSLog(#"init test Scout - what is self? %#", self);
if(self=[super init]){
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(messageReceived:)
name:#"sample"
object:nil];
}
return self;
}
- (void) messageReceived:(NSNotification*)notification{
NSDictionary *dictionary = [notification userInfo];
id object = [dictionary objectForKey:#"ID-code"];
int code = -1;
if([object isKindOfClass:[NSNumber class]]){
code = [((NSNumber*)object) intValue];
}
NSLog(#"Received a message from a Shouter instance. ID-code was: %i", code);
}
#end
Relevant code for main:
int main(int argc, const char * argv[])
{
#autoreleasepool {
[[Shouter alloc] init];
[[Scout alloc] init];
[[Shouter alloc] init];
}
return 0;
}
Example output in crash instance:
2014-01-23 11:38:30.800 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x100109480>
2014-01-23 11:38:30.802 Lab 3 Snippets[3461:303] init test Scout - what is self? <Scout: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] -[Shouter messageReceived:]: unrecognized selector sent to instance 0x1003007b0
You should always unregister notifications when your object is deallocated, e.g:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"sample" object:nil];
[super dealloc]; // Skip this if you're using ARC
}
What might happen is that you're Scout object is deallocated right after its init method finishes, and the next Shooter object is allocated at the same address. NSNotificationCenter doesn't know this has happened, and tries to call messageReceived: on the former Scout object. Instead it finds a Shooter object that does not respond to the method, hence your error.
NSNotificationCenter never retains objects registered as observers, because that would create a lot of retain cycles that would be really hard to work around. Make sure something holds on to your objects for as long as you need them to exist.
In guessing ARC is on. ARC will see that the object is never used again and release it immediately, but since you never unregister the notification subscription, the notification center tries to talk to the new object that's allocated in its place.
You can also use [[NSNotificationCenter defaultCenter] removeObserver:self];on dealloc method it will remove all references of self on the Notification Center.

Objective-C accessing properties inside block

I have read Apple's Blocks Programming Topics and my due diligence searching online, but I am still unclear if I am implementing my blocks correctly. I have an array of clients as a property that is populated when an NSNotification is sent. Clients is used as a tableview data source. The code below works, but I am curious if it is putting self in a retaining cycle. Should I do something like __block id theClients = self.clients; and then reference theClients inside the block?
#property (strong, nonatomic) NSMutableArray *clients;
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__block id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification){
// Explore notification
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (self.clients)
{
self.clients = nil;
}
self.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[self.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
There is no problem in accessing the clients property because it is a strong (i.e. retained) property. So you don't need the __block here.
One problem can be that self might not exist anymore when the notification is sent. Then you would access the deallocated object and the app can crash! To avoid that you should remove the observer in the dealloc method.
The __block before id observer is definitely required !
EDIT:
In iOS 5 you can safely capture self using a weak reference:
__weak id weakSelf = self;
Then inside the block you can safely use weakSelf.clients. The variable weakSelf will turn into nil automatically when the object is deallocated.
Yes, you have a retain cycle, at least until the notification occurs. When you access the clients ivar in the block, the block will retain self. It will be retained by the block in notification center until the notification occurs (since you remove the observer at the end of the block). If that's not desirable in your case, you can use a weak reference to self.
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__weak id weakSelf = self;
id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification) {
if (weakSelf) {
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (weakSelf.clients)
{
weakSelf.clients = nil;
}
weakSelf.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[weakSelf.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
I don't see any reason you need to __block qualify observer.
It's also not clear you're getting anything out of using the block-based API here. If you don't want to worry about the potential retain cycle, you could just use addObserver:selector:name:object: and put the body of your notification callback in an instance method.

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.

Dealloc Not Running When Dismissing Modal View from Block

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)