iOS常见面试题(持续更新中)
1、UIView和CALayer的区别和联系?
- UIView 继承 UIResponder,而 UIResponder 是响应者对象,可以对iOS 中的事件响应及传递,CALayer 没有继承自 UIResponder,所以 CALayer 不具备响应处理事件的能力。CALayer 是 QuartzCore 中的类,是一个比较底层的用来绘制内容的类,用来绘制UI
- UIView 对 CALayer 封装属性,对 UIView 设置 frame、center、bounds 等位置信息时,其实都是UIView 对 CALayer 进一层封装,使得我们可以很方便地设置控件的位置;例如圆角、阴影等属性, UIView 就没有进一步封装,所以我们还是需要去设置 Layer 的属性来实现功能。
- UIView 是 CALayer 的代理,UIView 持有一个 CALayer 的属性,并且是该属性的代理,用来提供一些 CALayer 行的数据,例如动画和绘制。
2、UIResponder的理解
UIResponder类是专门用来响应用户的操作处理各种事件的,包括触摸事件(Touch Events)、运动事件(Motion Events)、远程控制事件(Remote Control Events)。我们知道UIApplication、UIView、UIViewController这几个类是直接继承自UIResponder,所以这些类都可以响应事件。当然我们自定义的继承自UIView的View以及自定义的继承自UIViewController的控制器都可以响应事件。
3、使用 drawRect有什么影响?
drawRect 方法依赖 Core Graphics 框架来进行自定义的绘制
缺点:它处理 touch 事件时每次按钮被点击后,都会用 setNeddsDisplay 进行强制重绘;而且不止一次,每次单点事件触发两次执行。这样的话从性能的角度来说,对 CPU 和内存来说都是欠佳的。特别是如果在我们的界面上有多个这样的UIButton 实例,那就会很糟糕了。这个方法的调用机制也是非常特别. 当你调用 setNeedsDisplay 方法时, UIKit 将会把当前图层标记为 dirty,但还是会显示原来的内容,直到下一次的视图渲染周期,才会将标记为 dirty 的图层重新建立 Core Graphics 上下文,然后将内存中的数据恢复出来, 再使用 CGContextRef 进行绘制
4、Bounds和Frame的区别?
- Frame改变的是自身大小,以父视图的坐标系为参照,从而确定当前视图在父视图中的位置
- Frame的大小改变时,当前视图的左上角位置不会发生改变,只是大小发生改变
- Bounds改变位置时,改变的是子视图的位置,自身没有影响;其实就是改变了本身的坐标系原点,默认本身坐标系的原点是左上角(0,0)。比如origins改为(0,100),那么左上角坐标变成了(0,100), 子视图的坐标就要相对于(0,100)布局。
- Bounds的大小改变时,当前视图的中心点不会发生改变,当前视图的大小发生改变,看起来效果就想缩放一样
5、KeyWindown和delegate中的windown的区别
- delegate.window 程序启动时设置的window对象。
- keyWindow 这个属性保存了[windows]数组中的[UIWindow]对象,该对象最近被发送了[makeKeyAndVisible]消息
一般情况下 delegate.window 和 keyWindow 是同一个对象,但不能保证keyWindow就是delegate.window,因为keyWindow会因为makeKeyAndVisible而变化,例如,程序中添加了一个悬浮窗口,这个时候keywindow就会变化。
6、block和delegate的区别
- delegate运行成本低,block的运行成本高
block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。
- delegate更适用于多个回调方法(3个以上),block则适用于1,2个回调时。
7、为什么Block用copy关键字
Block在没有使用外部变量时,内存存在全局区,然而,当Block在使用外部变量的时候,内存是存在于栈区,当Block copy之后,是存在堆区的。存在于栈区的特点是对象随时有可能被销毁,一旦销毁在调用的时候,就会造成系统的崩溃。所以Block要用copy关键字。
8、KVO的实现原理
KVO 是(Key-valueObserve) Objective-C 对观察者模式(Observer Pattern)的实现。也是 Cocoa Binding 的基础。当被观察对象的某个属性发生更改时,观察者对象会获得通知。
1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
- 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
- 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数,这个函数内部调用的方法如下
1.willChangeValueForKey:
2.父类原来的setter
3.didChangeValueForKey:
4.didChangeValueForKey内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
2.如何手动触发KVO?
- 手动调用willChangeValueForKey:和didChangeValueForKey:
3.直接修改成员变量会触发KVO么?
- 不会触发KVO 因为其内部是重写set方法来达到监听的
9、什么情况使用weak关键字,相比assign有什么不同?
1.什么情况下使用weak?
在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性
自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。
2.不同点
weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
assign 可以用非 OC 对象,而 weak 必须用于 OC 对象
10、MVC、MVVM、MVP的优缺点
1.MVC:Model-View-Controller
MVC是通过controller的控制去操作model层的数据,并且返回给view层展示。
MVC工作原理:用户触发事件 -- view层发送指令到controller层 -- controller通知model层更新数据 -- 数据更新完显示在view层上
优缺点
优点:
通过controller控制全局,同时将view和model的变化分开,对复杂混乱的项目结构,有了明确的组织方式
缺点:
1.当业务逻辑增加时,大量的逻辑代码放进了controller,导致controller越发臃肿,后期维护成本随之提升
2.没有区分业务逻辑和业务展示,对单元测试不友好
2.MVVM:Model-View-ViewModel
MVVM相对MVP来说将presenter层换成了viewmodel层,并且view层和view model层是相互绑定的关系,也就是当viewmodel层更新时,view层会相应的变动ui。
优缺点
优点:
1.view可以独立于model进行变化和修改,一个viewmodel可以绑定在不同的view上,降低耦合,增加复用。2.不仅解决model和view耦合问题,同时解决了维护两者映射关系的大量繁杂代码和DOM操作代码,提高开发效率、可读性,并保持优越的性能表现
缺点:
1.不适用于简单的项目2.大型的项目视图状态较多时构建和维护成本大
3.MVP:Model-View-Presenter
MVP是MVC的演化,MVC中的model和view两层存在耦合,之间相互依赖过多,由于view可以和model直接通信,造成了view既依赖于controller又依赖于model的局面,controller同样依赖于view和model,耦合性过高。在这种情况下MVP应运而生。
可以看出与MVC不同的是,view层和model层不再相互可知,完全的耦合,presenter充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并将数据返回给view层。
优点:
1.model和view分离,可以做到修改view而不影响model,解决了两者耦合问题2.更加高效的使用model,view不依赖model,view能做到对业务的完全分离
缺点:
presenter中除了处理业务逻辑,还需处理view-model两层的协议,同样会导致presenter层的代码臃肿