四道笔试题详解

163 阅读4分钟

网上笔试题.jpg

最近在技术群看到的4道面试题。感觉挺难的,就google+自己测+交流然后得出的自己的见解,有什么错误,欢迎指出。

1.NSDictionary的数据结构与算法:

Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key

苹果官方给出的资料表明是使用hash table进行数据存储和访问的。

2.类方法,实例方法与runtime的联系: 这题在我看了应该是最简单的,因为平时接触runtime比较多,所以大致了解一点。这道题应该这么问:runtime是怎样调用类方法和实例方法的。 这个答案一搜一大堆,但是我还是要说一下我的见解。

1.当调用了一个方法时,objc_msgSend通过对象的isa指针获取到类的结构体,在结构体的struct objc_cache *cache  中寻找是否有该方法的缓存,如果cache没有,才去struct objc_method_list **methodLists中查找方法。

2.假如没找到,就会去这个类的父类的isa指针找到其父类的结构体,并在父类的分发表里面查找方法的selector。

3.一旦遍历搜索到与SEL相符的selector,就会根据这个selector获取到对应的imp指针,从而找到函数实现的起始位置。

4.假如没有找到就会进入消息转发流程。调用_objc_msgForward函数,这个函数大致会调用resolveInstanceMethod,forwardingTargetForSelector,methodSignatureForSelector,forwardInvocation,这些方法都可以在子类中重载。 注意:假如使用Category重载了类的对应方法,runtime只是把这个Category的方法加到原方法的前面。因此搜索SEL的时候会先匹配到Category的方法,但是原方法还在,依旧可以实现。 类方法则存放在class的metaClass的方法列表中。与实例方法的搜索方式一致。

3.block调用的变量的生命周期: Block本质是一个指向结构体的指针,在初始化block的时候,当block引用了外部变量时,会根据是否加了__block字段来决定是否是值传入,还是地址传入。 在 ARC 中,捕获外部变量的 block 的类会是 NSMallocBlock 或者 NSStackBlock,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 NSStackBlock 变成 NSMallocBlock;但是如果 block 没有被赋值给某个变量,那它的类型就是 NSStackBlock;没有捕获外部变量的 block 的类会是 NSGlobalBlock 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。 Block会强引用是因为是block的结构体中有variables结构体,对捕获的变量或者地址复制到这个结构体中。并且在block中还有一个BlockDescriptor的结构体,这个结构体中有一个gouxi_helper函数指针,当block在析构时会调用这个指针,对强引用对象发送release消息 Facebook检查循环引用的库就是利用dispose指针加上重写release方法达到检查强引用的目的

4.CALayer 的subLayer的数据结构以及重绘顺序 CALayer在内存中是以树形结构存在的,一个CALayer实例也有一个单独的superLayer和上面所有的子层(subLayers),它创建了一个有层次结构的层,我们称之为layer tree,在关于CALayer的文档中我只找到了下面两种tree 第一份,model tree 就是通过代码修改,例如更改layer的属性等等就在这一份。 第二份,presentation tree这棵树的内容是当前正被显示在屏幕上的内容。可以通过对目标视图调用- (id)presentationLayer获取当前展示的layer,这个方法的内部实现就是在presentation tree上获取一份当前显示的layer的拷贝。

presentation layer.png
model layer.png
基本上我们使用动画都是和presentation tree打交道。比如我之前需要绘制一个直线的波浪动画就是利用如下代码:

//通过两个辅助layer在动画过程中的frame的差值来计算出波浪的最大最小值
CALayer *sideHelperPresentationLayer   =  (CALayer *)[helperSideView.layer presentationLayer];
CALayer *centerHelperPresentationLayer =  (CALayer *)[helperCenterView.layer presentationLayer];

CGRect centerRect = [[centerHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];
CGRect sideRect = [[sideHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];

diff = sideRect.origin.x - centerRect.origin.x;
[self setNeedsDisplay];

layer的绘制和UIView是一样的,都是自低向顶层绘制(由父到子layer)