NSNotificationCenter is not working? - objective-c

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.

Related

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.

Pass index back to parent view controller

The NSMutableArray detailsDataSource and int detailIndex is passed on to next View Controller from
MainDetailViewController.m:
#import "UsersDetailViewController.h"
...
- (void)swipeDetectedUp:(UISwipeGestureRecognizer *)sender
{
UsersDetailViewController *usersController = [[self storyboard] instantiateViewControllerWithIdentifier:#"UsersController"];
[self.navigationController pushViewController:usersController animated:NO];
usersController.usersDataSource = [[NSMutableArray alloc] initWithArray:detailsDataSource];
usersController.userDetailIndex = detailIndex;
}
Swipe through the index in UserDetailViewController.m:
- (void)swipeDetectedRight:(UISwipeGestureRecognizer *)sender
{
if (userDetailIndex != 0)
userDetailIndex--;
}
When swipeDetectedDown to pop back, MainDataViewController needs to know which object at index to display:
- (void)swipeDetectedDown:(UISwipeGestureRecognizer *)sender
{
//jump to correct object at index, same as current object at index in this view
[self.navigationController popViewControllerAnimated:NO];
}
Code suggestions?
Use NSNotificationCenter to send an object back to the MainDataViewController...
Example:
In UsersDetailViewController populate an NSDictionary with a key=>value pair then send it over to where you want it to go.
NSArray *key = [NSArray arrayWithObject:#"myIndex"];
NSArray *object = [NSArray arrayWithObject:detailIndex];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:object forKeys:key];
[[NSNotificationCenter defaultCenter] postNotificationName:#"MainDataViewController" object:self userInfo:dictionary];
Note: You need to setup an identifier on MainDataViewController called MainDataViewController or whatever you want to call it. Using the VC name keeps it simpler.
Then on MainDataViewController do this in the viewDidLoad() method.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:#"MainDataViewController" object:nil];
And then receive the notification by using the following method:
- (void)receiveNotification:(NSNotification *) notification
{
if([[notification name] isEqualToString:#"MainDataViewController"])
{
NSDictionary *dictionary = [NSDictionary dictionaryWithDictionary:[notification userInfo]];
if([dictionary valueForKey:#"myIndex"])
{
// do whatever you need to do with the passed object here. In your case grab the detailIndex and use it for something...
}
}
}
The easy part is to put the UsersDetailViewController pointer into a property of MainDetailViewController so it can access self.usersController.usersDataSource & self.usersController.userDetailIndex later. Then the only trick is to have it know when the UsersDetailViewController was popped.
In code I used to write, I often tried something like making MainDetailViewController be a delegate of UsersDetailViewController, and having a delegate method in MainDetailViewController be called when the UsersDetailViewController want to close programmatically, and in that do both the popViewControllerAnimated: and update the MainDetailViewController's state. In other words, always have the parent's code pop the child off. This works, but not in the case where you have the child view controller pop automatically via the navigation controller's back button say, so overall I'd argue against that technique.
I think there's better solutions for having the parent's code get called when its child is popped. Perhaps implement a viewWillAppear method and if self.usersController is set there, then you know you're coming back from the UsersDetailViewController, at that point access the other controller's properties and finally clear self.usersController.

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.

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)

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