-1

I'm writing an iOS game with a bunch of different types of enemies and items/drops. Specific ones need to trigger things on different events, so I'm experimenting with using NSNotificationCenter.

Everything works when listener is defined in a way that's retained. When I define it locally or add it to an NSMutableArray, it's lost and the postNotification throws a EXC_BAD_ACCESS

This breaks, because the listener isn't retained past this.

MyListener *listener = [[MyListener alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:listener selector:@selector(myEventHandler:) name:@"MyEvent" object:nil];

This is a super basic example of what works:

MyListener *listener;
-(id)init {
    listener = [[MyListener alloc] init];
    [[NSNotificationCenter defaultCenter] addObserver:listener selector:@selector(myEventHandler:) name:@"MyEvent" object:nil];
}

The issue is that I will have a bunch of different enemy/item classes that need to listen for an event. I don't want a different class-level variable for every single one - but admit that I may be going about this the wrong way. I'm still somewhat new to iOS.

helion3
  • 34,737
  • 15
  • 57
  • 100
  • As you've seen, you need to keep a strong reference to the observer. How that is done depends on your needs. There's not enough enough here to offer any real suggestions. – rmaddy Jul 05 '14 at 19:04
  • 2
    It doesn't make much sense to create an object just to serve as an observer of a notification. If an object didn't have another reason to exist (which would mean something else was keeping a strong reference to it), then why does it care about the notification? The objects representing the "enemies and items/drops" should be the observers of the notification. – Ken Thomases Jul 05 '14 at 19:07
  • If you don't mind runtime tricks, here's a KVO helper I made that I find useful: https://github.com/nielsbot/kvo-helper It automatically deregisters when the observed object is deallocated. – nielsbot Jul 05 '14 at 19:30

1 Answers1

1

Well, first of all, your init snippet is entirely wrong. init methods should look more like this:

- (instancetype) {
    self = [super init];
    if (self) {
        // initialize
    }
    return self;
}

And in your example, // initialize would be replaced with:

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(myEventHandler:) 
                                             name:@"MyEvent" 
                                           object:nil];

But what's most important here is that at some point before this object is completely deallocated, we must stop observing. If we do not, after this object is deallocated, NSNotificationCenter will try to send a message to a deallocated object, which causes your EXC_BAD_ACCESS.

You may wish to stop observing earlier than dealloc, but at a minimum, you need to add this to your class:

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

As for keeping the object alive, well that's as simple as keeping a strong reference to it. This is sort of an entirely different question. Although... if you're trying to keep an object alive simply to respond to a notification, there's no need to even use an object here.

You can give the notification center a block to respond to notification with using the following method:

- (id)addObserverForName:(NSString *)name
                  object:(id)obj 
                   queue:(NSOperationQueue *)queue 
              usingBlock:(void (^)(NSNotification *))block
nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • My init was more psuedocode, so you're correct that it's missing stuff. The problem I have is that these classes need to listen for an unknown length of time. Something has to store the reference to each object listening. I found that adding them all to an `NSMutableArray` doesn't work. – helion3 Jul 05 '14 at 19:10
  • It's not that your `init` was missing stuff. It was poorly formed. – nhgrif Jul 05 '14 at 19:11
  • Using the block may be the solution for me - it's only a little bit of code for every class that needs it – helion3 Jul 05 '14 at 19:11
  • An `NSArray` has a strong reference to each of its members, @helion3, so there's no reason that should not be working -- something needs to own the array itself, too, though. – jscs Jul 05 '14 at 19:15
  • I must be doing something wrong then, because even though my object is in an array, it still throws the access error. This is essentially how I'm handling it: https://pste.me/#/VxpEQ – helion3 Jul 05 '14 at 19:21
  • @helion: [Having trouble adding objects to NSMutableArray](http://stackoverflow.com/q/851926) – jscs Jul 05 '14 at 19:26