核心概念
本质 :Property 是一组访问器方法的声明 (setter/getter) ,编译器可以自动"合成"「访问器」以及「底层存储(ivar)」,并且允许用点语法调用。
-
例如:
swift@property (nonatomic) NSInteger age; -
编译器等价(自动合成):
swift{ NSInteger _age; // 可选的"底层存储" (backing ivar) } - (NSInteger)age { return _age; } // getter - (void)setAge:(NSInteger)age { _age = age; } // setter
好处 :统一内存语义(strong/weak/copy...)、线程原子性控制(atomic/nonatomic)、可读性与 KVC/KVO 兼容。
常见属性修饰符
- 读写性
- readwrite:可读可写(默认)
- readonly:只读
- 原子性
- atomic :保证"单次访问器调用"的原子性,速度慢。(默认)
- 注意:atomic慢,且不是线程安全的,一致性还是要显式同步。
- nonatomic:不做同步,速度快。
- atomic :保证"单次访问器调用"的原子性,速度慢。(默认)
- 内存语义修饰符
- strong :持有关系,引用计数+1,新值
retain,旧值release.- 场景:一般对象所有权、父持子
- weak :不持有,引用计数不变,对象释放时指针置空。
- 场景:避免循环引用,如
delegate,子持父、IBOutlet。 - 注意:访问时可能已经变 nil。
- 场景:避免循环引用,如
- copy :生成不可变副本,setter 执行
-(id)copy方法。- 场景:阻止赋值可变对象的后续修改,
block入堆。
- 场景:阻止赋值可变对象的后续修改,
- assign :位拷贝,引用计数不变。
- 场景:用于标量和结构体
- 注意:对象指针使用 assign 会产生悬垂指针
- unsafe_unretained :不持有,引用计数不变,对象释放不会置空。
- 场景:以往无
weak可用时使用的。
- 场景:以往无
- strong :持有关系,引用计数+1,新值
- 其他
- getter=isEnabled/setter=setFoo:指定自定义 setter/getter。
- class:类属性。
何时存储(背后存储backing ivar的规则)
- 会有存储
- 在类的
@interface或类扩展里声明@property。 - 没有显式使用
@dynamic,且没有同时手写 setter + getter 的。
- 在类的
- 不会有存储
- 在
category里声明的@property。 - 使用
@dynamic的, 承诺运行时提供访问器的。 - 已经实现了 getter + setter 的。
- 协议
@protocol里的@property。 - 类属性。
- 在
- 例外和细节
-
readonly若你实现了 getter,则不会再自动合成ivar。 -
"类属性"没有
ivar实例,通常用static或者其他存储来实现存储。swift@interface Config : NSObject @property (class, nonatomic, copy) NSString *build; @end @implementation Config static NSString *_build; + (NSString *)build { return _build; } + (void)setBuild:(NSString *)b { _build = [b copy]; } @end -
分类里的属性如何有"存储"?
- 分类里的属性需要通过关联对象实现存储。
swift#import <objc/runtime.h> @interface UIView (Badge) @property (nonatomic, copy) NSString *badgeText; // 分类里不会有 ivar @end @implementation UIView (Badge) static const void *kBadgeKey = &kBadgeKey; - (void)setBadgeText:(NSString *)badgeText { objc_setAssociatedObject(self, kBadgeKey, badgeText, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)badgeText { return objc_getAssociatedObject(self, kBadgeKey); } @end
-
@dynamic 与 @synthesize 与 计算属性
-
@dynamic
-
作用:告诉编译器,不需要生成访问器和
ivar,也不要因为找不到方法而告警。 -
场景:Core Data 的
NSManagedObject子类:swift@interface Book : NSManagedObject @property (nonatomic, copy) NSString *title; @end @implementation Book @dynamic title; // 访问器由运行时(Core Data)注入;编译器不生成也不报缺实现 @end
-
-
@synthesize
- 作用:让编译器为
@property生成 getter/setter 以及背后存储ivar,并把属性名映射到自定义ivar名。
- 作用:让编译器为
-
计算属性
- 作用:不依赖存储,按需计算。
Property 和 ivar 的区别
ivar== 纯存储property== 访问这个存储的"方法接口"- 大多数情况使用
self.age,在init/dealloc/自定义访问器内部常用_age直接访问,避免递归等问题。