Objective-C 关联对象与 Method Swizzling
关联对象
关联对象,顾名思义,即通过唯一键(key)连接(关联)至某个类的实例上的对象。
那么什么时候会用到关联对象呢?
比如,我们需要对内置类 NSArray
添加一个属性(不使用继承)。如何解决?分类似乎只能添加方法。当我们了解关联对象后,就可以轻松实现。
关联对象基础
设置关联对象
1  | void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)  | 
参数说明:
object: 与谁关联,通常是selfkey: 唯一键,在获取值时通过该键获取,通常是使用static const void *来声明value: 关联所设置的值policy: 内存管理策略
内存管理策略
1  | typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy){  | 
当对象释放时,会根据设置关联对象时采用的策略来决定是否释放关联对象。当策略为
RETAIN/COPY 时,释放关联对象。当策略为 ASSIGN
时,不释放关联对象。
获取关联对象
1  | id objc_getAssociatedObject(id object, const void *key)  | 
参数说明:
object: 与谁关联,通常是传self,在设置关联时所指定的与哪个对象关联的那个对象key: 唯一键,在设置关联值所指定的键
取消关联对象
1  | void objc_removeAssociatedObjects(id object)  | 
取消对象的所有关联对象。如果要取消指定的关联对象,可使用
setAssociatedObject 设置为 nil 来实现。
关联对象应用
给 UIViewController 添加一个是否需要登录的属性。
1
2
3
4
5@interface UIViewController (Extension)
@property (nonatomic, assign) BOOL needToLogin;
@end
1  | static const char *ViewControllerNeedToLoginKey = "ViewControllerNeedToLoginKey";  | 
Method Swizzling
Method Swizzling,顾名思义,就是将两个方法的实现交换。
那么什么时候会用到 Method Swizzling 呢?
比如,在开发中,我们可能会遇到系统提供的 API 不能满足实际需求。我们希望能够修改它以达到期望的效果。
Method Swizzling 原理
Method Swizzling 的实现充分利用了 Objective-C runtime 动态绑定机制。
在 Objective-C
中调用方法,其实是向一个对象发送消息,而查找消息的唯一依据是方法名
selector。每个类都有一个方法列表
objc_method_list,存放着其所有的方法
objc_method。
1  | typedef struct objc_method *Method  | 
每个方法 objc_method
保存了方法名(SEL)和方法实现(IMP)的映射关系。Method
Swizzling 其实就是重置了 SEL 和 IMP
的映射关系。如下图所示:

Method Swizzling 基础
获取方法
1  | Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)  | 
参数说明:
cls: 目标类name: 方法名
获取方法实现
1  | IMP _Nonnull class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)  | 
添加方法
1  | BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)  | 
参数说明:
cls: 目标类name: 要添加方法的方法名imp: 要添加方法的方法实现types: 方法实现的编码类型
替换方法
1  | IMP _Nullable class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)  | 
参数说明:
cls: 目标类name: 目标方法的方法名imp: 方法的新方法实现types: 方法实现的编码类型
交换方法实现
1  | void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)  | 
获取方法的编码类型
1  | const char * _Nullable method_getTypeEncoding(Method _Nonnull m)  | 
Method Swizzling 应用
通过分类允许 NSObject 对任意两个方法进行 Method
Swizzling。
1  | @interface NSObject (Swizzle)  | 
1  | @implementation NSObject (Swizzle)  |