在iOS行业如此饱和的今天,跳槽是需要勇气的(ps裸辞),如此就要做好充足的准备。
怎么大风越狠,我心越荡
保持信心,一路向前。
生于忧患死于安乐,这句话还是很有道理的。
废话不多说了,还是直接来点实际的,如下是我准备的一些问题及相应的个人理解(集各路英雄豪杰的总结),也是觉得面试官会问的技术点:
一. load
和initialize
的异同
load
类方法是类加载进内存的时候调用一次,分类中若实现了load
方法,也会被调用,且当前类的load方法会先调用,分类中的load
方法后调用,不支持子类重写。
initialize
是类在第一次使用时会调用一次,调用其子类时,父类的initialize
会被先调用,支持重写。
在这两个方法中都不建议做一些负载的操作,以防止程序运行或加载时出现阻塞的现象。
二.GCD
和NSOperationQueue
在实际项目中如何进行选择
首先NSOperationQueue
有如下的一些特新
- 取消某个操作,调用
cancel
方法,会设置对象内的标志位,用以表明下次任务不需执行。 - 可以指定操作间的依赖关系。
- 通过
KVO
监控NSOperation
对象的属性,可以判断任务是否取消及任务是否已完成。 - 指定操作的优先级。
- 重用
NSOperation
对象,实现自定义操作任务。
然而GCD
也有它的优势,主要内部是基于C语音的一套封装,调用的方法更底层,运行的速度也会更快。如果需要用到异步线程加载一些任务,且不需要用到NSOperation
的那些特性,首选还是用GCD
的。
GCD
也有自己的特性:
- 通过
Dispatch Group
机制,根据系统资源来执行任务。 - 运用
Dispatch Barrier
特性,保证写入数据的线程安全。 - 运用
Dispatch Once
来创建多线程下安全的单例。
两个的相同点都是操作队列,实现多线程任务管理。
三. UITableView
的优化(防止卡顿)
1.主线程是用来更新UI的,若来处理与UI更新无关的事情,就会出现卡顿。(值得注意)
2.将计算cell
高度的过程在获取数据时就直接计算保存好,代理方法需要的时候,直接从模型中获取。
3.图片的部分最好做缓存,避免读I/O操作。
4.网络加载下来的图片尺寸最好跟显示的frame
尺寸匹配,避免控件去压缩图片。
5.cell
的复用机制。
6.避免设置控件的圆角,防止离屏渲染。
7.避免设置控件的透明度alpha
。
8.对于UILabel
的显示,避免设置富文本。
9.UI的布局最好不要用Xib和SB,反正系统做一些不必要的操作,最好手动布局。
10.避免代理方法里面文字size
的计算。
四.weak
的内部实现
weak
表明属性是一种非拥有关系,为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特性与assign
类似,但是当所指的对象的引用计数为0时,会执行dealloc
操作,将weak
属性设置为nil
runtime
特质,系统会将所有weak
属性存在一个hash
表中,将weak
属性所指的对象的内存地址作为key
,当weak
所指向的对象被清空时,系统会通过清空对象的内存地址key
,从hash
表中获取weak
属性,将其赋值为nil
。
五.copy
关键字怎么用
1.copy
特质:父类指针可以指向子类对象,为了保持属性不受外界的影响,无论外面给我传的是可变的或不可变的对象,都会保持一份不可变数据的副本。
2.strong
特质:当不可变的对象指向可变的子类对象时,只是增加了当前对象的引用计数,可变的对象和不可变的对象指向同样的地址空间,当可变对象的值发生变化时,同时会影响不可变的对象。
copy
此特质所表达的所属关系与strong
类似。然而设置方法并不保留新值,而是将其“拷贝”,当属性的类型为NSString
时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向NSMutableString
类的实例,这个类是NSString
的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能在对象不知情的情况下被篡改。所以,这时就要拷贝一份不可变的字符串,确保对象中的字符串的值不被无意间改动。只要实现所用的对象是可变的,就应该在设置新属性值时拷贝一份。
六.事件的传递和响应
当一个事件发生后,事件会从父控件传给子控件,也就是由UIApplication
->UIWindow
-> UIView
-> initial view
,以上就是事件的传递,也就是寻找最合适的view的过程.
事件的响应,首先看initial view
能否处理这个事件,如果不能则会将事件给其上级的视图(initial view
的superView
);如果上级视图任然无法处理则会继续往上传递,一直传递到视图控制器view controller
,首先判断视图控制器的根视图view
是否能处理此事件,如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传递,(对于第二个视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理),一直到window
,如果window
还是不能处理此事件则继续交给UIApplication
去处理,如果最后UIApplication
还是不能处理此事件则将其丢弃.
区别:事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺泽响应链条向上传递),子控件到父控件.
七.动态库和静态库的区别
在实际的编程过程中,通常会把一些公用的函数制作成函数库,供其它程序使用,一是提高了代码的复用,二则提高了核心技术的保密程度.所以在实际的项目中经常会使用到函数库,即函数库又分为静态库和动态库.这里所谓的动态库和静态库是相对编译期和运行期的.
静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库.
动态库在程序编译期间不会被链接到目标代码中,只是在程序运行时才被载入,在程序运行期间还需要动态库的存在.
八.runtime的介绍
runtime
是一套底层C语言的API
,包含许多强大的C语言函数,OC的函数调用,底层都会调用runtime
函数.
作用:可以获取当前对象的所有成员变量,方法名;可以动态的给对象添加属性和方法.利用黑魔法(Method Swizzling
)交换两个方法的实现等.
objc_setAssociatedObject
给关联对象添加属性
objc_getAssociatedObject
获取关联对象的属性
九.什么时候会报unrecognized selector
的异常
objc
在向一个对象发送消息时,runtime
库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表及其父类的方法列表中寻找方法的运行,如果最顶层的方法中依然找不到相应的方法时,程序在运行时会挂掉同时抛出异常unrecognized selector sent to XXX
.但是在这之前,objc的运行时会给出三次拯救程序奔溃的的机会:
1.Method resolution
objc运行时会调用+resolveInstanceMethod:
或+resolveClassMethod:
,让你有机会提供一个函数实现.如果你添加了函数,运行系统会重新启动一次消息发送的过程.否则,运行时会移到下一步,消息转发(message Forwarding
)
2.Fast forwarding
如果目标对象实现了-forwardingTargetForSelector:
,runtime
这时就会调用这个方法,给你把消息转发到其它对象的机会.只要这个方法返回的不是nil和self,整个消息发送的过程就会重启,发送的对象就会变成你返回的那个对象.否则,就会继续Normal Forwarding
.
3.Normal Forwarding
这一步是runtime
最后一次给你挽救的机会,首先它会发送-methodSignatureForSelector:
消息获得函数的参数和返回值类型.如果-methodSignatureForSelector:
返回的是nil
,runtime
就会发出-doesNotRecognizeSelector:
消息,程序这时也就挂掉了.如果返回了一个函数签名,runtime
就是创建一个NSIvocation
对象并发送-forwardInvocation:
消息给目标对象.
十.UIView
和CALayer
的区别和联系
1.UIView
可以响应事件,CALayer
不可以
UIView
继承自UIResponder
,具有响应事件的能力.
2.UIView
主要是对显示内容的管理而CALayer
主要是侧重对显示的绘制.
3.在iOS
动画的时候,修改非RootLayer
的属性会默认产生隐式动画,而修改UIView
则不会.
目前先整理这些,貌似都是些概率的东西,后续待继续更新...
若有好的面试题目,待各位在评论中指出,表示会及时更新上去.自己也会在平时的开发中多积累一些...
整理多了,也是一份不错的笔记...
收获自己,分享大家...