Property和ARC
属性本质上是个语法糖,最终是要翻译成一堆东西,包括getter和setter方法,还有内存管理相关的东西:
一、原始代码(你写的)
// MyClass.h
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *name;
@end
// MyClass.m
@implementation MyClass
@end
就这么多。编译器会帮你生成:
二、编译器大概补全的代码(近似还原)
// MyClass.h(扩展后)
@interface MyClass : NSObject {
NSString *_name; // ① 自动合成实例变量
}
- (NSString *)name;
- (void)setName:(NSString *)name;
@end
// MyClass.m(扩展后)
@implementation MyClass
// ② getter 方法(strong 语义下的 ARC 版本)
- (NSString *)name {
return _name;
}
// ③ setter 方法(strong 语义下的 ARC 版本)
- (void)setName:(NSString *)name {
// ARC 插入的 retain/release 逻辑
NSString *oldValue = _name;
_name = [name retain]; // strong: retain 新值
[oldValue release]; // 释放旧值
}
// ④ 如果需要,还会生成 dealloc(但 strong 实例变量会自动 release,通常不需要显式写)
- (void)dealloc {
// ARC 下编译器会自动插入 [_name release];
// 如果是 MRC,这里需要手动写 [_name release];
[super dealloc];
}
@end
⚠️ 注意:上面是逻辑等价的代码,编译器生成的中间表示比这个更底层,但核心语义完全一致。
三、不同属性修饰符的 setter 区别
| 修饰符 | setter 核心逻辑(ARC 下) |
|---|---|
strong | _name = [newValue retain]; [oldValue release]; |
weak | _name = newValue;(不 retain,且如果 newValue 被释放会自动置 nil) |
copy | _name = [newValue copy]; [oldValue release]; |
assign | _name = newValue;(不做任何 retain/release,用于非对象或 weak 的替代) |
unsafe_unretained | 同 assign,但不自动置 nil |
四、验证:不用属性,自己写一样的代码
@interface MyClass : NSObject {
NSString *_name;
}
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
@implementation MyClass
- (void)setName:(NSString *)name {
NSString *old = _name;
_name = [name retain];
[old release];
}
- (NSString *)name {
return _name;
}
@end
这段代码和 @property (strong) NSString *name; 的效果完全一致。
这就是“属性帮你补全”的全部内容:实例变量 + getter + setter(带正确的内存管理)。
五、一个容易被忽略的点:nonatomic 的影响
上面的 setter 是非原子性的简化版。如果换成 atomic(默认),生成的代码会复杂很多:
// atomic + strong 的 setter(简化示意)
- (void)setName:(NSString *)name {
@synchronized(self) {
NSString *old = _name;
_name = [name retain];
[old release];
}
}
大多数 iOS 开发用
nonatomic,因为atomic开销大且不能保证线程安全(只是 getter/setter 原子操作)。
六、属性补全了什么
- 实例变量(默认带下划线前缀)
- getter 方法
- setter 方法(根据 weak/strong/copy 自动插入对应的 retain/release/copy 逻辑)
以
strong为例,生成的 setter 等价于:先 retain 新值,再 release 旧值,最后赋值。”
ARC
属性确实帮你“补全”了ARC的代码,但是它的初衷不在于此。即使OC在设计的时候属性不补全相关retain, release语句,系统仍然有一次全局扫描兜底(不能说兜底,应该是正常情况下的机制),只是有了属性提前把这件事做了,全局扫描的时候就少做了一些事情,所以叫属性“补全”ARC的代码。
不写属性,ARC也生效:
// 没有属性,只有实例变量和局部变量
@interface MyClass : NSObject {
NSString *_name; // 这是一个对象指针
}
@end
@implementation MyClass
- (void)someMethod {
NSString *localString = [NSString stringWithFormat:@"Hello"]; // 局部对象指针
_name = localString; // 直接给实例变量赋值
}
@end
在这个例子中,虽然没有 @property,但 (Private) ARC 依然奏效:
- 它会确保
localString在超出作用域时被正确释放。 - 它会确保
_name这个实例变量在dealloc时被正确管理(对于 _name 直接赋值,其内存管理需要根据其声明的语义来判断,实际上是 strong 的)。