iOS面试属性@property看我就够了!!!
属性@property看我就够了
- property属性的本质
- ivar、getter、setter 是如何生成并添加到这个类中的?
- @protocol 和 category 中如何使用 @property
- ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
- @property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
- atomic noatomic区别
- 怎么用 copy 关键字?
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
- 什么情况使用 weak 关键字,相比 assign 有什么不同?
- IBOutlet连出来的视图属性为什么可以被设置成weak?
- 浅拷贝和深拷贝的区别?
- 系统对象的 copy 与 mutableCopy 方法
- 这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;
- 如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
- 写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString *name
- @synthesize 和 @dynamic 分别有什么作用?
- 常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int
- id 声明的对象有什么特性?
- 为什么我们常见的delegate属性都用是week而不是retain/strong?
property属性的本质
@property() = ivar + getter + setter;
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。使用@property 系统会自动生成setter和getter方法;
ivar、getter、setter 是如何生成并添加到这个类中的?
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(@synthesize)。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字
@protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只会生成 setter 和 getter 方法声明,并没有实现的方法,我们使用属性的目的,是希望遵守我协议的对象能实现该属性category 使用 @property 也是只会生成 setter 和 getter 方法的声明,如果我们真的需要给 category 增加属性的实现,需要借助于运行时的两个函数:
- objc_setAssociatedObject()
- objc_getAssociatedObject()
ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
对应基本数据类型默认关键字是
- atomic
- readwrite
- assign
对于普通的 Objective-C 对象
- atomic
- readwrite
- strong
@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
属性可以拥有的特质分为四类:
- 原子性 nonatomic 、atomic
- 读/写权限 readwrite(读写)、readonly (只读)
- 内存管理语义 assign、strong、 weak、unsafe_unretained、copy
- 方法名 getter= 、setter=
- 不常用的 nonnull、null_resettable、nullable
atomic noatomic区别
- 系统自动生成的 getter/setter 方法不一样。如果你自己写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用,写不写都一样。
- 对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能得到一个完好无损的对象。
- nonatomic的速度要比atomic快
atomic能保证线程安全吗?
- atomic可并不能保证线程安全。如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。
atomic底层实现
//implementation
@synthesize icode = _icode;
//set
-(void)setIcode:(UIImage *)icode
{
//同步代码块
@synchronized (self) {
if(_icode != icode)
{
[_icode release];
_icode = [icode retain];
}
}
}
//get
-(UIImage *)icode
{
UIImage *image = nil;
//同步代码块
@synchronized (self) {
image = [[_icode retain] autorelease];
}
return image;
}
@synchronized (self),对,性能最低的一把????或许苹果大大有自己的用意吧。。。
关于????后续文章会详细介绍!!!
总结:
atomic:系统自动生成的getter/setter方法会进行加锁操作
- 是默认的
- 会保证 CPU 能在别的线程来访问这个属性之前,先执行完当前流程
- 速度不快,因为要保证操作整体完成
noatomic:系统自动生成的getter/setter方法不会进行加锁操作
- 不是默认的
- 更快
- 线程不安全
- 如有两个线程访问同一个属性,会出现无法预料的结果
怎么用 copy 关键字?
用途:
- NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
- block 也经常使用 copy 关键字。
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。
用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
重写带copy关键字的setter方法
- (void)setName:(NSString *)name {
_name = [name copy];
}
- 因为父类的指针可以指向子类的对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入一个可变对象还是不可变对象,我本身持有的就是一个不可变的副本。
- 如果我们使用时strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部修改了,那么会影响该属性
- copy特质所表达的所属关系与strong类似,然而设置方法并不保留新值,而是将其拷贝,当属性类型是NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例,这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
- readwrite 是可读可写特性。需要生成getter方法和setter方法。
- readonly 是只读特性。只会生成getter方法,不会生成setter方法,不希望属性在类外改变
- assign 是赋值特性。setter方法将传入参数赋值给实例变量;仅设置变量时,assign用于基本数据类型
- retain(MRC)/strong(ARC) 表示持有特性。setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1
- copy 表示拷贝特性。setter方法将传入对象复制一份,需要完全一份新的变量时
- nonatomic 非原子操作。决定编译器生成的setter和getter方法是否是原子操作,atomic表示多线程安全,一般使用nonatomic,效率高
什么情况使用 weak 关键字,相比 assign 有什么不同?
- 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
- 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。
差异
- assign 可以用非 OC 对象,而 weak 必须用于 OC 对象。
- weak 表明该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。
IBOutlet连出来的视图属性为什么可以被设置成weak?
- 因为父控件的subViews数组已经对它有一个强引用。
浅拷贝和深拷贝的区别?
- 浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
- 深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。
系统对象的 copy 与 mutableCopy 方法
不管是集合类对象(NSArray、NSDictionary、NSSet … 之类的对象),还是非集合类对象(NSString, NSNumber … 之类的对象),接收到copy和mutableCopy消息时,都遵循以下准则:
- copy 返回的是不可变对象(immutableObject);如果用copy返回值调用mutable对象的方法就会crash。
- mutableCopy 返回的是可变对象(mutableObject)。
非集合类对象的copy与mutableCopy
在非集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
NSString *str = @"hello word!";
NSString *strCopy = [str copy] // 指针复制,strCopy与str的地址一样
NSMutableString *strMCopy = [str mutableCopy] // 内容复制,strMCopy与str的地址不一样
NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
NSString *strCopy = [mutableStr copy] // 内容复制
NSMutableString *strMCopy = [mutableStr mutableCopy] // 内容复制
集合类对象的copy与mutableCopy (同上)
在集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对集合内的对象元素仍然是指针复制。(即单层内容复制)
NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"];
NSArray *copyArr = [arr copy]; // 指针复制
NSMutableArray *mCopyArr = [arr mutableCopy]; //单层内容复制
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArr = [mutableArr copy]; // 单层内容复制 NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 单层内容复制
【总结一句话】:
只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!
这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;
问题:添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。
如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法)
原因:是因为 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。
如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
具体步骤:
-
需声明该类遵从 NSCopying 协议
-
实现 NSCopying 协议的方法。
该协议只有一个方法: - (id)copyWithZone:(NSZone *)zone;
注意:使用 copy 修饰符,调用的是copy方法,其实真正需要实现的是 “copyWithZone” 方法。
写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString *name
// retain
- (void)setName:(NSString *)str {
[str retain];
[_name release];
_name = str;
}
// copy
- (void)setName:(NSString *)str {
id t = [str copy];
[_name release];
_name = t;
}
@synthesize 和 @dynamic 分别有什么作用?
@property有两个对应的词,
- @synthesize(合成实例变量),
- @dynamic。
如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
// 在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
- @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
- @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int
Objective-C的数据类型有
- NSString
- NSNumber
- NSArray
- NSMutableArray
- NSData等等,
这些都是class,创建后便是对象,而什么样的定西能被称为对象??
继承NSObject NSProxy才能叫对象
而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是long。
id 声明的对象有什么特性?
答:id 声明的对象具有运行时的特性,即可以指向任意类型的Objcetive-C的对象。
为什么我们常见的delegate属性都用是week而不是retain/strong?
是为了防止delegate两端产生不必要的循环引用。
@property (nonatomic, weak) id delegate;
开始敲黑板了!! 常见的都是weak,不常见的在哪里???
NSURLSession代理的强引用
行笔简陋,如有问题,敬请指正!下期再见!
下一篇: OC----构造方法