一. 使用 NSValue
NSValue 可以弱引用保存一個對象磷箕,我們可以使用這種方法間接的引用。
NSMutableArray *array = @[].mutableCopy;
// 添加
NSObject *obj = [NSObject new];
[array addObject:[NSValue valueWithNonretainedObject:obj]];
// 讀取
NSValue *value = array[0];
NSObject *obj2 = [value nonretainedObjectValue];
注意:使用 NSValue 的方式,確實可以實現(xiàn)對對象的弱引用(即被添加到集合中時傲隶,對象的引用計數(shù)不會+1),但是當對象被釋放的時候窃页,數(shù)組中對應的對象會變成野指針跺株,因此需要手動刪除 NSArray 中對應對象的值,否則會在執(zhí)行 [value nonretainedObjectValue] 時崩潰脖卖;而使用 NSPointerArray 不會有這個問題乒省,對象的釋放會使得集合中的對象變?yōu)?NULL
二. 使用 NSPointerArray,NSMapTable畦木,NSHashTable
在iOS6.0之后出現(xiàn)了NSPointerArray袖扛,NSMapTable,NSHashTable十籍。
用法分別對應 NSMutableArray蛆封,NSMutableDictionary,NSMutableSet妓雾。
- 1. NSPointerArray
特性介紹
NSPointerArray 是 NSArray 的通用版本娶吞,和 NSArray/NSMutableArray 不同的是,NSPointerArray 具有下面這些特性:
- 與 NSArray/NSMutableArray 相對應械姻,NSArray/NSMutableArray 強引用集合對象
- NSPointerArray 可以弱引用集合對象妒蛇,一旦對象沒人持有了机断,NSPointerArray 中對應的項會被變成 NULL
- NSPointerArray 是可變的,沒有不可變的版本
- NSPointerArray 可以存儲 NULL绣夺,NULL 參與 count 計算
- NSPointerArray 的 count 可以被設置吏奸,如果直接設置 count,多余的位置會使用 NULL 占位
- NSPointerArray 存儲的是指針類型 void * 而不是對象陶耍,所以需要 __bridge 進行轉換
- 使用 addPointer 和 pointerAtIndex 來存取指針
- (instancetype)initWithOptions:(NSPointerFunctionsOptions)options;
- (instancetype)initWithPointerFunctions:(NSPointerFunctions *)functions;
NSPointerFunctionsOptions 枚舉定義著內(nèi)存管理策略奋蔚、方法特性和內(nèi)存標識,以下是幾個常用的枚舉值:
內(nèi)存管理策略:
NSPointerFunctionsStrongMemory:強引用成員
NSPointerFunctionsMallocMemory: 用于 Mach 的 虛擬內(nèi)存管理
NSPointerFunctionsMachVirtualMemory: 用于 Mach 的 虛擬內(nèi)存管理
NSPointerFunctionsWeakMemory:弱引用成員
方法特性:
NSPointerFunctionsObjectPersonality:hash烈钞、isEqual泊碑、對象描述
NSPointerFunctionsOpaquePersonality:pointer 的 hash 、直接判等
內(nèi)存標識:
NSPointerFunctionsCopyIn 添加成員時進行 copy 操作
提供 compact 方法剔除 NULL 元素
NSPointerArray 可以存儲 NULL毯欣,作為補充馒过,它也提供了 compact 方法,用于剔除數(shù)組中為 NULL 的成員酗钞。但是 compact 函數(shù)有個已經(jīng)報備的 bug腹忽,每次 compact 之前需要添加一個 NULL,否則會 compact 失敗
弱引用測試代碼
NSPointerArray *pointerArray = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory];
@autoreleasepool{
NSObject *obj = [NSObject new];
[pointerArray addPointer:(__bridge void *)obj];
NSLog(@"NSPointerArray is: %p count: %@", [pointerArray pointerAtIndex:0], @(pointerArray.count));
// 輸出 NSPointerArray is: 0x60000000e800 count: 1
}
NSLog(@"After Release NSPointerArray is: %p count: %@", [pointerArray pointerAtIndex:0], @(pointerArray.count));
// 輸出 After Release NSPointerArray is: 0x0 count: 1
// 每次 compact 之前需要添加 NULL砚作,規(guī)避系統(tǒng) Bug
[pointerArray addPointer:NULL];
[pointerArray compact];
NSLog(@"After Compact NSPointerArray count: %@", @(pointerArray.count));
// 輸出 After Compact NSPointerArray count: 0
- 2. NSHashTable
特性介紹
NSHashTable 是 NSSet 的通用版本窘奏,和 NSSet / NSMutableSet 不同的是,NSHashTable 具有下面這些特性:
- 與 NSSet/NSMutableSet 相對應葫录,NSSet/NSMutableSet 強引用集合對象
- NSHashTable 可以弱引用集合對象着裹,一旦對象沒人持有了,NSHashTable 中的值也會被移除
- NSHashTable 是可變的米同,沒有不可變的版本
- 除了存儲對象求冷,NSHashTable 也可以存儲任意指針,比如 void *
初始化參數(shù)
+ (NSHashTable<ObjectType> *)hashTableWithOptions:(NSPointerFunctionsOptions)options;
NSHashTableOptions 的取值如下:
- NSHashTableStrongMemory: 默認值窍霞,強引用集合對象匠题,與 NSSet 一樣
- NSHashTableWeakMemory: 弱引用集合對象
- NSHashTableZeroingWeakMemory: 廢棄,請使用 NSHashTableWeakMemory
- NSHashTableCopyIn: 在將對象添加到集合之前但金,會拷貝對象
- NSHashTableObjectPointerPersonality: 使用 shifted pointer 來做 hash 檢測及確定兩個對象是否相等
弱引用測試代碼
NSHashTable *hashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
@autoreleasepool {
NSObject *obj = [NSObject new];
[hashTable addObject:obj];
NSLog(@"hashTable is: %@", hashTable);
// hashTable is: NSHashTable {[3] <NSObject: 0x6000035e3f60>}
}
NSLog(@"hashTable is: %@", hashTable);
// hashTable is: NSHashTable {}
- 3. NSMapTable
特性介紹
NSMapTable 是 NSDictionary 的通用版本韭山,和 NSDictionary/NSMutableDictionary 不同的是,NSMapTable 具有下面這些特性:
- 與 NSDictionary/NSMutableDictionary 相對應冷溃,NSDictionary/NSMutableDictionary 對 Key 拷貝钱磅,對 Value 強引用
- key 和 value 的內(nèi)存管理方式可以分開,如:key 是強引用似枕,value 是弱引用
- NSMapTable 可以弱引用 Key 和 Value盖淡,一旦 Key 或 Value 中的某一個沒人持有了,NSMapTable 中對應的項也會被移除
- NSMapTable 是可變的凿歼,沒有不可變的版本
- 除了存儲對象褪迟,NSMapTable 也可以存儲任意指針冗恨,比如 void *
總結起來一共有 4 種可能:
- key 為 strong,value 為 strong
- key 為 strong味赃,value 為 weak
- key 為 weak掀抹,value 為 strong
- key 為 weak,value 為 weak
當用 weak 修飾 key 或 value 時心俗,有一方被釋放傲武,則該鍵值對移除
初始化參數(shù)
可以在初始化 NSMapTable 時指定 NSPointerFunctionsOptions 來分別確定對 Key 和 Value 的內(nèi)存引用
+ (NSMapTable<KeyType, ObjectType> *)mapTableWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions;
- NSMapTableStrongMemory: 默認值,強引用 Key/Value
- NSMapTableWeakMemory: 弱引用 Key/Value
- NSHashTableZeroingWeakMemory: 廢棄城榛,請使用 NSMapTableWeakMemory
- NSMapTableCopyIn: 在將對象添加到集合之前揪利,會拷貝對象
- NSMapTableObjectPointerPersonality: 使用 shifted pointer 來做 hash 檢測及確定兩個對象是否相等
弱引用測試代碼
NSMapTable *mapTable = [NSMapTable weakToStrongObjectsMapTable];
@autoreleasepool {
NSObject *key = [NSObject new];
NSObject *value = [NSObject new];
[mapTable setObject:value forKey:key];
NSLog(@"mapTable is: %@", mapTable);
// mapTable is: NSMapTable {<NSObject: 0x6000008df890> -> <NSObject: 0x6000008df870>}
}
NSLog(@"mapTable is: %@", mapTable);
// mapTable is: NSMapTable {}
// key 是 weak 引用,所以析構之后 NSMapTable 就會移除對應的項
參照
參考: 弱引用集合對象
[toc]