iOS专题

iOS property修饰关键字

2018-11-28  本文已影响0人  小秀秀耶

内存相关

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的用途:

  1. 修饰 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

  1. 添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;
  2. 使用了 atomic 属性会严重影响性能 ;

weak:指针的对象释放后,置为nil

weak用途:

  1. 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性
  2. 自身已经对它进行一次强引用,没有必要再强引用一次。如IBOutlet连出来的视图属性被设置成weak,因为ViewController强引用ViewView强引用视图属性,所以没有必要再强引用一次。

assign: 简单赋值,不改变引用计数,适用于Foundation基础数据类型、C数据类型、id类型

unsafe_unretained:指针的对象释放后,继续指向对象存在的那个内存,这会导致因为访问那个已释放对象引起的崩溃

strongweakunsafe_unretained往往都是用来声明属性的,如果想声明临时变量就得用__strong__weak__unsafe_unretained__autoreleasing

__autoreleasing:可以使对像延迟释放

__autoreleasing参考地址

weakassign的区别:

  1. weak 可以修饰对象类型,assign修改基本数据类型
  2. assign 可以用非 OC 对象,而 weak 必须用于 OC 对象

retain:在MRC环境下,你需要自己retain一个想要保持的对象,用一个指针指向这个对象,只要指针没有被重置为空,对象就会一直在堆上,当指针指向新值的时候,原来的对象就会被release一次

copyretain的区别

copy其实是建立了一个相同的对象,而retain不是;

copy是内容拷贝,retain是指针拷贝;
copy对于像NSString类型的,的确是内容的拷贝 。如果拷贝的是NSArray这时只是copy了指向array中相对应元素的指针,这便是所谓的浅拷贝。

__block__weak的区别

__block不管是ARC还是MRC模式下都可以使用,修饰对象、基本数据类型;
__weak只能在ARC模式下使用,修饰对象;
__block对象可以在block中被重新赋值,__weak不可以。

为什么有__weak还要用__unsafe_unretained呢?

  1. __weak只支持iOS 5.0OS X Mountain Lion作为部署版本(当然对于现在,这个原因已经可以无视了)
  2. __weak对性能会有一定的消耗,使用__weak,需要检查对象是否被释放,在追踪是否被释放的时候当然需要追踪一些信息,那么此时__unsafe_unretained__weak快,而且一个对象有大量的__weak引用对象的时候,当对象被废弃,那么此时就要遍历weak表,把表里所有的指针置空,消耗cpu资源。

那么什么时候使用__unsafe_unretained呢?

当你明确对象的生命周期的时候,可以使用__unsafe_unretained替代__weak,可以稍微提高一些性能,虽然这点性能微乎其微。
举个例子,当A拥有B对象,A消亡B也消亡,这样当B存在,A也一定会存在的时候,此时B要调用A的接口,就可以通过__unsafe_unretained 保持对A的引用关系。

参考地址1

参考地址2

原子性

atomic:(默认),线程有关,通常用于单线程,速度较慢
nonatomic: 线程无关,通常用于多线程,速度较快

atomic : 速度慢, 多线程调用getter、sette方法是线程安全,并不能保证整个对象是线程安全的
nonatomic : 速度快,多线程不安全

atomic、nonatomic参考博客地址

读写相关

readonly
readwrite

readonly : 只读属性,只生成getter方法,也就是说只能访问变量,不能修改
readwrite : (默认),可读可写,生成setter和getter方法

方法名

getter=<name>
setter=<name>
@property (nonatomic, getter=isOn) BOOL on;

能否为空

nullable
nonnull
null_resettable
null_unspecified

nonnull、nullable参考博客地址

nullablenonnullnull_unspecified 修饰属性

@property(nonatomic,copy,nullable)NSString *string;
@property(nonatomic,copy)NSString* __nullable string;
@property(nonatomic,copy)NSString* _Nullable string;

nullablenonnullnull_unspecified 修饰方法

- (void)methodWithString:(nullable NSString*)string;
- (void)methodWithString:(NSString* _Nullable)string;
- (void)methodWithString:(NSString* __nullable)string;

总结:

  1. 对于属性、方法返回值、方法参数的修饰,使用: nonnullnullable
  2. 对于 C 函数的参数、Block 的参数、Block 返回值的修饰,使用: _Nonnull_Nullable建议弃用__nonnull__nullable

Nonnull Audited Regions

NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END在这两个宏之间的代码,所有简单指针对象都被假定为 nonnull ,因此我们只需要去指定那些 nullable 指针对象即可,Xcode10 、iOS12之后默认添加Nonnull Audited Regions区域

不过,为了安全起见,苹果还制定了以下几条规则

  1. 通过 typedef 定义的类型的 nullability 特性通常依赖于上下文,即使是在 Nonnull Audited Regions 中,也不能假定它为 nonnull
  2. 对于复杂的指针类型(如 id * )必须显式去指定是 nonnull 还是 nullable。例如,指定一个指向 nullable 对象的 nonnull 指针,可以使用 __nullable id * __nonnull
  3. 我们经常使用的 NSError **通常是被假定为一个指向 nullable NSError 对象的 nullable指针。

null_resettable : 不为空,只有一种修饰方法

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

null_resettable修饰后,必须确保值不能为nil,可以重写settergetter函数,确保值不为nil

null_unspecified:不确定是否为空

相关问题

1.ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
  1. 对应基本数据类型默认关键字是atomic、readwrite、assign
  2. 对于普通的 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.copymutableCopy

非集合类对象

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制

集合类对象

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制

6.@synthesize和@dynamic分别有什么作用?
  1. @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
  1. @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。当我们同时重写了setter and getter方式时,需要在.m的文件中使用@synthesize string1 = _string1;
  1. @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。@dynamic string2;

招聘一个靠谱的iOS面试题参考答案

上一篇下一篇

猜你喜欢

热点阅读