引言
之所以要整理这份面试题,也是希望能对更多的知识进行巩固,希望大家通过看面试题来加深自己对于iOS知识的理解。
面试题:
1. 什么是arc?(arc是为了解决什么问题诞生的?)
首先解释ARC: automatic reference counting - 自动引用计数
。
ARC几个要点:
- 在对象被创建时 retain count +1,在对象被release时 retain count -1.当retain count 为0 时,销毁对象。
- Xcode编译器会在程序中,合适的位置,自动加上autorelease方法,如果该对象引用计数为0,则销毁。
那么ARC是为了解决什么问题诞生的呢?
这个得追溯到MRC手动内存管理时代说起。
MRC下内存管理的缺点:
- 当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
- 释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
- 模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
- 多线程操作时,不确定哪个线程最后使用完毕
为了解决上面MRC这些问题,所以引进的ARC。
2. +(void)load; +(void)initialize;有什么用处?
答案:
在Objective-C中,runtime会自动调用
每个类的两个方法。
- +load会在类初始加载时调用
- initialize会在第一次调用类的类方法或实例方法之前被调用
- 这两个方法是
可选的
,且只有在实现了它们时才会被调用
。 共同点
:两个方法都只会被调用一次。
3. 为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)
答案:
先来看看怎么理解发送消息的含义:
很多人在使用Objective-C 时把[receiver message]这样的形式,当成简单的方法调用,也就是receiver 调用了message方法,但是,实际上[receiver message]会被编译器转化为:
|
|
如果消息含有参数,则为:
|
|
如果消息的接收者能够找到对应的selector
,那么就相当于直接执行
了接收者这个对象的特定方法;
否则,消息要么被转发
,
或是临时向接收者动态添加
这个selector对应的实现内容,
要么就干脆玩完崩溃
掉。
现在可以看出[receiver message]真的不是一个简简单单的方法调用。因为这只是在编译阶段确定了要向接收者发送message这条消息
,而receive将要如何响应这条消息,那就要看运行时发生的情况来决定了。
总结:Objc Runtime使得C具有了面向对象能力,在程序运行时创建,检查,修改类、对象和它们的方法。可以使用runtime的一系列方法实现。
4. CALayer 和 UIView的区别和联系
答案:
首先UIView可以响应事件,Layer不可以.
UIKit使用UIResponder作为响应对象,来响应系统传递过来的事件并进行处理。
UIApplication、UIViewController、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。
View和CALayer的Frame映射及View如何创建CALayer.
一个 Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同决定的,而一个 View 的 frame 只是简单的返回 Layer的 frame,同样 View 的 center和 bounds 也是返回 Layer 的一些属性。在 [view initWithFrame] 的时候调用私有方法【UIView _createLayerWithFrame】去创建 CALayer。
然后我在创建 View 的时候,在 Layer 和 View 中Frame 相关的所有方法中都加上断点,可以看到大致如下的调用顺序如下123456[UIView _createLayerWithFrame][Layer setBounds:bounds][UIView setFrame:Frame][Layer setFrame:frame][Layer setPosition:position][Layer setBounds:bounds]我发现在创建的过程只有调用了 Layer 的设置尺寸和位置的然而并没有调用View 的 SetCenter 和 SetBounds 方法。
然后我发现当我修改了 view的 bounds.size 或者 bounds.origin 的时候也只会调用上边 Layer的一些方法。
所以我认为,View 的 Center 和 Bounds 只是直接返回layer 对应的 Position 和 Bounds.
UIView主要是对显示内容的管理而 CALayer 主要侧重显示内容的绘制。
在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。
对于每一个 UIView 都有一个 layer,把这个 layer 且称作RootLayer,而不是 View 的根 Layer的叫做 非 RootLayer。
我们对UIView的属性修改时时不会产生默认动画,而对单独 layer属性直接修改会,这个默认动画的时间缺省值是0.25s.
在 Core Animation 编程指南的 “How to Animate Layer-Backed Views” 中,对为什么会这样做出了一个解释:
1UIView 默认情况下禁止了 layer 动画,但是在 animation block 中又重新启用了它们是因为任何可动画的 layer 属性改变时,layer 都会寻找并运行合适的 ‘action’ 来实行这个改变。在 Core Animation 的专业术语中就把这样的动画统称为动作 (action,或者 CAAction)。
layer 通过向它的 delegate 发送 actionForLayer:forKey: 消息来询问提供一个对应属性变化的 action。
delegate 可以通过返回以下三者之一来进行响应:
- 它可以返回一个动作对象,这种情况下 layer 将使用这个动作。
- 它可以返回一个 nil, 这样 layer 就会到其他地方继续寻找。
- 它可以返回一个 NSNull 对象,告诉 layer 这里不需要执行一个动作,搜索也会就此停止。
当 layer 在背后支持一个 view 的时候,view 就是它的 delegate;
上面这些属于研究型的答案,有一部分分析内容,如果面试的时候,可以参考下面的答案来回答:
UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由CoreAnimation来实现的。
它真正的绘图部分,是由一个CALayer类来管理。
UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性。
UIView有个重要属性layer,可以返回它的主CALayer实例。
12// 要访问层,读取UIView实例的layer属性CALayer *layer = myView.layer所有从UIView继承来的对象都继承了这个属性。
这意味着你可以转换、缩放、旋转,甚至可以在Navigation bars,Tables,Text boxes等其它的View类上增加动画。
每个UIView都有一个层,控制着各自的内容最终被显示在屏幕上的方式。
UIView的layerClass方法,可以返回主layer所使用的类,UIView的子类可以通过重载这个方法,来让UIView使用不同的CALayer来显示。代码示例:
123- (class)layerClass {return ([CAEAGLLayer class]);}上述代码使得某个UIView的子类使用GL来进行绘制。
UIView的CALayer类似UIView的子View树形结构,也可以向它的layer上添加子layer,来完成某些特殊的表示。
即CALayer层是可以嵌套的。示例代码:
123grayCover = [[CALayer alloc] init];grayCover.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];[self.layer addSubLayer:grayCover];上述代码会在目标View上敷上一层黑色透明薄膜的效果。
UIView的layer树形在系统内部,被维护着三份copy。
- 分别是逻辑树,这里是代码可以操纵的;
- 动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;
- 显示树,其内容就是当前正被显示在屏幕上得内容。
动画的运作:对UIView的subLayer(非主Layer)属性进行更改,系统将自动进行动画生成,动画持续时间的缺省值是0.25秒。
坐标系统:CALayer的坐标系统比UIView多了一个anchorPoint属性,使用CGPoint结构表示,值域是0~1,是个比例值。
这个点是各种图形变换的坐标原点,同时会更改layer的position的位置,它的缺省值是{0.5,0.5},即在layer的中央。
layer可以设置
圆角显示
(cornerRadius),也可以设置阴影
(shadowColor)。
但是如果layer树中某个layer设置了圆角,树种所有layer的阴影效果都将不显示了。因此若是要有圆角又要阴影,变通方法只能做两个重叠的UIView,一个的layer显示圆角,一个layer显示阴影……
渲染:当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以调用setNeedsDisplay方法来重绘显示。
1[gameLayer setNeedsDisplay];若要重绘部分屏幕区域,请使用
setNeedsDisplayInRect:
方法,通过在CGRect结构的区域更新:1[gameLayer setNeedsDisplayInRect:CGRectMake(150.0,100.0,50.0,75.0)];如果是用的Core Graphics框架来执行渲染的话,可以直接渲染Core Graphics的内容。用renderInContext:来做这个事。
1[gameLayer renderInContext:UIGraphicsGetCurrentContext()];变换:要在一个层中添加一个3D或仿射变换,可以分别设置层的transform或affineTransform属性。
12345characterView.layer.transform = CATransform3DMakeScale(-1.0,-1.0,1.0);CGAffineTransform transform = CGAffineTransformMakeRotation(45.0);backgroundView.layer.affineTransform = transform;
5. 如何高性能的给UIImageView加个圆角?
答案:
一般情况下给 UIImageView 或者说 UIKit 的控件添加圆角都是改变 clipsToBounds 和 layer.cornerRadius,
这样大约两行代码就可以解决这个问题. 但是, 这样使用这样的方法会强制 Core Animation 提前渲染屏幕的离屏绘制, 而离屏绘制就会为性能带来负面影响.
从APP性能优化角度考虑,我们也可以使用另一种比较复杂的方式来为图片添加圆角, 就是通过画布的方式,进行裁剪,
使用Quartz2D直接绘制图片
步骤:
a、创建目标大小(cropWidth,cropHeight)的画布。
b、使用UIImage的drawInRect方法进行绘制的时候,指定rect为(-x,-y,width,height)。
c、从画布中得到裁剪后的图像。
|
|
这里再介绍一种用贝塞尔曲线的方式.
|
|
这两种都方式都可以达到我们的需求。
6.layoutSubviews 和 drawRect的调用
答案:
layoutSubviews在以下情况下会被调用:
- init初始化不会触发layoutSubviews。
- addSubview会触发layoutSubviews。
- 设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
- 滚动一个UIScrollView会触发layoutSubviews。
- 旋转Screen会触发父UIView上的layoutSubviews事件。
- 改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。
直接调用setLayoutSubviews。
drawRect在以下情况下会被调用:
如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 掉用是在Controller->loadView,?Controller->viewDidLoad?两方法之后掉用的.所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View?draw的时候需要用到某些变量值).
- 该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
- 通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
- 直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。
以上1,2推荐;而3,4不提倡
drawRect方法使用注意点:
- 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay?或 者?setNeedsDisplayInRect,让系统自动调该方法。
- 若使用CALayer绘图,只能在drawInContext:?中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
- 若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来调用setNeedsDisplay实时刷新屏幕
7. post和get的区别?
答案:
- get是从服务器上获取数据,post是向服务器传送数据。
- get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
- 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
- get安全性非常低,post安全性较高。但是执行效率却比Post方法好。
本作品采用 署名-非商业性使用-相同方式共享 2.5 中国大陆 (CC BY-NC-SA 2.5)协议
进行许可,欢迎转载,但转载请注明来自SarielTang
,并保持转载后文章内容的完整。本人保留所有版权相关权利。
本文永久链接:http://sarieltang.github.io/2016/10/26/面试题总结/面试题1/index/