1. 程式人生 > >iOS 自動移除KVO觀察者

iOS 自動移除KVO觀察者

nonatomic format 時機 value set observe fork tor @property

對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

iOS 自動移除KVO觀察者