iOS中的NSArray, NSDictionary, NSSet是用来存储OC对象的, 对其中的元素都是强引用. 但有一些使用场景是需要用到弱引用对象的, 如NSArray中存储一些delegate对象(而delegate对象通常都是weak类型).
观察者模式
通常会有如下的使用场景:
在头文件中:
@protocol CSDownloadOperationQueueDelegate;
@interface CSDownloadOperationQueue : NSOperationQueue
- (void)addObserver:(id<CSDownloadOperationQueueDelegate>)observer;
- (void)removeObserver:(id<CSDownloadOperationQueueDelegate>)observer;
-
@end
observer为继承指定delegate的OC对象, ARC中未放在循环引用, 所以observer通常使用weak类型.
在实现文件中:
@interface CSDownloadOperationQueue ()
@property (nonatomic, strong) NSMutableArray <NSValue *> *observers;
@end
@implementation CSDownloadOperationQueue
- (NSMutableArray <NSValue *> *)observers {
if (!_observers) {
_observers = [NSMutableArray array];
}
return _observers;
}
- (void)addObserver:(id<CSDownloadOperationQueueDelegate>)observer {
// 将weak类型的observer添加为观察者
}
- (void)removeObserver:(id<CSDownloadOperationQueueDelegate>)observer {
// 移除观察者
}
- (void)CSDownloadOperation:(CSDownloadOperation *)operation
downloadFinished:(BOOL)isSuccessful {
// for observer in self.observers {
// [observer sendDownloadFinishedNotification];
// }
}
@end
这里, 就需要向观察者数组observers中添加/移除元素, 通常有以下几种方式.
NSValue与nonretainedObjectValue
使用NSValue将weak类型的对象包装一层:
- (void)addObserver:(id<CSDownloadOperationQueueDelegate>)observer {
@synchronized (self.observers) {
BOOL isExisting = NO;
for (NSValue *value in self.observers) {
if ([value.nonretainedObjectValue isEqual:observer]) {
isExisting = YES;
break;
}
}
if (!isExisting) {
[self.observers addObject:[NSValue valueWithNonretainedObject:observer]];
NSLog(@"@");
}
}
}
- (void)removeObserver:(id<CSDownloadOperationQueueDelegate>)observer {
@synchronized (self.observers) {
NSValue *existingValue = nil;
for (NSValue *value in self.observers) {
if ([value.nonretainedObjectValue isEqual:observer]) {
existingValue = value;
break;
}
}
if (existingValue) {
[self.observers removeObject:existingValue];
}
}
}
- (void)CSDownloadOperation:(CSDownloadOperation *)operation
downloadingProgress:(CGFloat)progress {
@synchronized (self.observers) {
for (NSValue *value in self.observers) {
id<CSDownloadOperationQueueDelegate> observer = value.nonretainedObjectValue;
if ([observer respondsToSelector:@selector(CSDownloadOperationQueue:downloadOperation:downloadingProgress:)]) {
[observer CSDownloadOperationQueue:self downloadOperation:operation downloadingProgress:progress];
}
}
}
}
使用时, 只需要从NSValue对象中取出nonretainedObjectValue即可.
NSPointerArray
NSArray对其中元素是强引用的, 且元素不能为nil. 而NSPointerArray可存储NULL, 也可以用来存储weak类型的元素. weak类型的元素释放之后, NSPointerArray的对应位置即自动变成NULL. 使用count属性, 会将NULL元素也包含进来.
A PointerArray acts like a traditional array that slides elements on insertion or deletion. Unlike traditional arrays, it holds NULLs, which can be inserted or extracted (and contribute to count). Also unlike traditional arrays, the 'count' of the array may be set directly. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release. The copying and archiving protocols are applicable only when NSPointerArray is configured for Object uses. The fast enumeration protocol (supporting the for..in statement) will yield NULLs if present. It is defined for all types of pointers although the language syntax doesn't directly support this.
使用方式如下:
NSPointerArray *pointerArray = [NSPointerArray weakObjectsPointerArray];
[pointerArray addPointer:(__bridge void *)observer];
id<CSDownloadOperationQueueDelegate> firstObject = [arr pointerAtIndex:0];
for (id obj in arr) {
// action
}
因为NSPointerArray可以包含NULL, 所以遍历时候会自动跳过NULL的元素.
可以是要compact方法来剔除NULL元素.
- (void)compact; // eliminate NULLs
NSMapTable
NSMapTable类似NSDictionary, 只是其key和value都可以是weak类型.
An NSMapTable is modeled after a dictionary, although, because of its options, is not a dictionary because it will behave differently. The major option is to have keys and/or values held "weakly" in a manner that entries will be removed at some indefinite point after one of the objects is reclaimed. In addition to being held weakly, keys or values may be copied on input or may use pointer identity for equality and hashing. An NSMapTable can also be configured to operate on arbitrary pointers and not just objects. We recommend the C function API for "void *" access. To configure for pointer use, consult and choose the appropriate NSPointerFunction options or configure and use NSPointerFunctions objects directly for initialization.
当weak的key或weak的value被释放掉, 则该key-value键值对即被移除.
NSHashTable
同样, NSHashTable类似NSSet, 可以存放weak元素.
An NSHashTable is modeled after a set, although, because of its options, is not a set because it can behave differently (for example, if pointer equality is specified two isEqual strings will both be entered). The major option is to provide for "weak" references that are removed automatically, but at some indefinite point in the future. An NSHashTable can also be configured to operate on arbitrary pointers and not just objects. We recommend the C function API for "void *" access. To configure for pointer use, consult and choose the appropriate NSPointerFunctionsOptions or configure or use an NSPointerFunctions object itself for initialization.