views:

2575

answers:

4

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"]];
A: 

NSValue needs a pointer to your struct, not the struct itself:

[NSValue valueWithPointer:&f]

Diederik Hoogenboom
Thanks, but that doesn't seem to actually solve my problem.
John
+1  A: 

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.

Nathan Kinsinger
Okay, so now I have NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:f] forKey:@"fkey"];//f, not ...but the problem remains. Any chance this has to do with the typo I fixed?
John
Wow. My first time posting here... I edited the OP so you can actually see the formatting of that.
John
Tried [[NSNotificationCenter defaultCenter] postNotificationName:@"notName" object:nil userInfo:[NSDictionary dictionaryWithObject:[NSData dataWithBytes:f length:sizeof(structName)] forKey:@"fkey"]];Still no luck. Thanks for the continued help. There has got to be something really stupid I'm doing for that one NSLog line to break everything and not even allow "here" to be printed.
John
Try creating a new project and test the bare minimum needed to send a notification (no userInfo just the name). Then add a little bit at a time (like sending an NSString in the dictionary). See if you can get it to work and then, if you can, try to figure out what is different about your main project.
Nathan Kinsinger
Nathan: He does need to retain dict if he wants it to live beyond the autorelease pool. The NSNotification documentation doesn't say whether it retains the userInfo.
Peter Hosey
Peter: Most of Apple's documentation does not state whether an input is retained or not.Just follow the rules. You are not responsible for owning objects for other objects. Presumably NSNotification will follow the rules as well, if it expects to use the dictionary in the future it needs to take ownership of it.
Nathan Kinsinger
A: 

Works for me. What are you doing differently?

Peter Hosey
Well, if I leave my code in the "working" state and change my structName declaration to be as you did it (i.e. struct structName {...};) then my code no longer works. So that's one difference. Also, I'm declaring my struct and C function within my .m, not in my .h.Okay... just moved those declarations to my .h, but had to still use `typedef`. Still no joy.
John
Let me try saying something a bit more useful. Could threading/deadlock issues be causing my problem?
John
Syntactic issues like whether you use “struct structName” or typedef it and use that name are not relevant. Neither is whether you declare things in the header or the implementation; that only affects the visibility of those symbols to other files, not their behavior in the running program.
Peter Hosey
For that matter, even your latter NSLog is not causing the problem. I think it more likely that it's random. Try putting it back and running it repeatedly—you should find that your notification handler does get called some of the time. Then, take the statement out and run it repeatedly. You should find the same thing.
Peter Hosey
As for threading causing a deadlock: It could, if you have threads. Use Shark or Instruments to see what's happening. Also, run loops are per-thread, so check to make sure that you're always adding the observer and posting the notification on the same (and the correct) thread.
Peter Hosey
Thanks, Peter. I'll attack that next time I sit down with my code.
John
Okay, I'm pretty certain that it's not random because trying it several times yields no results. I created a new project and rewrote the same code and got, not too surprisingly, the same results. Is there any chance that certain connections I've made in IB are causing this? What about the fact that I'm using /System/Library/PrivateFrameworks/MultitouchSupport.framework? Could that causing thread issues, and how would I figure out what they were?Sorry if these are particularly noob-ish questions.
John
Without seeing the code, I have no idea. I do suggest you try removing the private framework from your project, just in case that is breaking it somehow. (Make a back-up first, obviously, so you can easily get back to your previous state.)
Peter Hosey
A: 

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]);
progrmr
`%@` sends `description` to the object and prints that string. As such, it works on all NSObjects and some CFTypes. Source: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1
Peter Hosey
(It does say that it uses `CFCopyDescription` on CFTypes, which should work on all such objects, but I've had it return the empty string for some CFTypes. I should file a bug the next time I encounter that.)
Peter Hosey
Thanks Peter for the correction and the link. I didn't know that %@ could be used with anything other than NSString. So his code should have worked.
progrmr