内存相关
ARC : strong、copy 、weak、assign、unsafe_unretained(xcode 4.3、ios5以上版本)
MRC : retain、weak、copy
strong
:只要某一对象被一个strong
指针指向,该对象就不会被销毁。如果对象没有被任何strong
指针指向,那么就会被销毁,此时所有剩余的weak
型指针都将被清除。在默认情况下,所有的实例变量和局部变量都是strong
类型的。可以说strong
类型的指针在行为上跟MRC下得retain
是比较相似的
copy
:建立一个索引计数为1 的对象,然后释放旧对象
copy
的用途:
- 修饰 NSString、NSArray、NSDictionary
用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份
2.修饰 block
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。
思考这个写法会出什么问题: @property (copy) NSMutableArray *array
- 添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;
- 使用了 atomic 属性会严重影响性能 ;
weak
:指针的对象释放后,置为nil
weak
用途:
- 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用
weak
来解决,比如:delegate
代理属性- 自身已经对它进行一次强引用,没有必要再强引用一次。如
IBOutlet
连出来的视图属性被设置成weak
,因为ViewController
强引用View
,View
强引用视图属性,所以没有必要再强引用一次。
assign
: 简单赋值,不改变引用计数,适用于Foundation基础数据类型、C数据类型、id类型
unsafe_unretained
:指针的对象释放后,继续指向对象存在的那个内存,这会导致因为访问那个已释放对象引起的崩溃
strong
、weak
、unsafe_unretained
往往都是用来声明属性的,如果想声明临时变量就得用__strong
、 __weak
、__unsafe_unretained
、 __autoreleasing
__autoreleasing
:可以使对像延迟释放
weak
和assign
的区别:
weak
可以修饰对象类型,assign
修改基本数据类型assign
可以用非 OC 对象,而weak
必须用于 OC 对象
retain
:在MRC环境下,你需要自己retain
一个想要保持的对象,用一个指针指向这个对象,只要指针没有被重置为空,对象就会一直在堆上,当指针指向新值的时候,原来的对象就会被release
一次
copy
与retain
的区别
copy其实是建立了一个相同的对象,而retain不是;
copy是内容拷贝,retain是指针拷贝;
copy对于像NSString类型的,的确是内容的拷贝 。如果拷贝的是NSArray这时只是copy了指向array中相对应元素的指针,这便是所谓的浅拷贝。
__block
和__weak
的区别
__block不管是ARC还是MRC模式下都可以使用,修饰对象、基本数据类型;
__weak只能在ARC模式下使用,修饰对象;
__block对象可以在block中被重新赋值,__weak不可以。
为什么有__weak还要用__unsafe_unretained呢?
__weak
只支持iOS 5.0
和OS X Mountain Lion
作为部署版本(当然对于现在,这个原因已经可以无视了)__weak
对性能会有一定的消耗,使用__weak
,需要检查对象是否被释放,在追踪是否被释放的时候当然需要追踪一些信息,那么此时__unsafe_unretained
比__weak
快,而且一个对象有大量的__weak
引用对象的时候,当对象被废弃,那么此时就要遍历weak
表,把表里所有的指针置空,消耗cpu资源。
那么什么时候使用__unsafe_unretained呢?
当你明确对象的生命周期的时候,可以使用
__unsafe_unretained
替代__weak
,可以稍微提高一些性能,虽然这点性能微乎其微。
举个例子,当A拥有B对象,A消亡B也消亡,这样当B存在,A也一定会存在的时候,此时B要调用A的接口,就可以通过__unsafe_unretained
保持对A的引用关系。
原子性
atomic:(默认),线程有关,通常用于单线程,速度较慢
nonatomic: 线程无关,通常用于多线程,速度较快
atomic
: 速度慢, 多线程调用getter、sette方法是线程安全,并不能保证整个对象是线程安全的
nonatomic
: 速度快,多线程不安全
读写相关
readonly
readwrite
readonly
: 只读属性,只生成getter方法,也就是说只能访问变量,不能修改
readwrite
: (默认),可读可写,生成setter和getter方法
方法名
getter=<name>
setter=<name>
@property (nonatomic, getter=isOn) BOOL on;
能否为空
nullable
nonnull
null_resettable
null_unspecified
nullable
、nonnull
、null_unspecified
修饰属性
@property(nonatomic,copy,nullable)NSString *string;
@property(nonatomic,copy)NSString* __nullable string;
@property(nonatomic,copy)NSString* _Nullable string;
nullable
、nonnull
、null_unspecified
修饰方法
- (void)methodWithString:(nullable NSString*)string;
- (void)methodWithString:(NSString* _Nullable)string;
- (void)methodWithString:(NSString* __nullable)string;
总结:
- 对于属性、方法返回值、方法参数的修饰,使用:
nonnull
、nullable
; - 对于 C 函数的参数、Block 的参数、Block 返回值的修饰,使用:
_Nonnull
、_Nullable
, 建议弃用__nonnull
、__nullable
Nonnull Audited Regions
NS_ASSUME_NONNULL_BEGIN
和 NS_ASSUME_NONNULL_END
在这两个宏之间的代码,所有简单指针对象都被假定为 nonnull
,因此我们只需要去指定那些 nullable
指针对象即可,Xcode10 、iOS12之后默认添加Nonnull Audited Regions区域
不过,为了安全起见,苹果还制定了以下几条规则
- 通过
typedef
定义的类型的nullability
特性通常依赖于上下文,即使是在 Nonnull Audited Regions 中,也不能假定它为nonnull
; - 对于复杂的指针类型(如 id * )必须显式去指定是
nonnull
还是nullable
。例如,指定一个指向 nullable 对象的 nonnull 指针,可以使用__nullable id * __nonnull
; - 我们经常使用的
NSError **
通常是被假定为一个指向nullable NSError
对象的nullable
指针。
null_resettable
: 不为空,只有一种修饰方法
@property(nonatomic,strong,null_resettable) NSNumber * number;
被null_resettable
修饰后,必须确保值不能为nil,可以重写setter
、getter
函数,确保值不为nil
null_unspecified
:不确定是否为空
相关问题
1.ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
- 对应基本数据类型默认关键字是atomic、readwrite、assign
- 对于普通的 Objective-C 对象atomic、readwrite、strong
2. @property 的本质是什么?
@property = ivar + getter + setter;
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。
3. ivar、getter、setter 是如何生成并添加到这个类中的?
“自动合成”(
autosynthesis
)
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis
)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)
的源代码。除了生成方法代码getter、setter
之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。
也可以在类的实现代码里通过@synthesize
语法来指定实例变量的名字.
4. @protocol 和 category 中如何使用 @property?
在 protocol 中使用 property 只会生成 setter 和 getter 方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
category 使用 @property 也是只会生成 setter 和 getter 方法的声明,如果我们真的需要给 category 增加属性的实现,需要借助于运行时的两个函数:
objc_setAssociatedObject
objc_getAssociatedObject
5.copy
和 mutableCopy
非集合类对象
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制
集合类对象
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制
6.@synthesize和@dynamic分别有什么作用?
- @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
- @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。当我们同时重写了setter and getter方式时,需要在.m的文件中使用
@synthesize string1 = _string1;
- @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
@dynamic string2;