Notifications

Notifications

Cocoa provides your app with a single instance of NSNotificationCenter, informally called the notification center, and available as [NSNotificationCenter defaultCenter]. This instance is the basis of a mechanism for sending messages called notifications. A notification includes an instance of NSNotification (a notification object). The idea is that any object can be registered with the notification center to receive certain notifications. Another object can hand the notification center a notification object to send out (this is called posting the notification). The notification center will then send that notification object, in a notification, to all objects that are registered to receive it.

The notification mechanism is often described as a dispatching or broadcasting mechanism, and with good reason. It lets an object send a message without knowing or caring what object or how many objects receive it. This relieves your app’s architecture from the formal responsibility of somehow hooking up instances just so a message can pass from one to the other (which can sometimes be quite tricky or onerous, as discussed in Chapter 13). When objects are conceptually “distant” from one another, notifications can be a fairly lightweight way of permitting one to message the other.

An NSNotification object has three pieces of information associated with it, which can be retrieved by instance methods:

name

An NSString which identifies the notification’s meaning.

object

An instance associated with the notification; typically, the instance that posted it.

userInfo

Not every notification has a userInfo; it is an NSDictionary, and can contain additional information associated with the notification. What information this NSDictionary will contain, and under what keys, depends on the particular notification; you have to consult the documentation. For example, the documentation tells us that UIApplication’s UIApplicationDidChangeStatusBarFrameNotification includes a userInfo dictionary with a key UIApplicationStatusBarFrameUserInfoKey whose value is the status bar’s frame. When you post a notification yourself, you can put anything you like into the userInfo for the notification’s recipient(s) to retrieve.

Cocoa itself posts notifications through the notification center, and your code can register to receive them. You’ll find a separate Notifications section in the documentation for a class that provides them.

Receiving a Notification

To register to receive a notification, you send one of two messages to the notification center. One is addObserver:selector:name:object:. The parameters are as follows:

observer:

The instance to which the notification is to be sent. This will typically be self; it isn’t usual for one instance to register a different instance as the receiver of a notification.

selector:

The message to be sent to the observer instance when the notification occurs. The designated method should return void and should take one parameter, which will be the NSNotification object (so the parameter should be typed as NSNotification* or id). Don’t forget to implement this method! If the notification center sends a notification by sending the message specified as the selector: here, and there is no method implemented to receive this message, your app will crash (see Chapter 3).

name:

The NSString name of the notification you’d like to receive. If this parameter is nil, you’re asking to receive all notifications associated with the object designated in the object: parameter. A built-in Cocoa notification’s name is usually a constant. As I explained in Chapter 1, this is helpful, because if you flub the name of a constant, the compiler will complain, whereas if you enter the name of the notification directly as an NSString literal and you get it wrong, the compiler won’t complain but you will mysteriously fail to get any notifications (because no notification has the name you actually entered) — a very difficult sort of mistake to track down.

object:

The object of the notification you’re interested in, which will usually be the object that posted it. If this is nil, you’re asking to receive all notifications with the name designated in the name: parameter. (If both the name: and object: parameters are nil, you’re asking to receive all notifications!)



The other way to register to receive a notification is by calling addObserverForName:object:queue:usingBlock:. It returns a value, whose purpose I’ll explain in a moment. The queue: will usually be nil; a non-nil queue: is for background threading. The name: and object: parameters are just like those of addObserver:selector:name:object:. Instead of an observer and a selector, you provide a block consisting of the actual code to be executed when the notification arrives. This block should take one parameter — the NSNotification itself.

This way of registering for a notification has some tremendous advantages. For addObserver:selector:name:object: to work properly, you must get the selector right and make sure you implement the corresponding method. With a block, on the other hand, there is no selector and no separate method; everything happens right there in the block:

MPMusicPlayerController* mp = [MPMusicPlayerController iPodMusicPlayer];

[mp beginGeneratingPlaybackNotifications];

id ob = [[NSNotificationCenter defaultCenter]

 addObserverForName:MPMusicPlayerControllerNowPlayingItemDidChangeNotification

 object:nil queue:nil usingBlock:^(NSNotification *n) {

         self->_nowPlayingItem = mp.nowPlayingItem;

         // ... and so on ...

     }

 }];

Consider how maintainable and understandable that code is. Heavy use of addObserver:selector:name:object: means that your code ends up peppered with methods that exist solely in order to be called by the notification center. But there is nothing about these methods that tells you what they are for — you will probably want to use explicit comments in order to remind yourself — and the methods are separate from the registration call, all of which makes your code very method-heavy and confusing. With a block, on the other hand, the whole purpose of the registration is crystal-clear, because the block accompanies it. And notice how, in the block, I don’t have to redefine mp as I did in the separate method nowPlayingItemChanged:; it is still in scope from where it was defined a couple of lines earlier. Blocks are so convenient!




Unregistering

It is up to you, for every object that you register as a recipient of notifications, to unregister that object before it goes out of existence. If you fail to do this, and if the object does go out of existence, and if a notification for which that object is registered is posted, the notification center will attempt to send the appropriate message to that object, which is now missing in action. The result will be a crash at best, and chaos at worst.

To unregister an object as a recipient of notifications, send the notification center the removeObserver: message. (Alternatively, you can unregister an object for just a specific set of notifications with removeObserver:name:object:.) The object passed as the observer: argument is the object that is no longer to receive notifications. What object that is depends on how you registered in the first place:

You called addObserver:...

You supplied an observer originally; that is the observer you must now unregister.

You called addObserverForName:...

The call to addObserverForName:... returned an observer token object, which you captured as an id variable (its real class and nature are no concern of yours); that is the observer you must now unregister.

The trick is finding the right moment to unregister. The fallback solution is the registered instance’s dealloc method, this being the last lifetime event an instance is sent before it goes out of existence. If you’re using ARC and addObserverForName:..., there are some additional memory management implications that I’ll talk about in Chapter 12.

If you’re calling addObserverForName:... multiple times from the same class, you’re going to end up receiving from the notification center multiple observer tokens, which you need to preserve so that you can unregister all of them later. If your plan is to unregister everything at once, one way to handle this situation is through an instance variable that is a mutable collection. So, for example, I might have an NSMutableSet instance variable called _observers. Early on, I initialize it to an empty set:

self->_observers = [NSMutableSet set];

Each time I register for a notification using a block, I capture the result and add it to the set:

id ob = [[NSNotificationCenter defaultCenter]

    addObserverForName:@"whatever" object:nil queue:nil

    usingBlock:^(NSNotification *note) {

        // ... whatever ...

    }];

[self->_observers addObject:ob];

When it’s time to unregister, I enumerate the set:

for (id ob in self->_observers)

    [[NSNotificationCenter defaultCenter] removeObserver:ob];

The tedium of arranging all that is a price worth paying in order to take advantage of blocks when using notifications.



Posting a Notification

Although you’ll mostly be interested in receiving notifications from Cocoa, you can also take advantage of the notification mechanism as a way of communicating between your own objects. As I mentioned before, one reason for doing this might be that two objects are conceptually distant or independent from one another.

To use notifications in this way, your objects must play both roles in the communication chain. One of your objects (or more than one) will register to receive a notification, identified by name or object or both, as I’ve already described. Another of your objects will post a notification, identified in the same way. The notification center will then pass the message along from the poster to the registered recipient.

To post a notification, send to the notification center the message postNotificationName:object:userInfo:.

For example, one of my apps is a simple card game. The game needs to know when a card is tapped. But a card knows nothing about the game; when it is tapped, it simply emits a virtual shriek by posting a notification:

- (void) singleTap: (id) g {

    [[NSNotificationCenter defaultCenter]

        postNotificationName:@"cardTapped" object:self];

}

The game object has registered for the @"cardTapped" notification, so it hears about this and retrieves the notification’s object; now it knows what card was tapped and can proceed correctly.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值