面试问题记录

1、UIView和UILayer的关系?
不同的地方:
1 )继承结构不同
UIView继承自UIResponder间接继承自NSObject的,CALayer是直接继承NSObject。因此UIView是可以响应事件的,如手势;CA Layer则不可以。
2)所属框架不同
UIView在UIKit.framework中定义的,UIKit主要是用来构建用户界面,并且可以响应事件。CALayer则在QuartzCore.framework中定义的,CALayer是用来承载绘制内容的底层对象。
3)相似的树形结构
4)在做iOS动画的时候,修改非rootlayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。

总结两者最大的不同则是,UIView可以响应用户事件,而CALayer不可以。UIView侧重于对内容的绘制,UIView侧重于对显示内容的管理。

UIView和CALayer是相互依赖的关系。UIView依赖于CALayer提供的内容,CALayer依赖UIView提供的容器来显示绘制的内容。归根结底CALayer是一切的基础,如果没有CALayer,UIView自身也不会存在。UIView是一个特殊的CALayer实现且添加了响应事件的能力。UIView本身更像是一个CALayer的管理器,访问它的跟绘图以及坐标相关的属性,例如frame、bounds等等,实际上内部都是在访问它所包含的CALayer的相关属性。UIView的layer树形在系统中,存在着三份copy:
一、逻辑树,代码里可以操作的,如修改layer的属性等就在这里;
二、动画树,中间层,系统正是在这一层上更改属性,进行各种渲染操作;
三、显示树,这棵树的内容是当前正被显示在屏幕上的内容。
这三棵树的逻辑结构都是一样的,区别只有各自的属性。
UIView的主layer以外,对它的subLayer即子layer的属性进行更改,系统将自动进行动画生成。
CALayer的坐标系系统和UIView的有点不一样,多了一个anchorPoint属性(相对于bound 坐标系)(CGPoint类型,值域是0-1,即按照比例来设置),这个点是各种图形变换的坐标原点,同时会改变layer的position位置(相对于superLayer),它的缺省值是{0.5,0.5},layer的中央

frame、position以及anchorPoint关系:
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;

2、hitTest
就直接描述一下事件响应链吧。
一、查找用户点击视图。
当用户点击某视图时,UIWindow首先接受到响应,此响应包括用户点击区域和一个封装好的UIEvent对象,然后UIWindow会通过这些信息调用pointInside:withEvent:方法返回yes得知用户点击的范围ViewA(假设);ViewA调用hitTest:withEvent:方法,在方法中遍历所有的subView(如ViewB、ViewC)调用hitTest:withEvent:方法;在遍历中发现使用ViewC调用pointInside:withEvent:方法时返回yes得知用户点击范围ViewC中;再遍历ViewC中各子视图,并调用pointInside:withEvent:判断点击范围;重复上述过程直至找到一个视图的subviews个数为0,此视图即为用户点击的View;
二、响应用户点击事件
利用UIResponder来实现,具体方法为UIResponder的nextResponder方法。
可以通过用户点击View,查看View是否响应了点击事件,如果没有,则找其nextResponder,如果没有再继续找,直至找到AppDelegate在没有响应,则点击事件会被系统丢弃
注意:
在调用nextResponder有以下几条规则:

  1. 当一个view调用其nextResponder会返回其superView;
  2. 如果当前的view为UIViewController的view被添加到其他view上,那么调用nextResponder会返回当前的UIViewController,而这个UIViewController的nextResponder为view的superView;
  3. 如果当前的UIViewController的view没有添加到任何其他view上,当前的UIViewController的nextResponder为nil,不管它是keyWinodw或UINavigationController的rootViewController,都是如此;
  4. 如果当前application的keyWindow的rootViewController为UINavigationController(或UITabViewController),那么通过调用UINavigationController(或UITabViewController)的nextResponder得到keyWinodw;
  5. keyWinodw的nextResponder为UIApplication,UIApplication的nextResponder为AppDelegate,AppDelegate的nextResponder为nil。

hit-Test View具体的应用见链接:http://www.jianshu.com/p/d8512dff2b3e

3、block
1)什么是block?
block是带有自动变量值的匿名函数,它也属于对象(有isa指针)。
2)block类型
根据block存储域block主要有三种类型,分别为:
_NSConcreteGlobalBlock存储在栈上,没有引用外部变量(但static以及全局变量除外);
_NSConcreteStackBlock,存储在程序的数据区域(.data区),引用了外部变量,不会持有外部变量;
_NSConcreteMallocBlock存储在堆上,一个block被copy时会生成_NSConcreteMallocBlock,会持有外部变量;
3)__block修饰符
当我们想在block体中修改引用的外部变量值(不包括全局以及static变量)的时候,需要使用__block来修饰外部变量。当我们对一个block进行copy的时候,若此block为stack类型,__block变量也会随着block的copy,从栈复制到堆并被block所持有;当对malloc类型的block进行copy的话,对__block变量没有任何影响。
4)block的循环引用问题
block是对象,拥有着和对象一样的生命周期,如果没有强引用的话,就会被释放;若产生循环引用,可以使用__weak(ARC)或者__block(MRC)来解决
MRC环境下,如果没有用__block会持有引用对象,而使用了__block则不会进行copy操作,只是进行指针复制,不会retain引用的对象。
5)block对以参数形式传进来的对象,会不会强引用?
可以参考函数以及方法,block对于传进来的参数,并不会持有
6)block的实现
使用clang -rewrite-objc来编译可以得到,block最后是被转化成指向__main_block_impl_0结构体的指针,struct中包括isa(block类型)、FunPtr(函数实现指针),引用变量(若是__block修饰的话,会是一个__Block_byref_i_0结构体的指针)。当不使用__block修饰的话,引用变量也仅仅是传值,当使用__block修饰的话,会变成传指针。
详见http://www.jianshu.com/p/ca6ac0ae93ad

4、runtime以及JSpatch
1)为什么说c语言等叫函数调用,而OC中叫消息发送?
c语言函数调用是编译时就已经决定要调用哪个函数了,编译完成之后直接顺序执行,无任何二义性。OC中函数调用称为消息发送,属于动态调用过程。在编译的时候,并不能决定真正调用哪个函数(事实上,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。这是与c函数调用是不同的)。只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
2)消息发送过程
编译器将[obj make]转化成obj_msgSend(obj,@selector(make))。在obj_msgSend函数中,首先通过obj的isa指针找到obj所属的class,在class中先去cache中通过SEL查找对应函数实现指针,若是没有找到则在class的methodList中查找,若仍未找到,则去superClass中查找直到查找到最顶层的class,若是一直未找到则进行消息转发过程。若是查找到,则将其加入cache中以方便下次查找,并通过查找到的method的函数指针跳转到对应的函数中去执行。
3)消息转发过程
1.动态方法解析
向当前类发送resolveInstanceMethod:信号,检查是否动态向该类添加了方法(class_addMethod)。
2.快速消息转发
检查该类是否实现了forwardingTargetForSelector:方法,若是实现了则调用这个方法。若该方法返回非nil或非self,则向该返回对象重新发送消息。
3.标准消息转发
runtime发送methodSignatureForSelector:消息获取selector对应的方法签名。返回值非空,则通过forwardInvocation:转发消息;返回值为空则向当前对象发送doesNotRecognizeSelector:消息,程序crash。
4)快速消息转发与标准消息准发比较
快速消息转发:简单、快速,但仅能转发给一个对象
标准消息转发:较复杂、较慢、但操作实现可控,可以实现多对象转发

5、UIButton的继承关系
UIButton继承自UIControl->UIView->UIResponder,UIControl这个父类添加了target-action事件

6、UINavigationController用啥数据结构实现的?
这个是使用栈来实现的,因为navigationController下有许多ViewController,而且存在pushViewController和popViewController这两种方法

7、__bridge、__bridge__transfer、__bridge__retained
__bridge只做类型转换,但是不修改对象(内存)管理权;
__bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
__bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。

8、怎么让一个UIScrollView不左右滑动?上下滑动?
禁止UIScrollView垂直方向滚动,只允许水平方向滚动
scrollview.contentSize = CGSizeMake(你要的长度, 0);

禁止UIScrollView水平方向滚动,只允许垂直方向滚动
scrollview.contentSize = CGSizeMake(0, 你要的宽度);

9、svn版本控制命令
服务器拉取代码:svn checkout http:XXXX
新建一个test.c文件然后提交:svn add test.c加到本地库里面,然后svn ci -m "添加问价说明",ci是commit的缩写
查看修改情况:svn status
对比服务器与本地版本之间的区别:svn diff
退到原来的工作拷贝:svn revert,这样你的本地修改就会丢失
更新本地库:svn update
解决冲突:svn resolved XX 这个会在更新的同时并保留本地的修改,并将冲突文件标记为resolved,此后你可以直接在冲突文件中看到标记

10、svn和git的区别(了解不深)
1)git是分布式的,svn却不是;
2)git把内容按元数据方式存储,svn是按文件
3)git没有一个全局版本号
4)git下载下来后,在离线状态下可以看到所有的log
5)svn克隆一份全新的目录耗时长
6)svn只有一个版本库,一旦挂掉所有的工作都不可以进行
7)git的分支很强大。svn的分支是一个完整的目录,开辟一个新分支会影响所有人。
详情请见 http://www.jianshu.com/p/bfec042349ca

你可能感兴趣的:(面试问题记录)