面试题答案
一键面试Objective - C关联对象底层实现原理
- 存储结构
- 在Objective - C中,关联对象是通过
objc_object
结构体中的一个isa
指针指向类的元数据,类的元数据中包含了关联对象的信息。关联对象实际上是存储在一个全局的哈希表(AssociationsManager
管理的AssociationsHashMap
)中。 - 这个哈希表的键是对象的内存地址(也就是对象的
isa
指针指向的地址),值是另一个哈希表(ObjectAssociationMap
),在ObjectAssociationMap
中,键是关联对象的key
(通常是一个NSString
或void*
类型),值是关联对象的Association
结构体,Association
结构体中包含了关联对象以及关联策略等信息。
- 在Objective - C中,关联对象是通过
- 关联策略
- 关联对象有几种关联策略,如
OBJC_ASSOCIATION_ASSIGN
(弱引用)、OBJC_ASSOCIATION_RETAIN_NONATOMIC
(非原子性强引用)、OBJC_ASSOCIATION_COPY_NONATOMIC
(非原子性拷贝)、OBJC_ASSOCIATION_RETAIN
(原子性强引用)、OBJC_ASSOCIATION_COPY
(原子性拷贝)。这些策略决定了关联对象的生命周期管理。例如,OBJC_ASSOCIATION_ASSIGN
类似属性的assign
,不会增加对象的引用计数,适用于避免循环引用的场景;而OBJC_ASSOCIATION_RETAIN
会增加对象的引用计数,确保关联对象在主对象存在期间不会被释放。
- 关联对象有几种关联策略,如
- runtime操作
- 当使用
objc_setAssociatedObject
函数设置关联对象时,runtime首先会获取当前对象的关联对象哈希表(如果不存在则创建)。然后根据传入的key
和value
以及关联策略,在哈希表中插入或更新相应的关联对象。 - 当使用
objc_getAssociatedObject
函数获取关联对象时,runtime会根据对象的内存地址找到对应的关联对象哈希表,再根据传入的key
从哈希表中查找并返回关联对象。 - 当对象被释放时,runtime会遍历其关联对象哈希表,根据关联策略释放相应的关联对象。例如,如果是强引用策略,会释放关联对象;如果是弱引用策略,不会对关联对象做任何操作。
- 当使用
大型项目中关联对象使用的优化
- 减少不必要的关联对象
- 在大型项目中,仔细评估是否真的需要使用关联对象。如果可以通过其他方式(如继承、组合等)来实现相同的功能,优先选择这些方式。因为关联对象的存储和管理会增加内存开销和runtime操作的成本。例如,对于一些简单的属性添加需求,可以通过子类化来添加属性,而不是使用关联对象。
- 合理选择关联策略
- 根据业务场景选择合适的关联策略。如果存在循环引用的风险,优先选择
OBJC_ASSOCIATION_ASSIGN
策略。例如,在视图控制器和其关联的视图之间,如果视图控制器持有视图的强引用,视图又通过关联对象持有视图控制器,就可能产生循环引用,此时视图对视图控制器的关联可以使用OBJC_ASSOCIATION_ASSIGN
。如果需要确保关联对象在主对象存在期间一直存在,选择OBJC_ASSOCIATION_RETAIN
或OBJC_ASSOCIATION_COPY
策略,但要注意避免过度引用导致内存泄漏。
- 根据业务场景选择合适的关联策略。如果存在循环引用的风险,优先选择
- 集中管理关联对象
- 可以创建一个工具类或模块来集中管理关联对象的设置和获取。这样可以减少代码中分散的关联对象操作,提高代码的可维护性。同时,在这个集中管理模块中,可以添加一些日志记录或性能监控代码,便于排查问题和优化性能。例如,记录每次设置和获取关联对象的时间,找出性能瓶颈。
- 延迟设置关联对象
- 如果关联对象的创建和设置成本较高,可以考虑延迟设置。只有在真正需要使用关联对象时才进行设置,而不是在对象初始化时就设置好。这样可以避免在程序启动或对象初始化时不必要的性能开销。例如,对于一些只有在特定用户操作后才会用到的关联对象,可以在用户触发相应操作时再进行设置。