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_unretainedassign,但不自动置 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 原子操作)。


六、属性补全了什么

  1. 实例变量(默认带下划线前缀)
  2. getter 方法
  3. setter 方法(根据 weak/strong/copy 自动插入对应的 retain/release/copy 逻辑)

strong 为例,生成的 setter 等价于:先 retain 新值,再 release 旧值,最后赋值。”


ARC

属性确实帮你“补全”了ARC的代码,但是它的初衷不在于此。即使OC在设计的时候属性不补全相关retainrelease语句,系统仍然有一次全局扫描兜底(不能说兜底,应该是正常情况下的机制),只是有了属性提前把这件事做了,全局扫描的时候就少做了一些事情,所以叫属性“补全”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 的)。