Objective-C 关联对象与 Method Swizzling
关联对象
关联对象,顾名思义,即通过唯一键(key
)连接(关联)至某个类的实例上的对象。
那么什么时候会用到关联对象呢?
比如,我们需要对内置类 NSArray
添加一个属性(不使用继承)。如何解决?分类似乎只能添加方法。当我们了解关联对象后,就可以轻松实现。
关联对象基础
设置关联对象
1 | void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) |
参数说明:
object
: 与谁关联,通常是self
key
: 唯一键,在获取值时通过该键获取,通常是使用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) |