面试题答案
一键面试对象创建过程中runtime的作用
-
类的加载
- 在程序启动时,runtime会进行类的加载。它会从可执行文件和动态链接库中读取类的相关信息,包括类的定义、属性、方法列表等。例如,
objc_class
结构体包含了类的基本信息,runtime通过解析这些数据结构来加载类。 - 类的加载过程还涉及到类的依赖关系处理,runtime会确保父类先被加载,然后再加载子类,以保证类的继承体系完整。比如,若有一个
SubClass
继承自SuperClass
,runtime会先加载SuperClass
,再加载SubClass
。
- 在程序启动时,runtime会进行类的加载。它会从可执行文件和动态链接库中读取类的相关信息,包括类的定义、属性、方法列表等。例如,
-
元类的作用
- 元类(meta - class)在runtime中是用于存储类方法的地方。每个类都有一个对应的元类。例如,对于一个普通类
MyClass
,MyClass
的实例方法存储在MyClass
类对象的methodLists
中,而MyClass
的类方法(如+ (void)classMethod
)则存储在MyClass
对应的元类的methodLists
中。 - 元类在消息发送机制中也起着关键作用。当向一个类发送类方法消息时,runtime首先会在该类对应的元类中查找方法实现。例如,当执行
[MyClass classMethod]
时,runtime会到MyClass
的元类中查找classMethod
的实现。
- 元类(meta - class)在runtime中是用于存储类方法的地方。每个类都有一个对应的元类。例如,对于一个普通类
-
对象创建的具体过程
- 当使用
alloc
方法创建对象时,runtime首先会根据类的信息计算对象所需的内存大小。这包括实例变量占用的空间以及一些额外的开销(如对象的引用计数等)。 - 然后,runtime会向系统申请一块足够大小的内存来存储对象。例如,对于一个包含几个
int
和NSString *
类型实例变量的类,runtime会计算出这些变量以及其他必要信息总共需要的内存字节数,并分配相应的内存。 - 接着,runtime会对分配的内存进行初始化,将对象的引用计数设置为1(在ARC环境下有不同的处理方式,但基本概念类似),并将实例变量初始化为默认值(如
int
类型初始化为0,指针类型初始化为nil
)。最后返回一个指向该对象内存地址的指针。
- 当使用
对象销毁时runtime的底层操作
- 引用计数处理
- 在MRC(手动引用计数)环境下,当对象的引用计数减为0时(通过
release
方法),runtime会开始销毁对象。在ARC(自动引用计数)环境下,编译器会在适当的位置插入引用计数操作代码。 - runtime会检查对象的引用计数是否为0。如果为0,它会释放对象占用的内存。例如,对于一个
NSString
对象,当引用计数变为0时,runtime会释放该NSString
对象所占用的堆内存。
- 在MRC(手动引用计数)环境下,当对象的引用计数减为0时(通过
- 调用析构函数
- 在释放对象内存之前,runtime会调用对象的析构函数(
dealloc
方法)。在dealloc
方法中,可以进行一些资源清理操作,如释放分配的文件句柄、关闭网络连接等。例如,如果一个对象在初始化时打开了一个数据库连接,在dealloc
方法中需要关闭这个数据库连接。 dealloc
方法会递归地调用父类的dealloc
方法,以确保整个继承体系中的资源都能被正确清理。例如,若SubClass
继承自SuperClass
,SubClass
的dealloc
方法在执行完自身的清理操作后,会调用[super dealloc]
来清理SuperClass
中的资源。
- 在释放对象内存之前,runtime会调用对象的析构函数(
利用runtime机制优化对象创建与销毁的性能
- 对象创建优化
- 缓存重用:可以利用runtime的缓存机制。例如,runtime有方法缓存,对于频繁调用的方法,会将其缓存起来。在对象创建时,可以考虑复用一些常用的对象结构或者缓存已创建的对象。比如,在一个频繁创建和销毁
NSString
对象的场景中,可以使用NSMutableString
并通过setString:
方法来复用对象,而不是每次都创建新的NSString
对象。 - 减少不必要的属性:通过runtime查看类的属性列表,减少对象中不必要的属性。属性越多,对象创建时计算内存大小和初始化的开销就越大。例如,如果一个类中有一些很少使用的属性,可以考虑将其移除或者采用懒加载的方式。
- 缓存重用:可以利用runtime的缓存机制。例如,runtime有方法缓存,对于频繁调用的方法,会将其缓存起来。在对象创建时,可以考虑复用一些常用的对象结构或者缓存已创建的对象。比如,在一个频繁创建和销毁
- 对象销毁优化
- 及时释放资源:在
dealloc
方法中,确保及时释放资源。避免在dealloc
方法中进行复杂的操作,因为dealloc
方法执行时,对象已经处于即将被销毁的状态,过多复杂操作可能影响性能。例如,如果对象持有一个大的图片数据,在dealloc
方法中应尽快释放该图片占用的内存。 - 优化引用计数管理:在MRC环境下,准确管理引用计数,避免过度释放或提前释放。在ARC环境下,虽然编译器自动管理引用计数,但了解其原理有助于优化代码结构。例如,合理使用
weak
和strong
等修饰符,避免循环引用导致对象无法及时销毁。如果存在循环引用,对象的引用计数永远不会变为0,从而无法被销毁,造成内存泄漏。通过使用weak
修饰符打破循环引用,可以使对象在不再被强引用时及时被销毁。
- 及时释放资源:在