iOS 面试题面试

iOS : 一个区分很大的面试题

2017-07-17  本文已影响61人  胡小夜大叔

这是 sunny博客的一道面试题,还挺值得一说的,主要考基础扎实不扎实。

考察一个面试者基础咋样,基本上问一个 @property 就够了:

@property 后面可以有哪些修饰符?
什么情况使用 weak 关键字,相比 assign 有什么不同?
怎么用 copy 关键字?
这个写法会出什么问题: @property (copy) NSMutableArray *array;
如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
这一套问题区分度比较大,如果上面的问题都能回答正确,可以延伸问更深入点的:

@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
@protocol 和 category 中如何使用 @property
runtime 如何实现 weak 属性
每个人擅长的领域不一样,我们一般会从简历上找自己写擅长的技术聊,假如自己并不是很熟,最好别写出来或扯出来,万一面试官刚好非常精通这里就露馅了。

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

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

一般weak使用的地方有:

weak 和 assign 的小区别:
哎呀上面刚说了...复制一下吧....

一般情况下 weak 用来修饰对象类型,而 assign 来修饰一般数据类型, id 类型看情况用。是因为 weak 修饰的对象在释放后被自动置为 nil,而 assign 不会有这部操作,所以语法上 assign 也是可以修饰对象类型,但这样很容易野指针,所以一般用 weak 修饰,但是 weak 从语法上就不能修饰一般数据类型


3.怎么用 copy 关键字?

要知道怎么要用copy关键字我们首先得知道深浅拷贝的概念:

然后我们在看下 copy 关键字使用场景:

我们的初衷是给 _str1 赋值为 @"str1" , 这种场景下,
如果 str1strong 关键字修饰的,那么打印会发现 _str1 的 值为 @"str1 - mutStr1" , 并且 _str1 的地址就是 mutStr1 的地址,
如果 str1copy 关键字修饰的,那么打印会发现 _str1 的 值为 @"str1" , 并且 _str1 的地址是一个新的地址,
显然第二种结果才是我们想要的, 所以NSStringNSArrayNSDictionary这些属性用copy来修饰。

扩展一下:
反过来NSMutableStringNSMutableArrayNSMutableDictionary 却不能用 copy 修饰,因为 copy 深拷贝后的值是一个 非mutable 的,这样你再增删改就会直接崩溃。

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

哎呀上面也刚说了...再复制一下吧....

反过来NSMutableString,NSMutableArray,NSMutableDictionary 却不能用 copy 修饰,因为 copy 深拷贝后的值是一个 非mutable 的,这样你再增删改就会直接崩溃。

其实只要理解了深浅拷贝,这些很容易理解,就不详细举例子了


5.如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

要想自己的类能用 copy 修饰符,就要给实现 NSCopying 协议, 要是有 mutable 需求的就实现 NSMutableCopying

如何重写带copy关键字的setter :

假如我们有一个类实现了 NSCopying 协议:

@interface CopyClass : NSObject <NSCopying>
@property (nonatomic, strong) NSString *str1;
@property (nonatomic, strong) NSString *str2;
@end

@implementation CopyClass 
- (id)copyWithZone:(nullable NSZone *)zone
{
    CopyClass *copyObject = [[CopyClass alloc] init];
    copyObject.str1 = copyObject.str1;
    copyObject.str2 = copyObject.str2;
    return copyObject;
}
@end

那么我们在外部调用假如是这样的:

@property (nonatomic, copy) CopyClass *myCopyB;

调用的地方:

CopyClass *copyA = [[CopyClass alloc] init];
copyA.str1 = @"str1";
copyA.str2 = @"str2";

self.myCopyB = copyA;

一般如果属性带 copy 关键字 ,那么 setter 的默认实现就是类似这样:

-(void)setMyCopyB:(CopyClass *)myCopyB
{
    _myCopyB = [myCopyB copy];
}

那这道题说如何重写带 copy 关键字的 setter,该咋写咋写呗,这还能玩出花来?不太懂这第二问的意思.....


6.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

__@property 的本质就是 ivar + getter + setter __
我们往一个类里添加一个属性,然后用runtime方法去观察,就可以知道,这个类的
objc_ivar_list 里添加了对应的变量,methodLists 添加了对应的 sette 和 getter

那么ivar、getter、setter 是如何生成并添加到这个类中的
编译期,编译器会自动生成这个属性的 setter 和 getter 代码,并且向类中添加对应的实例变量,生成的结果和属性的声明是否有setter、getter关键字,还有@synthesize指定的变量名有关系。

也就是说我们每次在增加一个属性,系统都会在 ivar_list中添加一个成员变量的描述,在 method_list中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.


7.@protocol 和 category 中如何使用 @property

我们知道在类里定义一个属性时,会在该类的ivar_list添加ivar描述,会在method_list添加该属性的seter和geter方法描述,在property_list中添加一个属性描述。

但在@protocol 和 category 中如果定义一个 @property 时,只会在property_list中添加一个属性描述,method_list 和 ivar_list中都不会有相关的描述添加。

所以要想@protocol 和 category 中的 @property 有意义,就必须在类中或category中,实现这个@property的setter和getter。

但是 ivar_list 是没有该属性对应的 ivar, 所以 setter 中你是没法赋值的, 这时候就要用到 objc_setAssociatedObject 了,在 setter 中用 objc_setAssociatedObject 赋值,并在 getter 中用 objc_getAssociatedObject 取值,这样就变相完成了@protocol 和 category 中属性的设置


8.runtime 如何实现 weak 属性

首先我们要知道 weak 关键字的一些点:

存放weak对象映射的表是由一个自旋锁管理的哈希表,当有 weak 引用时,会把 weak 对象指向的对象那个内存地址作为 weak 表的 key, 然后 value 就是 weak 对象的地址,当指向的那个对象被释放的时候, 会拿着被释放对象的地址来在 weak 里查,查到的对应的 weak 对象就会被置为nil, 然后从 weak 表中删除。


上一篇下一篇

猜你喜欢

热点阅读