面试题答案
一键面试关联对象基本原理
在Objective-C运行时中,关联对象是通过objc_setAssociatedObject
、objc_getAssociatedObject
和objc_removeAssociatedObjects
这几个函数来实现的。
每个对象都有一个AssociationsManager
,它内部管理着一个AssociationsHashMap
。当通过objc_setAssociatedObject
函数为对象设置关联对象时,会将源对象(设置关联对象的对象)、关联对象的键(可以是NSString
等类型)以及关联对象的值包装成一个ObjectAssociation
结构体,存储在AssociationsHashMap
中。
当通过objc_getAssociatedObject
获取关联对象时,会从AssociationsHashMap
中根据源对象和键找到对应的ObjectAssociation
结构体,从而取出关联对象的值。objc_removeAssociatedObjects
则是将AssociationsHashMap
中与源对象相关的所有关联对象移除。
使用场景举例
- 为类别(Category)添加属性:在Objective-C中,类别无法直接声明属性。通过关联对象技术,可以为类别添加“属性”。例如,为
UIButton
类别添加一个NSString
类型的identifier
属性,用于标识按钮的特定用途。
@interface UIButton (Identifier)
@property (nonatomic, strong) NSString *identifier;
@end
@implementation UIButton (Identifier)
- (void)setIdentifier:(NSString *)identifier {
objc_setAssociatedObject(self, @selector(identifier), identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)identifier {
return objc_getAssociatedObject(self, @selector(identifier));
}
@end
- 为代理模式提供补充信息:在代理模式中,有时候代理对象需要额外的信息来处理委托事件。例如,在一个图片下载的代理回调中,通过关联对象为下载任务对应的视图控制器存储一些额外的配置信息,如图片的显示尺寸等。
@interface ImageDownloader : NSObject
@property (nonatomic, weak) id<ImageDownloaderDelegate> delegate;
@end
@implementation ImageDownloader
- (void)downloadImageWithURL:(NSURL *)url {
// 下载逻辑
// 下载完成后调用代理方法,并可以通过关联对象传递额外信息
id<ImageDownloaderDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(imageDownloaded:withInfo:)]) {
NSDictionary *info = objc_getAssociatedObject(self, @selector(downloadInfo));
[delegate imageDownloaded:image withInfo:info];
}
}
@end
- 实现对象间的松散耦合关系:当两个对象之间需要传递数据,但又不想建立紧密的继承或组合关系时,可以使用关联对象。比如在一个复杂的视图层级中,一个子视图可能需要根据其所在父视图的一些特定配置来调整自身行为,通过关联对象可以在不直接引用父视图特定属性的情况下传递这些配置信息。