iOS 常用修饰词

ARC下不指定任何属性关键字时,默认的关键字有哪些?

对于基本数据类型默认关键字是: atomic, readwrite, assign
对于普通的OC对象: atomic, readwrite, strong

@property, @synthesize, @dynamic

@property = interface + ivar(实例变量/成员变量) + setter/getter

@property 属性有两个关键词 @synthesize 和 @dynamic。默认是 @synthesize。

@synthesize 如果你没有手动实现 @setter 和 @getter 方法,编译器会自动为你加上这两个方法。

@dynamic 告诉编译器属性的 @setter 和 @getter 方法由用户自己实现,不自动生成。

⚠️ 以下几点需要注意 ⚠️

  1. 假如一个属性被标记为 @dynamic var,如果没有提供 @setter和 @getter 方法,当程序调用 @setter或 @getter时会崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

  2. 默认情况编译器自动为 @property 生成 ivar(实例变量/成员变量)、@setter和 @getter 方法。当重写 @property 的 @setter 和 @getter 方法时,系统就不会生成 ivar(实例变量/成员变量)

  3. 手动实现 ivar(实例变量/成员变量)的 @setter 和 @getter 方法,KVC的访问会触发 @setter方法,_pId除了无法通过点语法访问外,其他表现与@property无异

  4. 以下情况不会 autosynthesis(自动合成):
    参考stackoverflow

    1
    2
    3
    4
    5
    6
    1.同时重写了 @setter 和 @getter 方法时
    2.重写了只读属性的 @getter 方法时
    3.使用了 @dynamic 声明时
    4.在 @protocol 中定义的所有属性
    5.在 category 中定义的所有属性
    6.重载的属性

readonly, readwrite

readonly: 只读,默认只生成getter方法,不生成setter方法。如果调用setter方法会报 Assignment to readonly property 错误。

注意 我们可以利用kvc来修改readonly属性

如果我们在项目中做如下修改会发现程序会崩溃,txt属性无法修改

参考官方文档Key-Value Coding Programming Guide可知:

默认该方法返回yes,访问器会寻找名为 _\<key>, _is\<Key>, \<key>, is\<Key>的成员变量. 如果返回false, 就会跳转至第6步报[valueForUndefinedKey:]错误。

readwrite: property默认修饰词,代表可读可写。

nonotomic, automic

automic: 原子性,系统默认的属性修饰词,相对线程安全的,因为系统生成的getter/setter方法会进行加锁操作,这个锁仅仅保证了getter和setter存取方法的线程是安全的。

1
2
3
ep: 线程1调用了某一属性的setter方法并进行到了一半, 线程2调用其getter方法,那么会执行完setter操作后,在执行getter操作, 线程2会获取到线程1 setter后的完整的值.

当几个线程同时调用同一属性的setter、getter方法时,会get到一个完整的值,但get到的值不可控.

nonotomic: 非原子性,效率较快,线程不安全, 如果有两个线程访问同一个属性, 会出现数据紊乱

assign, weak, strong

assign: 在MRC 和 ARC下都可以使用,修饰基本数据类型,不会使对象的引用计数 +1, 如果用assign去修饰对象,这个对象会被立即释放,assign在被释放的时候是不会自动置为nil,保留对象的指针地址,从而形成野指针(EXC_BAD_ACCESS),如果给该对象发送消息会奔溃.

weak: 用于修饰对象,不会使对象的引用计数 +1, 当对象被释放时自动置为nil(oc向nil发送消息不会崩溃),比较常用的是delegate属性设置,相当于MRC下的assign,如果用来修饰基本数据类型xcode会报错

weak底层原理

1
2
3
4
5
runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

1. 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2. 添加引用时:objc_initWeak函数会调用 storeWeak() 函数, storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3. 释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

strong: 用于修饰对象,引用计数+1,相当于MRC下的reatin

retain, copy

copy: 分为深复制和浅复制

1
2
3
深复制: 内容拷贝,源对象和副本对象指向两个不同的对象,源对象的引用计数不变,副本对象的引用计数为1。

浅复制: 指针拷贝,源对象和副本对象指的是同一个对象,对象的引用计数+1,相当于retain

只有不可变对象创建不可变副本(copy)才是浅复制,其它的都是深复制,copy减少对象的上下文依赖。

⚠️被定义为copy属性的对象必须要符合NSCopying协议,实现- (id)copyWithZone:(NSZone *)zone方法。

retain: 常用在MRC下,引用计数+1,只能修饰oc对象(CoreFoundation是C语言框架,没有引用计数,不能用retain修饰),即对象的指针拷贝。

nonnull, nullable, null_resettable, null_unspecified

nonnull: 不能为空,用来修饰属性,或者方法的参数,方法的返回值,不适用于assign属性,因为他是专门用来修饰指针的

1
2
3
@property (nonatomic, copy, nonnull) NSString *name;
@property (nonatomic, copy) NSString *_Nonnull name;
@property (nonatomic, copy) NSString *__nonnull name;

nullable: 表示可以为空,代码提示会告诉你这个谁能够是可以为空的

1
2
3
@property (nonatomic, copy, nullable) NSString *name;
@property (nonatomic, copy) NSString *_Nullable name;
@property (nonatomic, copy) NSString *__nullable name;

null_resettable: 表示get方法不能返回为空, set方法可以为空, 必须重写该属性的get方法,保证返回值不为空,且只有下面这一种表达方式

1
@property(nonatomic, strong, null_resettable) NSNumber * number;

null_unspecified: 表示不确定是否为空

1
2
3
@property(nonatomic, strong) NSNumber *_Null_unspecified height;
@property(nonatomic, strong) NSNumber *__null_unspecified height;
@property(nonatomic, strong, null_unspecified) NSNumber *height;

以上内容参考以下文章:

iOS中属性的关键修饰词:strong weak assign copy retain等

weak实现原理