Men的博客

欢迎光临!

0%

通知原理

定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖
于它的对象都得到通知并自动更新。

使用

1.向通知中心注册观察者
    addObserver:selector:name:object:
    (观察者接收到通知后执行任务的代码在发送通知的线程中执行)
    addObserverForName:object:queue:usingBlock:
    (观察者接收到通知后执行任务的代码在指定的操作队列中执行)
2.通知中心向所有注册的观察者发送通知
    postNotification:
    postNotificationName:object:
    postNotificationName:object:userInfo:

通知与线程关系

同步和异步都是相对于发送通知所在的线程的。
postNotification:总是会卡住当前线程,待observer执行(如不特殊处
理selector也会在postNotification:所在线程执行)结束之后才会继续
往下执行。所以是同步的。
我们在底层当中的消息的触发其实是依赖与端口的,我们想要在一个线程中发消
息,在另一个线程中进行处理的话,我们可以用端口来实现
enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefau
这里并不是让通知回调在异步执行,只是让通知回调等待到runloop空闲的时候再去执行,如果方法中有高耗时操作,主线程中还是会住UI刷新。

为什么要移除通知

若在iOS8或之前版本系统中,对一个对象
addObserver:selector:name:object:(假设name为@“111”),但是
并没有在dealloc的之前或之中,对其进行remove操作。那么,在发送通知、
(name为@“111”)的时候,会因为bad_access(野指针)而crash。
若在iOS9及以后,同上操作,不会crash。
iOS8及以前,NSNotificationCenter持有的是观察者的
unsafe_unretained指针(可能是为了兼容老版本),这样,在观察者回收
的时候未removeOberser,而后再进行post操作,则会向一段被回收的区域
发送消息,所以出现野指针crash。而iOS9以后,unsafe_unretained改成
了weak指针,即使dealloc的时候未removeOberser,再进行post操作,
则会向nil发送消息,所以没有任何问题

Notification Queues和异步通知

[[NSNotificationQueue defaultQueue] 
enqueueNotification:notification 
postingStyle:NSPostWhenIdle 
coalesceMask:NSNotificationNoCoalescing 
forModes:@[NSDefaultRunLoopMode]];
该参数的三个可选参数:
1,NSPostWhenIdle:runloop空闲的时候回调通知方法
2,NSPostASAP:runloop能够调用的时候就回调通知方法
3,NSPostNow:runloop立即回调通知方法
根据其type,NSNotificationQueue在合适的时机将其post到
NSNotificationCenter。这样就完成了异步的需求。

NSNotification与KVO异同点

kvo: 这些代码都只需在观察者里边进行实现,被观察者不用添加任何代码,所以谁要监听谁注册,然后对响应进行处理即可,使得观察者与被观察者完全解耦,运用很灵活,很简单.
缺点:
KVO只能检测类中的属性,并且属性名都是通过NSString来查找,编译器不会帮你去检测与补全,纯手敲可能比较容易出错.
NSNotification:一对多”,在APP中,很多控制器都需要知道一个事件,应该用通知;
要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO 多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的装填变化进行监听,监听范围广,使用也更灵活

在子现成发送通知

[NSThread detachNewThreadWithBlock:^{
    NSLog(@"1");
    NSLog(@"%@", [NSThread currentThread]);
    NSNotification *notification = [NSNotification notificationWithName:@"JKRNO" object:nil];
    [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle
    coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
    NSLog(@"3");
}];
这样写了之后,发现无论怎么发送这个通知,通知都不会回调到,这个是因为并没有为该子线程添加runloop,
这个线程触发之后马上就结束了。(没有添加通知队列的时候,是可以回调到的,因为那种情况下,
通知发送后,通知回调马上就会执行,该线程会等待通知回调执行完毕后才结束)
这个时候,我们就需要添加为子线程添加一个runloop,让子线程常驻:
@property (nonatomic, strong) NSThread *thread;
...
- (NSThread *)thread {
    if (!_thread) {
        _thread = [[NSThread alloc] initWithBlock:^{
            NSRunLoop *ns_runloop = [NSRunLoop currentRunLoop];
            [ns_runloop addPort:[NSPort port] forMode:NSRunLoopCommonModes];

            CFRunLoopRef runloop = CFRunLoopGetCurrent();
            CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
                switch (activity) {
                    case kCFRunLoopEntry:
                        NSLog(@"进入runLoop");
                        break;
                    case kCFRunLoopBeforeTimers:
                        NSLog(@"处理timer事件");
                        break;
                    case kCFRunLoopBeforeSources:
                        NSLog(@"处理source事件");
                        break;
                    case kCFRunLoopBeforeWaiting:
                        NSLog(@"进入睡眠");
                        break;
                    case kCFRunLoopAfterWaiting:
                        NSLog(@"被唤醒");
                        break;
                    case kCFRunLoopExit:
                        NSLog(@"退出");
                        break;
                    default:
                        break;
                }
            });
            CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
            CFRelease(observer);
            [ns_runloop run];
        }];
        [_thread start];
    }
    return _thread;
}

- (IBAction)postNotification:(UIButton *)sender {
    [self performSelector:@selector(postNotification) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)postNotification {
    NSLog(@"1");
    NSLog(@"%@", [NSThread currentThread]);
    //NSPostWhenIdle
    //NSPostASAP
    //NSPostNow
    NSNotification *notification = [NSNotification notificationWithName:@"JKRNO" object:nil];
    [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
    NSLog(@"3");
}

- (void)receiceNotification:(NSNotification *)notification {
    NSLog(@"2");
    NSLog(@"%@", [NSThread currentThread]);
}