Crash问题总结与防护--NSNotification Crash

产生原因

注册观察者后,没有在观察者dealloc时及时注销观察者,极有可能通知中心再发送通知时发送给僵尸对象而发生Crash。(iOS9以后不会Crash)

防护方案

在宿主释放过程中嵌入我们自己的对象,使得宿主释放时顺带将我们的对象一起释放掉,从而获取dealloc的时机点

实现

(1)创建一个NSObject的分类NSObject+AdNotifyEvent。在这个Category中,我们创建了添加观察者的方法,其具体实现由它的associate Object实现。这里的associate Object是类SLVObserverAssociater的对象。

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
30
31
32
33
34
35
36
// NSObject+AdNotifyEvent.h
#import <Foundation/Foundation.h>
#import "SLVObserverAssociater.h"
@interface NSObject (AdNotifyEvent)
- (void)slvWatchObject:(id)object eventName:(NSString *)event block:(SLVNotifyBlock)block;
- (void)slvWatchObject:(id)object eventName:(NSString *)event level:(double)level block:(SLVNotifyBlock)block;
@end
// NSObject+AdNotifyEvent.m
#import "NSObject+AdNotifyEvent.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation NSObject (AdNotifyEvent)
- (SLVObserverAssociater *)observerAssociater
{
SLVObserverAssociater *observerAssociater = (SLVObserverAssociater *)objc_getAssociatedObject(self, _cmd);
if (observerAssociater == nil) {
observerAssociater = [[SLVObserverAssociater alloc] initWithObserverObject:self];
objc_setAssociatedObject(self, _cmd, observerAssociater, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return observerAssociater;
}
- (void)slvWatchObject:(id)object eventName:(NSString *)event block:(SLVNotifyBlock)block
{
[[self observerAssociater] addNotifyEvent:event watchObject:object observerObject:self level:RFEventLevelDefault block:block];
}
- (void)slvWatchObject:(id)object eventName:(NSString *)event level:(double)level block:(SLVNotifyBlock)block
{
[[self observerAssociater] addNotifyEvent:event watchObject:object observerObject:self level:level block:block];
}
@end

(2)SLVObserverAssociater的实现

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
30
31
32
33
34
// SLVObserverAssociater.h
#import <Foundation/Foundation.h>
#import "SLVNotifyLevelBlocks.h"
@interface SLVObserverAssociater : NSObject
@property (nonatomic, weak) id observerObject; // selfRef,观察者
@property (nonatomic, strong) NSMutableDictionary *notifyMap; // key:通知名_watchObject value:RFNotifyEventObject
- (id)initWithObserverObject:(id)observerObject;
- (void)addNotifyEvent:(NSString *)event
watchObject:(id)watchObject
observerObject:(id)observerObject
level:(double)level
block:(SLVNotifyBlock)block;
@end
@interface SLVNotifyInfo : NSObject
@property (nonatomic, weak) SLVObserverAssociater *associater;
@property (nonatomic, unsafe_unretained) id watchObject; // 被观察对象
@property (nonatomic, strong) NSString *event;
@property (nonatomic, strong) NSMutableArray *eventInfos;
@property (nonatomic, weak) id sysObserverObj; // 观察者
- (id)initWithRFEvent:(SLVObserverAssociater *)rfEvent event:(NSString *)event watchObject:(id)watchObject;
- (void)add:(SLVNotifyLevelBlocks *)info;
- (void)removeLevel:(double)level;
- (void)handleRFEventBlockCallback:(NSNotification *)note;
@end

.m

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
30
31
32
33
34
// SLVObserverAssociater.h
#import <Foundation/Foundation.h>
#import "SLVNotifyLevelBlocks.h"
@interface SLVObserverAssociater : NSObject
@property (nonatomic, weak) id observerObject; // selfRef,观察者
@property (nonatomic, strong) NSMutableDictionary *notifyMap; // key:通知名_watchObject value:RFNotifyEventObject
- (id)initWithObserverObject:(id)observerObject;
- (void)addNotifyEvent:(NSString *)event
watchObject:(id)watchObject
observerObject:(id)observerObject
level:(double)level
block:(SLVNotifyBlock)block;
@end
@interface SLVNotifyInfo : NSObject
@property (nonatomic, weak) SLVObserverAssociater *associater;
@property (nonatomic, unsafe_unretained) id watchObject; // 被观察对象
@property (nonatomic, strong) NSString *event;
@property (nonatomic, strong) NSMutableArray *eventInfos;
@property (nonatomic, weak) id sysObserverObj; // 观察者
- (id)initWithRFEvent:(SLVObserverAssociater *)rfEvent event:(NSString *)event watchObject:(id)watchObject;
- (void)add:(SLVNotifyLevelBlocks *)info;
- (void)removeLevel:(double)level;
- (void)handleRFEventBlockCallback:(NSNotification *)note;
@end

(3)为通知的回调block排了优先级

1
2
3
4
5
6
7
#define RFEventLevelDefault 1000.0f
typedef void (^SLVNotifyBlock) (NSNotification *note, id selfRef);
@interface SLVNotifyLevelBlocks : NSObject
@property (nonatomic, assign) double level; // block的优先级
@property (nonatomic, copy) SLVNotifyBlock block; //收到通知后的回调block
@end

(4)test

1
2
3
4
5
6
- (void)viewDidLoad {
[super viewDidLoad];
[self slvWatchObject:nil eventName:@"newNotification" block:^(NSNotification *aNotification, id weakSelf){
NSLog(@"收到一个通知,现在开始处理了。。。");
} ];
}