OC 属性修饰符的底层逻辑,核心围绕 引用计数管理 和 内存拷贝机制 展开,面试官最爱追问原理差异和使用场景,以下是分点拆解:
1. strong
底层原理
• 本质是强引用,依赖 Runtime 的 objc_setProperty 函数实现。
• 赋值时执行三步操作:
1. 对新值调用 objc_retain,使其引用计数 +1,确保对象不被释放;
2. 对旧值调用 objc_release,使其引用计数 -1,若计数归零则销毁对象;
3. 将属性指针指向新值的内存地址。
• 内存管理:在 ARC 下自动完成 retain/release,MRC 下需手动管理。
面试关键点
• 是 OC 对象属性的默认修饰符,用于持有对象;
• 风险点:容易引发循环引用(如 self 强引用 block,block 又强引用 self),需配合 weak 解决。
2. copy
底层原理
• 核心是创建对象副本,依赖对象必须遵循 NSCopying 协议并实现 copyWithZone: 方法。
• 赋值时流程:
1. 调用新值的 copyWithZone: 方法生成副本;
2. 对副本执行 strong 的 retain/release 逻辑(持有副本,释放旧值);
3. 属性指针指向副本的内存地址。
• 拷贝类型分两种(面试必问):
浅拷贝:源头是不可变对象(如 NSString),copyWithZone: 直接返回原对象,仅引用计数 +1,共享内存(性能高);
深拷贝:源头是可变对象(如 NSMutableString),copyWithZone: 会开辟新内存,生成一个不可变的新对象,与原对象独立。
面试关键点
• 只能修饰不可变对象属性(如 NSString),修饰可变对象(如 NSMutableString)会导致调用可变方法崩溃;
• 核心作用:防止外部可变对象篡改属性值,保证属性的不可变性。
3. weak
底层原理
• 本质是弱引用,不参与引用计数管理,底层依赖 Runtime 的 SideTable(哈希表) 实现。
• 赋值时:直接将指针指向对象地址,不调用 retain,引用计数不变化;
• 对象销毁时:Runtime 会遍历 SideTable 中所有指向该对象的 weak 指针,自动将其置为 nil,从根源避免野指针。
面试关键点
• 专门解决循环引用(如代理模式、block 中引用 self);
• 特点:weak 指针不会持有对象,对象销毁后自动置 nil,是安全的非持有引用。
4. assign
底层原理
• 本质是直接赋值,底层就是简单的内存拷贝(类似 C 语言的 = 赋值)。
• 对于基本数据类型(int/float 等):直接拷贝栈上的数值,无需内存管理;
• 对于 OC 对象:直接拷贝对象的指针地址,但不调用 retain/release,不管理引用计数。
面试关键点
• 仅适用于基本数据类型;
• 修饰 OC 对象时,对象销毁后指针不会置 nil,会变成野指针,访问时触发崩溃,面试中常考“为什么 assign 不能修饰对象”。
面试高频追问应答模板
1. strong 和 copy 的区别?
→ strong 是持有原对象,多个 strong 指针共享同一内存;copy 是生成副本(分深浅拷贝),属性与原对象独立,且 copy 能保证属性不可变。
2. weak 为什么能自动置 nil?
→ 底层依赖 Runtime 的 SideTable 哈希表,对象销毁时 Runtime 会自动遍历并清空所有指向它的 weak 指针。
3. 什么时候用 copy?
→ 当属性是不可变类型(如 NSString),且赋值源头可能是可变对象时,用 copy 防止外部篡改。