观察者模式观察者模式是一种对象行为模式,用于解耦类之间一系列需要相互协作的通信。它定义了对象之间一对多的依赖关系。当一个对象的状态改变时,所有依赖它的对象都会被通知。观察者模式的实现一般分为两步:消费者注册通知消息监听器,生产者发送通知消息。iOS系统提供了多种观察者模式的实现:在CocoaTouch层使用NSNotification类和NSNotificationCenter类实现通知消息的注册、处理和发送,在CoreFoundation层使用CFNotificationXXX系列的提供了C函数来实现通知消息。注册处理和发送,在操作系统层面,通过libsystem_notify.dylib库提供了一套基于C语言的底层通知消息注册和发送机制。本文将重点介绍库libsystem_notify.dylib(以下简称系统通知库)中提供的用于注册通知消息和发送通知消息的各种接口函数。系统通知库中的通知消息注册和发送是一种底层通知机制,可以用来实现跨进程通信。系统通知库的API系统通知库中的所有函数都在notify.h中声明,所以当你想使用系统通知库提供的函数时,需要在代码中#include。就像所有其他基于通知消息的实现一样,每条通知消息都由一个字符串标识,系统通知库中的通知消息也是如此。另外,每个进程在注册监听一个通知消息时,也会在进程内生成一个有效的通知消息标识token。令牌可以理解为进程在运行时向侦听器发送的通知消息的表示。在处理通知消息时,系统通知库提供了四种处理方式:block-basedprocessor、machport-basedmessageport、signal-basedprocessing、file-basedprocessor。1.通知消息注册系统为了支持以上四种消息处理机制,通知库提供了四个函数来实现各种处理类型的通知消息的注册://基于块处理的通知注册uint32_tnotify_register_dispatch(constchar*name,int*out_token,dispatch_queue_tqueue,notify_handler_thandler)//基于信号处理的通知注册uint32_tnotify_register_signal(constchar*name,intsig,int*out_token);//基于machport消息的通知注册uint32_tnotify_register_mach_port(constchar*name,mach_port_t*notify_port,intflags,int*out_token);//基于文件描述符的通知注册。uint32_tnotify_register_file_descriptor(constchar*name,int*notify_fd,intflags,int*out_token);从上面四个函数可以看出,每个函数的参数都是notificationmessage的名字,也就是我们要监听的通知消息的名字,并且每个函数都有一个out_token输出,用来标识进程在运行时注册的通知消息。对于blockprocessor,每次触发监听通知时,总是会在指定的队列中调用指定的block函数;对于信号,指定的块功能将始终在每次触发监视通知时发送到系统。信号;对于machport,每次触发监听通知时,总会向指定的machport端口发送一个空的machmsg消息;对于一个文件描述符,每次触发监听的通知时,总会将特定的内容写入到指定的文件中。系统通知库不仅支持iOS系统也支持macOS系统,并且是跨进程通知消息。但是一般情况下,iOS系统只是通过notify_register_dispatch函数来监听通知,通过block进行处理,而macOS系统中所有的处理方式都有。2.通知消息的发送当通知消息产生时,需要将该通知消息发送给所有的监听者。通知消息通过函数notify_post发送:uint32_tnotify_post(constchar*name);函数的签名很简单,入参就是通知消息的名称。系统通知函数中的通知不会有任何附加参数。3.暂停、恢复、取消通知消息监听器在注册通知消息时,系统会返回一个token值来标识该通知消息。同时,系统还分别提供了通知消息监听的暂停、恢复、取消处理://通知暂停,设置后,本令牌将暂时不接受消息通知。uint32_tnotify_suspend(inttoken)//通知恢复,设置这个token后会恢复接受消息的通知。uint32_tnotify_resume(inttoken)//通知取消,此token设置后将不再接受消息通知。uint32_tnotify_cancel(inttoken);4.通知消息发送检测有时候我们并不想注册一个通知消息处理器来处理通知,而只是想检测一个通知消息是否已经发送,所以系统提供了两个Function来实现这个功能://Register用于通知消息检测的令牌。uint32_tnotify_register_check(constchar*name,int*out_token);//检查是否发送了该token对应的通知消息。如果通知消息已发送,check返回1,否则返回0。uint32_tnotify_check(inttoken,int*check);5.通知消息的状态对于通知消息的监听者,我们可以给返回的token绑定一个64位的状态数据。我们可以获取并设置它。该状态数据主要用于实现通知监听器的扩展处理。uint32_tnotify_set_state(inttoken,uint64_tstate64)uint32_tnotify_get_state(inttoken,uint64_t*state64)六、系统预置通知消息系统底层支持一些预置通知消息,在头文件notify_keys.h中声明。这些预设消息用于目录服务、磁盘空间和卷挂起、网络配置更改、写入日志通知以及系统时区和时间更改。对于每个特定的通知消息,您可以在文件中查看描述。例如下面的例子实现了磁盘空间不足、网络状态变化、系统时间调整的监控过程:#include#includevoidfoo(){inttoken1,token2,token3;//注册以监控网络状态更改通知。notify_register_dispatch(kNotifySCNetworkChange,&token1,dispatch_get_main_queue(),^(inttoken){//...});//在监控系统中注册磁盘空间不足的通知{//....});//注册监听系统时间改变的通知。notify_register_dispatch(kNotifyClockSet,&token3,dispatch_get_main_queue(),^(inttoken){//...});}除了notify_keys.h公开的通知消息外,还有一些未公开的通知消息,我们可以通过它们来获取有关系统状态更改的更多信息,以下列表将在系统底部列出所有通知消息。每个通知的具体含义留给读者去猜测和验证。“com.apple.asl.remote”“com.apple.system.timezone”“com.apple.MCX._managementStatusChangedForDomains”“com.apple.CFPreferences._domainsChangedExternally”“com.apple.system.clock_set”“com.apple。system.timezone""AppleNumberPreferencesChangedNotification""AppleTimePreferencesChangedNotification""AppleDatePreferencesChangedNotification""AppleLanguagePreferencesChangedNotification""AppleTextBehaviorPreferencesChangedNotification""com.apple.librarian.account-token-changed""com.apple.system.batterysavermode""com.apple.accessibility.cache。forcetouch.sensitivity.changed""com.apple.networkd.started""com.apple.neconfigurationchanged""com.apple.networkd.settings""com.apple.system.config.network_change""com.apple.CoreAnimation.CAWindowServer.DisplayChanged""com.apple.networkd.proxy_count""com.apple.iohideventsystem.available""com.apple.backboardd.rawOrientation""com.apple.springboard.hasBlankedScreen""UIBacklightLevelChangedNotification""com.apple.accessibility.wob.status""com.apple.backboardd.videosettingschanged""com.apple.mobile.keybagd.user_changed""com.apple.LaunchServices.database""com.apple.accessibility.cache.enhance.text.legibility""com.apple.frontboard.systemappservices.serverNotifyToken""com.apple.frontboard.workspace.serverNotifyToken""com.apple.accessibility.cache.captioning""com.apple.accessibility.cache.vot""com.apple.accessibility.cache.ax""com.apple.accessibility.cache.app.ax""com.apple.accessibility.status""com.apple.language.changed""com.apple.springboard.showingAlertItem""com.apple.mobile.keybagd.lock_status""NameLayerTree""ApplePreferredContentSizeCategoryChangedNotification""kKeepAppsUpToDateEnabledChangedNotification""com.apple.accessibility.cache.reduce.motion""UIKeyboardSpringBoardKeyboardShow""UIKeyboardSpringBoardKeyboardHide""com.apple.locationd.registration""kCTDaemonReadyNotification""com.apple.system.config.network_change""com.apple.system.timezone./var/db/timezone/zoneinfo/UTC""com.apple.system.info:/etc/hosts""com.apple.MSVLoggingConfigurationDidChange""com.apple.managedconfiguration.defaultsdidchange""com.apple.AppSupport.loggingDefaultsChanged""com.apple.mobileipod.MPMusicPlayerController.launched""com.apple.networkd.nat64.ifstate""com.apple.ManagedConfiguration.profileListChanged""com.apple.backboardd。unambiguousOrientation""com.apple.accessibility.cache.button.shapes.enabled""com.apple.accessibility.cache.use.single.system.color.enabled""com.apple.accessibility.cache.darken.system.colors.enabled""com.apple.coreui.statistics""com.apple.UIKit.UIScreenEdgeGestureMode""com.apple.managedconfiguration.restrictionchanged""com.apple.managedconfiguration.passcodechanged""PINPolicyChangedNotification""com.apple.managedconfiguration.settingschanged""com.apple.managedconfiguration.effectivesettingschanged""com.apple.managedconfiguration.appwhitelistdidchange""com.apple.managedconfiguration.defaultsdidchange""com.apple.managedconfiguration.keyboardsettingschanged""com.apple.managedconfiguration.clientrestrictionschanged""com.apple.managedconfiguration.webFilterUIActiveDidChange""com.apple.ManagedConfiguration.managedAppsChanged""MCManagedBooksChanged""com.apple.managedconfiguration.allowpasscodemodificationchanged""com.apple.mediaserverd.up""com.apple.hangtracer.prefchangednotification""com.apple.accessibility.cache.enhance.background.contrast""com.apple.system.thermalpressurelevel""com.apple.backboardd.backlight.changed""com.apple.accessibility.QuickSpeakEnabled""com.apple.accessibility.cache.quick.speak""com.apple.powerlog.state_changed""com.apple.powerlog.clientPermissionState"