NSNotification系统通知优化

最近在github上看到了LRNotificationObserver这个项目,看了一下实现方式,作者通过ARC机制实例化注册对象子类与关联对象的方法来管理注册对象的生命周期。从而省去了系统通知移除的过程,本篇介绍该项目实现过程。

NSNotificationCenter 通知存在的问题

注册

1
2
3
4
[[NSNotificationCenter defaultCenter] addObserver:anObserver
selector:@selector(handleNotification:)
name:aNotificationName
object:anObjectThatFiresTheNotification];

移除

1
2
3
[[NSNotificationCenter defaultCenter] removeObserver:anObserver
name:aNotificationName
object:anObjectThatFiresTheNotification];

这是正常使用通知的情形,这样使用存在两个问题

  • 每次使用都需要手动移除观察者。
  • 如果在主线程注册观察者然后在线程B中发送通知,如果通知事件是更新UI那么就会出现问题因为此时是在线程B操作而不是主线程。

LRNotificationObserver

LRNotificationObserver对象的内部持有NSNotificationCenter对象实际上是对NSNotificationCenter基础功能进一步的封装。

LRNotificationObserver自动管理观察者

LRNotificationObserver有两种方法初始化

  • 实例化注册对象
    1
    2
    3
    4
    5
    6
    @property (nonatomic, strong) LRNotificationObserver *backgroundObserver;

    self.backgroundObserver = [LRNotificationObserver observerForName:UIApplicationDidEnterBackgroundNotification
    block:^(NSNotification *note) {
    // Do appropriate background task
    }];

这种方法需要声明一个LRNotificationObserver对象作为属性,根据ARC内存管理当对象销毁时对象的属性也会一起销毁
这种情况下作者把销毁的方法写在了LRNotificationObserver对象的dealloc方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)dealloc
{
[self stopObserving];
}
- (void)stopObserving
{
[_notificationCenter removeObserver:self name:_name object:_object];
[self clear];
}
- (void)clear
{
_block = nil;
_targetAction = nil;
_operationQueue = nil;
_dispatchQueue = nil;
}
  • 使用类方法不用实例化对象
    1
    2
    3
    4
    5
    [LRNotificationObserver observeName:UIApplicationDidReceiveMemoryWarningNotification
    owner:self
    block:^(NSNotification *note) {
    // Purge unnecessary cache
    }];

虽然不用实例化LRNotificationObserver但是要传入owner参数,owner相当于一个钩子,让其与LRNotificationObserver对象进行关联。

1
2
3
4
+ (void)addObserver:(LRNotificationObserver *)observer asPropertyOfOwner:(id)owner
{
objc_setAssociatedObject(owner, (__bridge void *)observer, observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

通过运行时关联对象的方法将LRNotificationObserver对象与owner对象关联,当owner销毁时关联断开,LRNotificationObserver对象不被任何对象持有销毁。执行dealloc方法


线程安全

LRNotificationObserver可以指定通知方法执行队列

1
2
3
4
5
6
[LRNotificationObserver observeName:@"someNotificationThatShouldUpdateTheUI"
object:anObject
owner:anOwner
dispatch_queue:dispatch_get_main_queue()
target:viewController
selector:@selector(methodToBeExecutedOnMainThread:)];

当收到通知后线程会自动切换到dispatch_queue置顶队列所在的线程中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (void)executeNotificationFiredBlock:(dispatch_block_t)block
{
if (self.operationQueue)
{
if ([NSThread isMainThread] && self.operationQueue == [NSOperationQueue mainQueue])
{
block();
}
else
{
[self.operationQueue addOperationWithBlock:block];
}
}
else if (self.dispatchQueue)
{
if ([NSThread isMainThread] && self.dispatchQueue == dispatch_get_main_queue())
{
block();
}
else
{
dispatch_async(self.dispatchQueue, block);
}
}
else
{
block();
}
}

作者通过是否指定dispatch_queueoperationQueue来判断是否切换队列,如果没有指定那么默认操作将在发送通知的线程中执行。