1. 什么情况使用 weak 关键字,相比 assign 有什么不同?
什么情况使用 weak 关键字?
1)在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性
2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong。在下文也有论述:《IBOutlet连出来的视图属性为什么可以被设置成weak?》
不同点:
1)weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
2)assigin 可以用非OC对象,而weak必须用于OC对象。
2. 怎么用 copy 关键字?
用途:
1)NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
2)block也经常使用copy关键字,具体原因见官方文档:Objects Use Properties to Keep Track of Blocks:
block使用copy是从MRC遗留下来的“传统”,在MRC中,方法内部的block是在栈区的,使用copy可以把它放到堆区。在ARC中写不写都行:对于block使用copy还是strong效果是一样的,但写上copy也无伤大雅,还能时刻提醒我们:编译器自动对block进行了copy操作。
说白了,就是用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
3. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
1)因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响。使用copy无论给我们传入的是一个可变对象还是不可对象,我们本身持有的就是一个不可变的副本。
2)如果我们使用是的strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。
当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。
所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
为了理解这种做法,首先要知道,对非集合类对象的copy操作:
在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
-
[immutableObject copy] // 浅复制
-
[immutableObject mutableCopy] //深复制
-
[mutableObject copy] //深复制
-
[mutableObject mutableCopy] //深复制
其原理同上2.
说白了,就是用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
4. 这个写法会出什么问题: @property (copy) NSMutableArray *array;
两个问题:
1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为copy就是复制一个不可变NSArray的对象;
2、使用了atomic属性会严重影响性能。
第1条的相关原因可以有2.中得出。
第2条原因,如下:
该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明nonatomic可以节省这些虽然很小但是不必要额外开销。
在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不使用同步锁。请注意,尽管没有名为“atomic”的特质(如果某属性不具备nonatomic特质,那它就是“原子的”(atomic))。
在iOS开发中,你会发现,几乎所有属性都声明为nonatomic。
一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全” ( thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。例如,一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读到不同的属性值。
因此,开发iOS程序时一般都会使用nonatomic属性。但是在开发Mac OS X程序时,使用 atomic属性通常都不会有性能瓶颈。
5. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的。
@property 的本质是什么?
@property = ivar + getter + setter;
下面解释下:
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。这个概念已经定型,并且经由“属性”这一特性而成为Objective-C 2.0的一部分。 而在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正因为有了这种严格的命名规范,所以 Objective-C 这门语言才能根据名称自动创建出存取方法。其实也可以把属性当做一种关键字,其表示:
编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。 所以你也可以这么说:
@property = getter + setter;
例如下面这个类:
1
2
3
4
|
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
|
上述代码写出来的类与下面这种写法等效:
1
2
3
4
5
6
|
@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
|
ivar、getter、setter 是如何生成并添加到这个类中的?
“自动合成”( autosynthesis)
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”( autosynthesis)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName与_lastName。也可以在类的实现代码里通过 @synthesize语法来指定实例变量的名字.
1
2
3
4
|
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
|
我为了搞清属性是怎么实现的,曾经反编译过相关的代码,大致生成了五个东西:
1)OBJC_IVAR_$类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
2)setter与getter方法对应的实现函数
3)ivar_list :成员变量列表
4)method_list :方法列表
5)prop_list :属性列表
也就是说我们每次在增加一个属性,系统都会在ivar_list中添加一个成员变量的描述,在method_list中增加setter与getter方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
6. @protocol 和 category 中如何使用 @property
1)在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
2)category 使用 @property 也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数:
①objc_setAssociatedObject
②objc_getAssociatedObject
7. @property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
属性可以拥有的特质分为四类:
-
原子性---nonatomic特质
在默认情况下,由编译器合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不使用同步锁。请注意,尽管没有名为“atomic”的特质(如果某属性不具备nonatomic特质,那它就是“原子的” ( atomic) ),但是仍然可以在属性特质中写明这一点,编译器不会报错。若是自己定义存取方法,那么就应该遵从与属性特质相符的原子性。
-
读/写权限---readwrite(读写)、readooly (只读)
-
内存管理语义---assign、strong、 weak、unsafe_unretained、copy
-
方法名---getter=、setter=
getter=的样式:
1
|
@property (nonatomic, getter=isOn) BOOL on;
|
( setter=这种不常用,也不推荐使用。故不在这里给出写法。)
-
不常用的:nonnull,null_resettable,nullable
8. weak属性需要在dealloc中置nil么?
不需要。
在ARC环境无论是强指针还是弱指针都无需在deallco设置为nil,ARC会自动帮我们处理。
也即:在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。
9. @synthesize和@dynamic分别有什么作用?
1)@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
2)@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
3)@dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
10. ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
-
对应基本数据类型默认关键字是
atomic,readwrite,assign
-
对于普通的OC对象
atomic,readwrite,strong