MST

星途 面试题库

面试题:Objective-C运行时关联对象的实现原理与应用场景

阐述Objective-C运行时关联对象(Associated Objects)的底层实现原理,以及在实际开发中,你会在哪些具体场景下使用关联对象来解决问题,并举例说明如何避免因使用关联对象可能导致的内存管理问题。
22.9万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

底层实现原理

  1. 关联对象的存储:Objective-C 运行时通过一个全局的哈希表来存储关联对象。这个哈希表以对象的内存地址作为 key,每个 key 对应一个 objc_object 结构体,结构体中包含了指向关联对象数组的指针。
  2. 关联策略:运行时使用关联策略(objc_AssociationPolicy)来决定如何管理关联对象的内存。这些策略包括 OBJC_ASSOCIATION_ASSIGN(弱引用)、OBJC_ASSOCIATION_RETAIN_NONATOMIC(非原子强引用)、OBJC_ASSOCIATION_COPY_NONATOMIC(非原子拷贝)、OBJC_ASSOCIATION_RETAIN(原子强引用)和 OBJC_ASSOCIATION_COPY(原子拷贝)。
  3. 关联过程:当使用 objc_setAssociatedObject 函数来设置关联对象时,运行时会根据传入的关联策略,将关联对象存储到全局哈希表中与目标对象对应的位置。在获取关联对象时,通过 objc_getAssociatedObject 函数从哈希表中查找对应的对象。

实际开发场景

  1. 给分类添加属性:在 Objective-C 中,分类不能直接添加实例变量,但可以通过关联对象来模拟添加属性。例如,给 UIButton 分类添加一个自定义属性 customData,用于存储与按钮相关的额外数据。
#import <objc/runtime.h>

@interface UIButton (CustomData)
@property (nonatomic, strong) id customData;
@end

@implementation UIButton (CustomData)
static const char customDataKey;

- (void)setCustomData:(id)customData {
    objc_setAssociatedObject(self, &customDataKey, customData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)customData {
    return objc_getAssociatedObject(self, &customDataKey);
}
@end
  1. 在不继承的情况下扩展对象功能:比如在一个基础的 NSObject 子类上,通过关联对象添加一些特定功能的数据,而无需创建新的子类。例如,为网络请求类添加一个用于存储请求标识的属性,方便在请求过程中进行管理。
@interface NetworkRequest : NSObject
@end

@implementation NetworkRequest
@end

@interface NetworkRequest (RequestIdentifier)
@property (nonatomic, copy) NSString *requestIdentifier;
@end

@implementation NetworkRequest (RequestIdentifier)
static const char requestIdentifierKey;

- (void)setRequestIdentifier:(NSString *)requestIdentifier {
    objc_setAssociatedObject(self, &requestIdentifierKey, requestIdentifier, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)requestIdentifier {
    return objc_getAssociatedObject(self, &requestIdentifierKey);
}
@end

避免内存管理问题

  1. 选择合适的关联策略:根据关联对象的生命周期需求,选择正确的关联策略。如果关联对象的生命周期与宿主对象不一致,并且不希望宿主对象持有强引用导致循环引用,应选择 OBJC_ASSOCIATION_ASSIGN。例如,在处理视图控制器与临时数据的关联时,如果临时数据的生命周期可能比视图控制器短,使用弱引用策略。
  2. 及时释放关联对象:在宿主对象的 dealloc 方法中,手动清除关联对象,以避免内存泄漏。例如:
@interface MyViewController : UIViewController
@end

@implementation MyViewController
static const char associatedObjectKey;

- (void)dealloc {
    objc_setAssociatedObject(self, &associatedObjectKey, nil, OBJC_ASSOCIATION_ASSIGN);
}
@end
  1. 避免循环引用:在使用关联对象时,要注意避免循环引用。如果两个对象相互通过关联对象持有对方,可能会导致对象无法释放。可以通过合理设计对象关系,或者使用弱引用策略来打破循环。例如,在视图控制器与它所管理的子视图之间,如果子视图通过关联对象持有视图控制器,应使用弱引用策略。