@property & 拷贝

2017-11-28  本文已影响18人  yaoyao妖妖

1. @property 后面可以有哪些修饰符?

属性可以拥有的特质可以分为四类:

1)原子性:

2)读写权限:

3)内存管理语义:

注:block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。

4)方法名:(自定义存取方法名)
getter =<>
setter =<>

注: 同时重写setter和getter方法的时候,编译器不会自动生成_xxx 实例变量,需要手动添加@synthesize xxx = _xxx;
例子:

这时你就可以使用下面的方式来避免编译器报错:

@property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString *initBy;

另外也可以用关键字进行特殊说明,来避免编译器报错:

@property(nonatomic, readwrite, copy, null_resettable) NSString *initBy;

- (NSString *)initBy __attribute__((objc_method_family(none)));

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

  1. 对应基本数据类型默认关键字是
    atomic, readwrite, assign
  2. 对于普通的 Objective-C 对象
    atomic, readwrite, strong

3.什么情况使用 weak 关键字,相比 assign 有什么不同?

4.什么时候用 copy 关键字?

一般是 NSString, block, 不可变的集合类时使用

5.这个写法会出什么问题:@property (copy) NSMutableArray *array;

调用NSMutableArray方法时出现奔溃。

6.拷贝:

指针复制,没有对象的复制(不会重新分配存储空间)

两个集合的地址不一样,同时两个集合的所存储的元素的地址是不一样的。

从左到右:
@property (nonatomic, copy) NSString *string ;
@property (nonatomic, copy) NSMutableString *mutableString;
@property (nonatomic, mutableCopy) NSString * string ;
@property (nonatomic, mutableCopy) NSMutableString *mutableString;

屏幕快照 2017-11-28 13.25.03.png

注:集合类仿照上面的一样,区别是将非集合类的深复制结果变为单层的深复制即可。

字符串类


屏幕快照 2017-11-28 13.44.23.png

集合类


屏幕快照 2017-11-28 13.38.16.png

下面我们来看一下具体的语义是怎样实现的?

retain:始终执行浅拷贝,引用计数每次加一,返回对象是否可变与被复制的对象一致;
copy 对于不可变为浅复制,引用计数每次加一;可变为深复制,引用计数不变;
mutableCopy 始终执行深拷贝,引用计数不变,始终返回一个可变的对象;

7. 集合的深复制是怎样实现的?

集合的深复制有两种方法。可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制,如

NSDictionary shallowCopyDict = [[NSDictionary alloc]     initWithDictionary:someDictionary copyItems:YES];

如果你用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。
copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。

第二个方法是将集合进行归档(archive),然后解档(unarchive),如:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

8. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

9. 不同内存管理语义下内存的处理方式?

@property (nonatomic,关键字) NSObject *a;
当使用了不同的关键字后自动实现的set方法:

//assign
-(void)setA:(int)a{
        _a=a;
}

//retain
-(void)setA:(Car *)a{
       if(_a!=a){
           [_a release];
           _a=[a retain];
       }
}

//copy
-(void)setA:(NSString *)a{
       if(_a!=a){
          [_a release];
          _a=[a copy];
       }
}

10. @synthesize和@dynamic分别有什么作用?

11. @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;

例如下面这个类:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

上述代码写出来的类与下面这种写法等效:

@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

更新:

property在runtime中是objc_property_t定义如下:

typedef struct objc_property *objc_property_t;
而objc_property是一个结构体,包括name和attributes,定义如下:

struct property_t {
    const char *name;
    const char *attributes;
};
而attributes本质是objc_property_attribute_t,定义了property的一些属性,定义如下:

/// Defines a property attribute
typedef struct {
    const char *name;           /**< The name of the attribute */
    const char *value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

而attributes的具体内容是什么呢?其实,包括:类型,原子性,内存语义和对应的实例变量。

例如:我们定义一个string的property@property (nonatomic, copy) NSString *string;,通过 property_getAttributes(property)获取到attributes并打印出来之后的结果为T@"NSString",C,N,V_string

其中T就代表类型,可参阅Type Encodings,C就代表Copy,N代表nonatomic,V就代表对于的实例变量。

ivar、getter、setter 是如何生成并添加到这个类中的?

“自动合成”( autosynthesis)
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName 与 _lastName。也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字.

@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 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

链接:
https://www.zybuluo.com/MicroCai/note/50592
http://www.jianshu.com/p/a1111480e4a7
https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01《招聘一个靠谱的iOS》面试题参考答案/《招聘一个靠谱的iOS》面试题参考答案(上).md#4-这个写法会出什么问题-property-copy-nsmutablearray-array

上一篇 下一篇

猜你喜欢

热点阅读