對NSObject寫一個分類:
#import <Foundation/Foundation.h>
@interface NSObject (FMObserverHelper)
- (void)fm_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
// 對象被釋放之前, 會調用dealloc方法, 其持有的實例變量也會被釋放.
// 在監聽注冊時, 為self和Observer關聯個臨時對象, 當兩者在釋放實例變量時, 借助這個時機, 在臨時對象的dealloc方法中, 移除Observer
// self在被釋放之前, 會先釋放其持有的關聯屬性, self並未完全釋放, 可在臨時對象中target卻成了nil.
// weak: 持有者不會對目標進行retain, 當目標銷毀時, 持有者的實例變量會被置空
// unsafe_unretained: 持有者不會對目標進行retain, 當目標釋放后, 持有者的實例變量還會依然指向之前的內存空間(野指針)
// 如果Observer提前釋放,而添加關聯屬性, 兩者還不能同時持有臨時對象, 否則臨時對象也不會及時的釋放,既然一個不行, 那就各自關聯一個.
// 兩個關聯屬性釋放的同時, 進行了兩次觀察移除的操作. 為避免這個問題, 需要判斷weak引用的實例變量factor是否為空即可
#import "NSObject+FMObserverHelper.h"
#import <objc/runtime.h>
@interface FMObserverHelper : NSObject
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, unsafe_unretained) id observer;
@property (nonatomic, strong) NSString * keyPath;
@property (nonatomic, weak) FMObserverHelper * factor;
@end
@implementation FMObserverHelper
- (void)dealloc {
if ( _factor ) {
[_target removeObserver:_observer forKeyPath:_keyPath];
}
}
@end
@implementation NSObject (FMObserverHelper)
- (void)fm_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
[self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:nil];
FMObserverHelper * helper = [FMObserverHelper new];
FMObserverHelper * sub = [FMObserverHelper new];
sub.target = helper.target = self;
sub.observer = helper.observer = observer;
sub.keyPath = helper.keyPath = keyPath;
helper.factor = sub;
sub.factor = helper;
const char * helpeKey = [NSString stringWithFormat:@"%zd", [observer hash]].UTF8String;
objc_setAssociatedObject(self, helpeKey, helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(observer, helpeKey, sub, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end