iOS 集合如何弱引用对象

一. 使用 NSValue

NSValue 可以弱引用保存一个对象,我们可以使用这种方法间接的引用。


NSMutableArray *array = @[].mutableCopy;
// 添加
NSObject *obj = [NSObject new];
[array addObject:[NSValue valueWithNonretainedObject:obj]];
// 读取
NSValue *value = array[0];
NSObject *obj2 = [value nonretainedObjectValue];

注意:使用 NSValue 的方式,确实可以实现对对象的弱引用(即被添加到集合中时,对象的引用计数不会+1),但是当对象被释放的时候,数组中对应的对象会变成野指针,因此需要手动删除 NSArray 中对应对象的值,否则会在执行 [value nonretainedObjectValue] 时崩溃;而使用 NSPointerArray 不会有这个问题,对象的释放会使得集合中的对象变为 NULL

二. 使用 NSPointerArray,NSMapTable,NSHashTable

在iOS6.0之后出现了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 枚举定义着内存管理策略、方法特性和内存标识,以下是几个常用的枚举值:
内存管理策略:
NSPointerFunctionsStrongMemory:强引用成员
NSPointerFunctionsMallocMemory: 用于 Mach 的 虚拟内存管理
NSPointerFunctionsMachVirtualMemory: 用于 Mach 的 虚拟内存管理
NSPointerFunctionsWeakMemory:弱引用成员

方法特性:
NSPointerFunctionsObjectPersonality:hash、isEqual、对象描述
NSPointerFunctionsOpaquePersonality:pointer 的 hash 、直接判等

内存标识:
NSPointerFunctionsCopyIn 添加成员时进行 copy 操作

提供 compact 方法剔除 NULL 元素

NSPointerArray 可以存储 NULL,作为补充,它也提供了 compact 方法,用于剔除数组中为 NULL 的成员。但是 compact 函数有个已经报备的 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,规避系统 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 *

初始化参数

+ (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 的内存管理方式可以分开,如: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 时,有一方被释放,则该键值对移除

初始化参数

可以在初始化 NSMapTable 时指定 NSPointerFunctionsOptions 来分别确定对 Key 和 Value 的内存引用

+ (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]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 基础集合类是每一门语言的基础,下面我们一起来对OC的基础集合类进行一个总结。 NSArray NSArray作为一...
    Mr_Baymax阅读 1,041评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 关于键值编码 键值编码(KVC)是一种由NSKeyValueCoding非正式协议提供的机制,对象采用该机制来提供...
    渐z阅读 902评论 0 0
  • 卷首语 欢迎来到 objc.io 第七期! 这个月,我们选择了 Foundation 框架作为我们的主题。 Fou...
    评评分分阅读 1,514评论 0 8
  • 级别: ★★☆☆☆标签:「iOS 」「避免常见崩溃」「FBKVOController」「KVO」作者: WYW[...
    QiShare阅读 3,031评论 2 26