下面给出一个注册通知代码:
[code]
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(networkByeBye:)
name: kNetworkWentByeByeNotification
object: self.networkMonitor];
selector可以写成这种形式:
[code]
- (void) networkByeBye: (NSNotification *) notification {
// handle the notification
} // networkByeBye
当使用block型的notification注册通知时,你需要提供一个queue,当通知被发送的时候,这个block就放入到这个queue中去执行。因为执行代码已经被封装到block中了,所以就不再需要一个selector去处理该通知了,也不需要关联某一个对象。当然,如果这个处理过程需要完成很多事情的话,你还是很希望有一个参数能够传进来。
例如,下面是一个block的注册方式,notification block持有一个notification的对象:
[code]
_token = [center addObserverForName: kNetworkWentByeByeNotification
object: nil
queue: [NSOperationQueue mainQueue]
usingBlock: ^(NSNotification *notification) {
NSLog (@"Network went down: %@", notification);
}];
如果要注销该notification话,需要回调token变量。
notification的名字是一个NSString类型的,前面提到的代码中名字叫做kNetworkWentByeByeNotification。
可以将不同的通知来源,用同一个selector来处理,这样的话,会有一个问题就是那个处理函数的函数体会比较大,但只需要增加一点额外的代码来处理不同的case。
[code]
- (void) networkChanged: (NSNotification *) notification {
// do stuff
if ([notification.name isEqualToString: kNetworkWentByeByeNotification]) {
NSLog (@"Network went away");
// update timestamp label
}
} // networkChanged
object是notification的第二个参数,这个参数是持有该Notification的对象。下面是一个通知notification的代码:
[code]
// NetworkMonitor
[center postNotificationName: kNetworkWentByeByeNotification
object: self
userInfo: userInfo];
可以通过object来区别对待同一个nofication
[code]
- (void) tapcasterTapped: (NSNotification *) notification {
if (notification.object == self.shoesizeTapcaster) {
// Upload the shoe size.
} else if (notification.object == self.bloodtypeTapcaster) {
// Take a blood sample from the user.
}
} // tapcasterTapped
useriinfo是第三个参数,这是一个NSDictionary类型的参数,里面包含着健值对,当通知被推送的时候,任何接收到该通知的都能收到这个参数。userinfo没有预定义里面要存什么数据,所以你可以传送任何你想要传送的数据。例如在网络监视器中,当数据下载完毕时,可以传送这些内容:IP地址,端口号,上次连接时间和连接设备名称等信息。
可以定义好常量符号来表示这些数据:
[code]
extern NSString *const kNetworkInterfaceKey;
extern NSString *const kNetworkDownTimestampKey;
extern NSString *const kNetworkAddressKey;
extern NSString *const kNetworkPortKey;
extern NSString *const kNetworkUptimeSecondsKey;
extern NSString *const kNetworkBonjourKey;
上面这种方式,可以使得程序看上去更加易读。
当传送通知时,用下面这种方式封装userinfo dictionary:
[code]
NSDictionary *userInfo =
[NSDictionary dictionaryWithObjectsAndKeys:
self.interface, kNetworkInterfaceKey,
[NSDate date], kNetworkDownTimestampKey,
self.connectionAddress, kNetworkAddressKey,
self.connectionPort, kNetworkPortKey,
[NSNumber numberWithInt: timeDelta], kNetworkUptimeSecondsKey,
self.bonjourName, kNetworkBonjourKey,
nil];
// Post notification.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center postNotificationName: kNetworkWentByeByeNotification
object: self
userInfo: userInfo];
当接收到通知之后,就可以从userinfo中取出自己需要的信息。
[code]
NSString *interface = [notification.userInfo objectForKey: kNetworkInterfaceKey];
NSLog (@"interface is %@", interface);
当我声明一个notification和keys的时候,我会把他们放在一起,这样可以保持好良好的编码风格,非常有必要建立一些编码规则使得程序看上去更加易读和清晰。
[code]
extern NSString *const kNetworkWentByeByeNotification;
extern NSString *const kNetworkInterfaceKey;
extern NSString *const kNetworkDownTimestampKey;
extern NSString *const kNetworkAddressKey;
extern NSString *const kNetworkPortKey;
extern NSString *const kNetworkUptimeSecondsKey;
extern NSString *const kNetworkBonjourKey;
常量个后缀用“Notifgication”和“Keys”来表示他们之间的关系和所代表的变量。
对于变量时实际数值,我一般有两种赋值方式。
一种是让数值看上去非常易读:
[code]
NSString *const kNetworkWentByeByeNotification = @"network went bye bye";
NSString *const kNetworkInterfaceKey = @"network interface";
NSString *const kNetworkDownTimestampKey = @"network down timestamp";
NSString *const kNetworkAddressKey = @"network address";
NSString *const kNetworkPortKey = @"network port";
NSString *const kNetworkUptimeSecondsKey = @"network uptime seconds";
NSString *const kNetworkBonjourKey = @"bonjour key";
用这种方式,可以在打印的时候,看上去更加易读
[code]
{
"bonjour key" = Nerdinalia;
"network address" = "192.168.254.13";
"network down timestamp" = "2012-07-16 19:52:19 +0000";
"network interface" = en1;
"network port" = 666;
"network uptime seconds" = 10;
}
另一种方式是,把数值和变量名称写成一样的
[code]
NSString *const kNetworkWentByeByeNotification = @"kNetworkWentByeByeNotification";
NSString *const kNetworkInterfaceKey = @"kNetworkInterfaceKey";
NSString *const kNetworkDownTimestampKey = @"kNetworkDownTimestampKey";
NSString *const kNetworkAddressKey = @"kNetworkAddressKey";
NSString *const kNetworkPortKey = @"kNetworkPortKey";
NSString *const kNetworkUptimeSecondsKey = @"kNetworkUptimeSecondsKey";
NSString *const kNetworkBonjourKey = @"kNetworkBonjourKey";
这种赋值方式的好处是和自己定义的API相像,看到数值就知道是哪一个通知发出来的数值。
[code]
{
kNetworkBonjourKey = Nerdinalia;
kNetworkAddressKey = "192.168.254.13";
kNetworkDownTimestampKey = "2012-07-16 19:52:49 +0000";
kNetworkInterfaceKey = en1;
kNetworkPortKey = 666;
kNetworkUptimeSecondsKey = 10;
}
监视代码中的notification发送流非常的简单。notification的名字一般都是预定义好的,尤其是来自cocoa的通知。
最近我需要查看我的app中的都有哪些notification流。尤其是,我很好奇为什么我获取不到来自iCloud文件容器的BSMetadataQuery通知。我已经设置好所有的事情,检查了我们的cloud document设置,里面的文件也在,但是却收不到任何metadata的数据。我知道肯定是有一些metadata的通知的,所以我想查看一下这些通知信息。
监视所有的通知非常的简单。只需要注册一个通知,然后object和name的参数都传nil即可。传送nil参数,就是告诉notification center可以接受任意类型的通知。下面是监视所有notification的代码:
[code]
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
token = [center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
QuietLog (@"NOTIFICATION %@ -> %@",
notification.name, notification.userInfo);
}];
下面是输出的信息:
[code]
NOTIFICATION NSWillBecomeMultiThreadedNotification -> (null)
NOTIFICATION _UIWindowDidCreateWindowContextNotification -> {
"_UIWindowContextIDKey" = "-464166441";
}
...
NOTIFICATION UIWindowDidBecomeVisibleNotification -> (null)
NOTIFICATION UIDeviceOrientationDidChangeNotification -> {
UIDeviceOrientationRotateAnimatedUserInfoKey = 1;
}
NOTIFICATION UIWindowDidBecomeKeyNotification -> (null)
NOTIFICATION UIApplicationDidFinishLaunchingNotification -> (null)
NOTIFICATION UIApplicationDidBecomeActiveNotification -> (null)
NOTIFICATION _UIApplicationDidEndIgnoringInteractionEventsNotification -> (null)
...
NOTIFICATION UINavigationControllerDidShowViewControllerNotification -> {
UINavigationControllerLastVisibleViewController =
"";
UINavigationControllerNextVisibleViewController =
"";
从输出结果中果然没有看到metadata的通知,然后我才意识到我们没有调用startQuery函数,这个错误太低级了。。
我们一般使系统默认的notification center,你也可以创建自己的notification center(NSNotificationCenter并不是一个一个单例类)。在Mac中,我们可以访问NSDistirbutedNotificationCenter,它是一个方便与进行通信的机制。一个进程向该center中推送一个通知,其他的进程都可以监听到,如果要监视这个center发出来的所有通知,你只需要在注册通知时,将name,object的参数设置为nil即可:
[code]
center = [NSDistributedNotificationCenter defaultCenter];
token = [center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
QuietLog (@"DISTRIBUTED %@ -> %@",
notification.name, notification.userInfo);
}];
当你设置系统中的按钮时,你可以从notification center中看到改通知:
[code]
DISTRIBUTED com.apple.HIToolbox.beginMenuTrackingNotification -> {
ToolboxMessageEventData = 145;
}
DISTRIBUTED com.apple.HIToolbox.endMenuTrackingNotification -> (null)
DISTRIBUTED AppleShowScrollBarsSettingChanged -> (null)
DISTRIBUTED com.apple.HIToolbox.beginMenuTrackingNotification -> {
ToolboxMessageEventData = 25921;
}
DISTRIBUTED com.apple.HIToolbox.endMenuTrackingNotification -> (null)
DISTRIBUTED AppleSideBarDefaultIconSizeChanged -> (null)
最后,我们来看一下Workspace中的通知,NSWorkspace是Mac开发中使用的类,它是一个单例的类,拥有自己的notification center,通过NSWorkpace你可以捕捉到,程序的启动,退出事件,程序跳转,机器休眠等事件。监听改center的通知,和上面的方式类似,不同的是被监听的center不同。
[code]
center = [[NSWorkspace sharedWorkspace] notificationCenter];
token = [center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
QuietLog (@"WORKSPACE %@ -> %@",
notification.name, notification.userInfo);
}];
下面是被监听到的workspace中的通知事件:
[code]
WORKSPACE NSWorkspaceDidActivateApplicationNotification -> {
NSWorkspaceApplicationKey =
"";
}
WORKSPACE NSWorkspaceDidDeactivateApplicationNotification -> {
NSWorkspaceApplicationKey =
"";
}
WORKSPACE NSWorkspaceWillSleepNotification -> (null)
WORKSPACE NSWorkspaceDidWakeNotification -> (null)
WORKSPACE NSWorkspaceDidTerminateApplicationNotification -> {
NSApplicationName = "backupd-helper";
NSApplicationPath =
"/System/Library/CoreServices/backupd.bundle/Contents/Resources/backupd-helper";
NSApplicationProcessIdentifier = 25947;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 3703688;
NSWorkspaceApplicationKey =
"";
NSWorkspaceExitStatusKey = 0;
}
改章节中所用到的代码,可以在改gist中获取。可以将代码复制下来跑跑看。