面试的坑,不同的基础就会有不同的坑,不多说了直接上面试题:(面试题与答案会持续更新)
1、简述关键字assign、weak、strong、retain、copy、readonly、readwrite、nonatomic、atomic的作用?
答:assign用于基本数据类型,如:NSInteger、double、bool等。weak、retain、strong和copy都用于对象。
weak表示的是弱引用,是ARC引入的对象变量的属性,相当于assign,只是当指向的对象被释放的时候会把当前的指针指向nil。
retain、strong、copy都表示持有该对象,strong的功能等同于retain,copy则表示会复制并指向新的对象。
readonly、readwrite是属性读写权限,readonly系统只允许使用属性的getter方法,readwrite则既可以访问setter方法也可以访问getter方法。
atomic、nonatomic是原子和非原子性访问属性。在多线程中访问属性时,atomic能保证属性的存取安全,nonatomic则不会。
2、weak 和 assign 的区别?
答:weak功能相当于assign都表示弱引用。但是使用weak关键字时,当指向的对象被释放的时候会把当前的指针指向nil。assign则不会,它会造成野指针的问题。
3、weak 的实现原理?
答:Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash表,Key是所指对象的地址,Value是weak指针的地址数组。a. 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。b. 添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数,objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用列表。c. 释放时:调用 clearDeallocating 函数。clearDeallocating 函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后移除这个弱引用的散列表。
4、atomic是不是一定就是线程安全?为什么?如何保证其线程安全?
第一问答:不是完全的线程安全,只是属性的存取方法是线程安全的。
第二问答:atomic在属性的setter和getter方法中加入了自旋锁,但这个锁只能保证setter/getter存取的安全,不能保证数据结果的正确性;举个例子:线程A、B都调用了属性的setter方法,线程C调用属性的getter方法,每个线程都保证了各自数据的完整性,那么D线程最后getter到的属性值就不确定了。
第三问答:需要加入互斥锁来解决线程安全。
5、Classroom这个类有什么问题?
@interface Classroom : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) NSMutableArray *students;
@end
答:可变数组使用了copy属性修饰,一旦通过set方法设置了该属性值后,就不能直接使用这个属性来调用NSMutableArray的方法了,否则会造成崩溃。
6、图示MVC、MVVM?
7、简述MVVM模式中VM的作用?
答:VM主要是关联Model和View,VM负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model中。VM旨在剥离原本在MVC模式Controller中的数据解析和视图展示逻辑。
8、简述事件传递和响应链?
答:事件传递:当用户触摸屏幕时,就会生成一个事件,这个事件会被加入到UIApplication管理的事件队列里,接下来开始自UIApplication往下传递,首先会传递给Window,然后按照View的层级结构一层层往下传递,一直找到最合适的view来处理这个事件。查找最合适的View是一个递归过程,其中主要涉及到:hitTest:withEvent: 和 pointInside:withEvents: 这两个方法。事件响应:当找到最合适处理该事件的View时,会调用View的touchs方法对事件进行响应,如果没有重写touchs方法的话,touchs默认会把事件沿着响应者链往上传递,一直到UIApplication,如果依旧不能处理事件,则这个事件将会被丢弃不做响应。响应者链是由一系列继承自UIResponder的对象组成的,它决定了响应者对象响应事件的先后顺序。
9、如何将一个控件的触摸事件传递给另一个控件处理?
答:重写hitTest方法,返回处理事件的View;拦截touch方法,防止触摸事件继续传递。
10、开发中Runtime一般用来干啥?
答:动态添加类、添加方法、关联属性等。
11、替换系统的方法一般需要做什么处理?
答:使用dispatch_once来确保替换方法只执行一次;替换时最好先为该类添加新方法,通过返回的Bool值来继续替换方法操作;替换完成后需要在新方法中调用一次替换后的方法。
12、为什么说OC是一门动态语言?
答:简单来说,就是把一些工作从编译时推迟到运行时;比如代码运行时,才能知道对象是什么类型,以及它能响应哪些方法。主要表现在:动态类型、动态绑定、动态加载,三个方面。
13、什么时候会报unrecognized selector的异常?
答:向对象发送一条消息时,如果经历消息发送、动态方法解析、消息转发,都没有得到响应的话,就会报这个异常。
14、消息转发的步骤?
答:三个步骤,resInstanceMethod、forwardingTargetForSel、forwardInvocation
15、如何在子线程开启一个runloop?
答:在子线程中调用[[NSRunLoop currentRunLoop] run];即可。
16、如何对tableView进行性能优化?
答:数据结构上,计算数据占用高度等,可放在请求到数据时进行计算。如果视图过于复杂的话,使用异步绘制。来保证帧率在60左右 。
17、如何监测APP的性能?
答:使用Xcode自带Profile中的一些工具进行查看,比如说使用Time方法调用所占时间,用Leaks查看内存相关问题等。
18、如何缩小 ipa 包?
答:1. 资源文件优化(无损压缩<图片、音频、视频>,删除无用资源)。2. 可执行文件优化(删除无用代码,删除静态库中的无用架构,关闭编译器中的异常调试处理)
19、如何在不用工具也不用第三方框架的情况下,对项目进行一个内存泄露的监测?
答:1. 静态方法检测,使用Xcode自带的Analyze工具;2. 使用动态方法检测,instructment工具实时检测;3. 自己写代码检测。
20、如何自定义一个tableView?
答:主要使用重用机制
21、现需要对5个ip,每个ip ping5次然后计算平均值,如何设计?
答:通过GCD开启5个线程执行 ip ping 操作,然后通过 GCD 的调度组,进行处理平均值的计算。
22、GCD、NSOperationQueue的异同?
答:GCD和NSOperation都是多线程开发,NSOperation是对GCD的封装,底层是GCD实现的。
- GCD的核心是C语言实现的,执行和操作简单高效;NSOperation是对GCD的封装的,NSOperation会多一些开销。
- NSOperation可以设置依赖关系,GCD无法直接设置依赖关系,可以通过调度组、或栅栏函数来实现。
- NSOperation可以使用KVO观察当前状态,GCD是无法使用KVO观察的。
- NSOperation可以设置自身的优先级,GCD只能设置队列优先级。
- NSOperation是个抽象类,使用时需要继承子类再使用;GCD执行任务可自由组装,自由度较强。
23、main函数执行前都有什么操作?
答:1. 动态链接库;2. ImageLoader加载可执行文件<编译过的符号表、代码等>;3. Runtime与+load方法调用
24、+load 和 +initialize 的联系和区别?
答:1. +load 和 +initialize 都由系统调用,都不需要调用super方法。2. +load 和 +initialize 都先调用类中的,再调用类别中的。3. +load 方法是在main函数之前调用的,+initialize是第一次初始化这个类之前被调用的。4. 如果子类没有 +initialize 方法也会调用父类的方法,而 +load 方法则不会调用父类的方法。5. +load 和 +initialize 方法内部使用了锁,实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。
25、看如下代码打印有什么区别?
- (void)test {
NSLog(@"%@", [super class]);
NSLog(@"%@", [self class]);
}
答:打印的结果都是一样的,因为super只是编译器表示符,他的代表的结构体是struct objc_super
,这个结构体中含有receiver
、super_class
两个数据,当使用super去发送消息时,只是优先从父类开始找方法;而使用self发送消息时,优先从本类开始找。本题中两个打印代码,消息发送者其实都是self,只是优先找方法的位置不一样而已!
注意:还可以延伸下,使用super、self分别去调用class、superclass消息的结果!!!