NSNotificationCenter

栏目: IOS · 发布时间: 6年前

内容简介:首先看两段 Apple Doc 上的话:最初我看到这两段之后觉得 Notification Center 是这样工作的:不理解说的什么请忽略,毕竟 Notification Center 实际不是这样实现的

首先看两段 Apple Doc 上的话:

  1. NSNotificationCenter
In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.
  1. Delivering Notifications To Particular Threads
Regular notification centers deliver notifications on the thread in which the notification was posted. Distributed notification centers deliver notifications on the main thread. At times, you may require notifications to be delivered on a particular thread that is determined by you instead of the notification center. For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.

错误的理解

最初我看到这两段之后觉得 Notification Center 是这样工作的:

- addObserver:selector:name:object:
- postNotification:

不理解说的什么请忽略,毕竟 Notification Center 实际不是这样实现的

正确的方式

回想在『错误的理解』中的方式,如果要实现那样的方式,Notification Center 势必需要维护 Threads 和在它们之上注册的 Observers 的映射表。这样实现也是挺麻烦的,而实际的工作方式是这样:

  1. 线程 A 通过调用 Notification Center 上的 - addObserver:selector:name:object: 注册一个 Observer。可以注意下这三个参数,它们分别是 函数运行的上下文需要运行的函数函数被调用时传递的实参 ,那么 Notification Center 内部实际上就是存储了这个调用结构体(没有源码,但是大概就是这个意思),然后将这些个调用结构体根据 Notification Name 组织到一个 Set 中。
  2. 线程 B 通过调用 Notification Center 上的 - postNotification: 来向 Observers 们发出通知。于是 Notification Center 在内部维护的调用结构体 Set 中找到了相关的 Observers,然后在 B 线程上面分别的以它们被添加时的 函数运行上下文需要运行的函数函数被传递时的实参 来逐个调用它们。
  3. 于是你会发现,你在 A 线程中注册的 Observers,但是由于你在 B 线程中 post notification,最后那些 Observers 都在 B 线程中被运行了。

为了不再理解错 :joy:,需要用代码验证下,先看看命令行的:

#import <Foundation/Foundation.h>
#import <pthread.h>

#define CURRENT_THREAD_ID (pthread_mach_thread_np(pthread_self()))

static NSString* const CrossThreadNotification = @"CrossThreadNotification";

@interface SubThread : NSThread

@end

@implementation SubThread

- (void)main
{
    NSLog(@"Sub thead is running: %u", CURRENT_THREAD_ID);

    // 往 RunLoop 中添加一个 port 这样 RunLoop 才不会因为没有 source 而退出
    NSPort* port = [[NSPort alloc] init];
    [[NSRunLoop currentRunLoop] addPort:port forMode:NSDefaultRunLoopMode];

    // 在子线程中注册 observer
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receievedNotification)
                                                 name:CrossThreadNotification
                                               object:nil];

    [[NSRunLoop currentRunLoop] run];
}

- (void)receievedNotification
{
    NSLog(@"%s %u", __func__, CURRENT_THREAD_ID);
}

@end

int main(int argc, const char* argv[])
{
    @autoreleasepool
    {
        NSLog(@"press 'q' to exit...");

        SubThread* sub = [[SubThread alloc] init];
        [sub start];

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC),
            dispatch_get_global_queue(0, 0), ^{
                // 我们添加 observer 的动作发生在 SubThread 的 `-main` 中,等 0.1 秒让 SubThread 运行起来
                NSLog(@"try to notify CrossThreadNotification in %u", CURRENT_THREAD_ID);
                [[NSNotificationCenter defaultCenter] postNotificationName:CrossThreadNotification object:nil];
            });

        int c = getchar();
        while (c != 'q') {
            c = getchar();
        }
    }
    return 0;
}
复制代码

运行后的结果:

press 'q' to exit...
Sub thead is running: 6147
try to notify CrossThreadNotification in 4099
-[SubThread receievedNotification] 4099
复制代码

可以看到 post 和执行都发生在 4099 ,说明了 observer 的执行发生在 post 所在的线程。

再看一个 iOS 中的,大同小异,不过我们测试了两个,一个是 System Notification,一个 Custom Notification:

#import <pthread.h>

#define CURRENT_THREAD_ID (pthread_mach_thread_np(pthread_self()))

static NSString* const CrossThreadNotification = @"CrossThreadNotification";

@interface SubThread : NSThread

@end

@implementation SubThread

- (void)main
{
    NSLog(@"Sub thead is running: %u", CURRENT_THREAD_ID);

    NSPort* port = [[NSPort alloc] init];
    [[NSRunLoop currentRunLoop] addPort:port forMode:NSDefaultRunLoopMode];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receievedNotification)
                                                 name:UIApplicationDidReceiveMemoryWarningNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receievedNotification)
                                                 name:CrossThreadNotification
                                               object:nil];

    [[NSRunLoop currentRunLoop] run];
}

- (void)receievedNotification
{
    NSLog(@"%s %u", __func__, CURRENT_THREAD_ID);
}

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    NSLog(@"Main thread ID: %u", CURRENT_THREAD_ID);

    SubThread* sub = [[SubThread alloc] init];
    [sub start];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC),
        dispatch_get_global_queue(0, 0), ^{
            // 5 秒后在另一个线程中 post notification
            NSLog(@"try to notify CrossThreadNotification in %u", CURRENT_THREAD_ID);
            [[NSNotificationCenter defaultCenter] postNotificationName:CrossThreadNotification object:nil];
        });

    return YES;
}
复制代码

这是运行的结果:

Main thread ID: 1803
Sub thead is running: 19715
try to notify CrossThreadNotification in 9475
-[SubThread receievedNotification] 9475
Received memory warning.
-[SubThread receievedNotification] 1803
复制代码

首先两个 9475 得出的结果和在命令行中的结果一致:observer 的执行发生在 post 所在的线程。另外还注意到,提供通知 UIApplicationDidReceiveMemoryWarningNotification 是在主线程上 post 的(两个 1803 ),于是我们的 observer 在主线程上执行了。

但是我还没有在 Apple Doc 中找到说所有的系统调用都在主线程上被 post,但是至少以 UI 开头的系统调用会是在主线程上被调用的,比如上面的 UIApplicationDidReceiveMemoryWarningNotification

Happy coding!


以上所述就是小编给大家介绍的《NSNotificationCenter》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

虚拟现实:最后的传播

虚拟现实:最后的传播

聂有兵 / 中国发展出版社 / 2017-4-1 / 39.00

本书对“虚拟现实”这一诞生自70年代却在今天成为热门话题的概念进行了历史发展式的分析和回顾,认为虚拟现实是当今最重大的社会变革的技术因素之一,对虚拟现实在未来百年可能给人类社会的各个层面带来的影响进行说明,结合多个大众媒介的发展趋势,合理地推演未来虚拟现实在政治、经济、文化等领域的态势,并基于传播学理论框架提出了几个新的观点。对于普通读者,本书可以普及一般的虚拟现实知识;对于传媒行业,本书可以引导......一起来看看 《虚拟现实:最后的传播》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具