1. Objective-C中的Copy机制深度解析
在iOS开发中,理解copy机制是掌握Objective-C内存管理的关键一环。作为一个从MRC时代走过来的开发者,我见过太多因为copy使用不当导致的诡异bug。本文将结合十多年的实战经验,带你彻底吃透Objective-C的copy机制。
1.1 为什么需要Copy?
在面向对象编程中,对象传递默认都是引用传递。这就带来一个潜在问题:当我们将一个可变对象赋值给属性时,外部代码可能在我们不知情的情况下修改这个对象的值。想象这样一个场景:
objective-c复制NSMutableString *nickname = [NSMutableString stringWithString:@"老张"];
User *user = [[User alloc] init];
user.nickname = nickname;
// 某处代码修改了nickname
[nickname appendString:@"(已离职)"];
如果不使用copy,user.nickname会意外变成"老张(已离职)"。这就是copy修饰符存在的核心价值——保护对象免受外部修改的影响。
1.2 Copy与内存管理
在ARC环境下,copy修饰符实际上是一种特殊的强引用。它与strong的关键区别在于:
- strong:直接持有原对象,引用计数+1
- copy:创建原对象的副本,并持有这个副本
通过以下测试代码可以清晰看到区别:
objective-c复制NSMutableString *original = [NSMutableString stringWithString:@"测试"];
NSLog(@"原始对象地址:%p 引用计数:%ld", original, CFGetRetainCount((__bridge CFTypeRef)original));
// 使用strong
NSString *strongRef = original;
NSLog(@"strong引用后 - 原始对象地址:%p 引用计数:%ld", original, CFGetRetainCount((__bridge CFTypeRef)original));
// 使用copy
NSString *copiedRef = [original copy];
NSLog(@"copy后 - 原始对象地址:%p 引用计数:%ld", original, CFGetRetainCount((__bridge CFTypeRef)original));
NSLog(@"副本对象地址:%p 引用计数:%ld", copiedRef, CFGetRetainCount((__bridge CFTypeRef)copiedRef));
输出结果会显示:
- strong引用会增加原对象的引用计数
- copy不会影响原对象的引用计数,而是创建一个新对象
注意:在MRC时代,copy修饰符会自动对副本对象执行retain操作,这是历史遗留问题。ARC下我们不需要再关心这个细节
