iOS面试题-OC
1. 如何手动触发一个value的KVO
NSObject 有 willChangeValueForKey:
和 didChangevlueForKey:
两个方法,会在 KVO 的时候调用,我们可以手动重写这两个方法。
2. @protocol 和 category 中使用 @property
- 在协议中添加属性,只会生成accessor,并没有后为遵循协议的对象添加属性。
- 分类中也是这样
- 如果真的需要添加属性,可以用 runtime 方法:
objc_setAssociatedObject
和objc_getAssociatedObject
。
3. Runtime
在类中向使用 runtime ,需要先导入<objc/message.h>。
消息机制
objc_msgSend(objc_getClass(“Person”), sel_registerName(“doSth”));
// 或者第一个参数是一个对象:
objc_msgSend(obj, sel_registerName(“doSth”));
// 也可以调用带参数的方法:
objc_msgSend(obj, sel_registerName(@selector(eat:),”food”));
// 注意这里参数和方法名之间是逗号关系
交换方法(method swizzing)
我们在类的 load 方法中进行方法的交换:
+ (void)load
{
// 获得两个将要交换的方法
Method oldMethod = class_getClassMethod(self, @selector(oldMethod:));
Method newMethod = class_getClassMethod(self, @selector(newMethod:));
// 交换:
method_exchangeImplamentations(oldMethod, newMethod);
}
动态添加方法
会员机制、收费机制、部分受限制功能,这些都需要动态添加方法来实现。
只要对象调用了一个未实现的方法,就会调用 resolveInstanceMethod:
方法,所以我们可以在这个方法中进行添加:
class_addMethod(类,sel,方法名,类型);
动态添加属性
扩展属性只会生成 accessor 方法,不能在类中访问添加的成员变量,所以我们可以使用 runtime 添加属性:
objc_setAssociatedObject(对象, 属性名称, 属性值, 枚举策略);
访问这个动态添加的属性:
objc_getAssociatedObject(对象, 属性名);
为类添加属性的过程,实际上就是把属性和类关联起来,达到同生共死的目的。
消息转发
_objc_msgForward
4. 方法调用流程
- 通过 isa 指针找到类对象或元类的方法列表
- 注册方法编号
- 根据方法编号找到对相应的方法的地址(SEL)
- 在代码段中找到方法的具体实现(IMP)
5. 引用和指针的区别
- 引用是指针的一种别称
- 引用不能为空,指针可以
- 引用初始化后不可改变,指针可以
6. 标准宏
#define Min(a,b) (((a)>=(b))?(b):(a))
由于是原样替换,所以要加上括号
7. 自定义对象实现copy
- 使自定义对象遵循 NSCopy 协议
- 重写父类的 copyWithZone 方法
- 创建一个新对象
- 设置当前对象的值给新对象
- 返回新的对象
- (id) copyWithZone:(NSZone *)zone
{
Person *p = [[[self class] allocWithZone] init];
p.age = _age;
p.name= _name;
return p;
}
8. @class 和 #import 以及 @import
- import 确保该头文件只被导入一次,无论在该文件中出现几次。
- @class 告诉编译器,无需知道类的具体实现,只需要知道是个类就好了。这样可以避免类之间的依赖关系导致的重复编译。
OC 中的对象都是动态分配内存,所以在编译时,编译器不需要了解这个对象的具体的内容,只需要获取指向这个对象的指针就可以了。这也回答了为什么 @class 关键字只会出现在 @interface 而不能出现在 @implementation 中。 - @import 可以让你只引用一个库里的某一个类。
9. +load 和 +initialize 的区别是什么?
-
load
只要类所在文件被引用就会被调用 -
initialize
是在类或者其子类的第一个方法被调用前调用
所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么 initialize
也不会被调用。
10. 各种修饰符的特点以及使用场合
-
retain
与strong
:retain
是 MRC 下的strong
-
assign
与weak
:assign
是MRC下的weak
。weak
必须作用于 oc 对象,而assign 不必。
weak 在作用对象销毁后直接将指针置为 nil。而assign
会让属性的setter
方法直接传递值,不考虑内存管理,所以通常用于基本数据类型。 - `copy:对不可变对象使用时,可以防止外界的对实例变量的误改。
11. new、alloc/init 的区别
new
相当于一种快捷省事版的 alloc/init
,其本质都是开辟内存空间并且初始化对象。但 alloc/init
在内部会使用一个 zone
机制,将相关的对象分配在相邻的内存中,以便检索对象时减少损耗。并且 new
无法在创建对象时给定参数。
12. 深复制与浅复制
-
[immutableObject copy]
浅复制 -
[immutableObject mutableCopy]
深复制 -
[mutableObject copy]
深复制 -
[mutableObject mutableCopy]
深复制
13. runtime 中,SEL 和 IMP 的区别
SEL只是方法编号,IMP(implementation) 是方法具体的地址。
14. 为什么 Extension 可以增加实例变量, 而 Category 不可以?
- Extension: 在编译期进行,与类同生共死。
- Category: 在运行期进行。
Category 在运行期间进行,但类在内存中的布局已经成型,添加实例变量会危险的修改类的结构。
15. __block
和__weak
修饰符的区别
-
__block
不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。 -
__weak
只能在ARC模式下使用,也只能修饰对象,不能修饰基本数据类型(int)。 -
__block
对象可以在block中被重新赋值,而__weak
不可以。
16. isMemberOfClass 和 isKindOfClass 的异同
两者都能检测一个对象是否是某个类的成员,但后者还可以检测一个对象是否为某个类的后代,而前者却不行。
17. 谈谈instancetype和id的异同
-
相同点
都可以作为方法的返回类型 -
不同点
①instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
②instancetype只能作为返回值,不能像id那样作为参数
18. atomic和nonatomic的区别,默认是哪个?
- atomic 是默认的,原子属性,防止多线程下的不安全
- nonatomic 是可以添加的,如果是不用考虑多线程安全的话,可以用这个属性防止编译器生成过多的代码。
19. 向一个nil对象发送消息将会发生什么?
什么都不会发生。
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
20. 如何访问并修改一个类的私有属性?
- 通过 KVC 可以访问一个类的私有属性
- 通过 runtime 可以修改
21. accessibility 访问限制修饰符
- @private:当前类
- @protected:当前类以及子类
- @package:不同包时是 private,同包使用时是public
- @public:当前类以及其他类
22. 如何序列化自定义对象?
实现 NSCoding 协议。
23. 如何调试BAD_ACCESS错误?
Xcode 7 已经集成了BAD_ACCESS捕获功能:Address Sanitizer
24. 数据持久化方式以及使用场景
- plist 文件 键值对存储,有嵌套关系。比如城市名称
- preference(UserDefaults)轻量数据。比如偏好设置
- SQLite 重量数据。比如视频
- CoreData sqlite的封装。
- NSKeyedAchiver 序列化自定义对象
25. 应用程序沙盒的目录结构
沙盒机制使应用之间不能互相访问数据
屏幕快照 2017-01-29 下午4.51.52.pngDocuments
将应用程序的数据文件保存在该目录下.不过这些数据类型仅限于不可以再生的数据,可再生的数据文件应该存在LIbrary/Cache下
Library 它有两个子文件
- caches 主要是缓存文件,用户使用过程中缓存都可以保存在这些目录中.保存那些可再生的文件,比如网络数据请求.因此,应用程序通常还需要负责删除这些文件.
- Preferences 应用程序的偏好设置文件. 我们使用NSUserDefaults写的设置数据都会保存到该目录下的一个plist文件中 它会被iTunes同步
tmp
各种临时文件, 保存应用再次启动时不需要的文件.而且,当应用不在需要这些文件时应主动将其删除,因为该目录下的东西随时有可能被系统清除,目前已知的一种可能清理的原因是系统磁盘存储空间不足的时候