Objective-C 关联对象与 Method Swizzling
栏目: Objective-C · 发布时间: 5年前
内容简介:关联对象,顾名思义,即通过唯一键(那么什么时候会用到关联对象呢?比如,我们需要对内置类
关联对象,顾名思义,即通过唯一键( key
)连接(关联)至某个类的实例上的对象。
那么什么时候会用到关联对象呢?
比如,我们需要对内置类 NSArray
添加一个属性(不使用继承)。如何解决?分类似乎只能添加方法。当我们了解关联对象后,就可以轻松实现。
关联对象基础
设置关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
参数说明:
-
object
: 与谁关联,通常是self
-
key
: 唯一键,在获取值时通过该键获取,通常是使用static const void *
来声明 -
value
: 关联所设置的值 -
policy
: 内存管理策略
内存管理策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy){ OBJC_ASSOCIATION_ASSIGN = 0, // 表示弱引用关联,通常是基本数据类型 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 表示强引用关联对象,是线程安全的 OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // 表示关联对象copy,是线程安全的 OBJC_ASSOCIATION_RETAIN = 01401, // 表示强引用关联对象,不是线程安全的 OBJC_ASSOCIATION_COPY = 01403 // 表示关联对象copy,不是线程安全的 };
当对象释放时,会根据设置关联对象时采用的策略来决定是否释放关联对象。当策略为 RETAIN/COPY
时,释放关联对象。当策略为 ASSIGN
时,不释放关联对象。
获取关联对象
id objc_getAssociatedObject(id object, const void *key)
参数说明:
-
object
: 与谁关联,通常是传self
,在设置关联时所指定的与哪个对象关联的那个对象 -
key
: 唯一键,在设置关联值所指定的键
取消关联对象
void objc_removeAssociatedObjects(id object)
取消对象的所有关联对象。如果要取消指定的关联对象,可使用 setAssociatedObject
设置为 nil
来实现。
关联对象应用
给 UIViewController
添加一个是否需要登录的属性。
@interface UIViewController (Extension) @property (nonatomic, assign) BOOL needToLogin; @end
static const char *ViewControllerNeedToLoginKey = "ViewControllerNeedToLoginKey"; - (void)setNeedToLogin:(BOOL)needToLogin { objc_setAssociatedObject(self, ViewControllerNeedToLoginKey, @(needToLogin), OBJC_ASSOCIATION_ASSIGN); } - (BOOL)needToLogin { return [objc_getAssociatedObject(self, ViewControllerNeedToLoginKey) boolValue]; }
Method Swizzling
Method Swizzling,顾名思义,就是将两个方法的实现交换。
那么什么时候会用到 Method Swizzling 呢?
比如,在开发中,我们可能会遇到系统提供的 API 不能满足实际需求。我们希望能够修改它以达到期望的效果。
Method Swizzling 原理
Method Swizzling 的实现充分利用了 Objective-C runtime 动态绑定机制 。
在 Objective-C 中调用方法,其实是向一个对象发送消息,而查找消息的唯一依据是方法名 selector
。每个类都有一个方法列表 objc_method_list
,存放着其所有的方法 objc_method
。
typedef struct objc_method *Method struct objc_method{ SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; // 方法实现 }
每个方法 objc_method
保存了方法名( SEL
)和方法实现( IMP
)的映射关系。Method Swizzling 其实就是重置了 SEL
和 IMP
的映射关系。如下图所示:
Method Swizzling 基础
获取方法
Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
参数说明:
cls name
获取方法实现
IMP _Nonnull class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
添加方法
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
参数说明:
cls name imp types
替换方法
IMP _Nullable class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
参数说明:
cls name imp types
交换方法实现
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
获取方法的编码类型
const char * _Nullable method_getTypeEncoding(Method _Nonnull m)
Method Swizzling 应用
通过分类允许 NSObject
对任意两个方法进行 Method Swizzling。
@interface NSObject (Swizzle) + (BOOL)swizzleMethod:(SEL)originalSEL withMethod:(SEL)targetSEL error:(NSError **)error; + (BOOL)swizzleClassMethod:(SEL)originalSEL withMethod:(SEL)targetSEL error:(NSError **)error; @end
@implementation NSObject (Swizzle) + (BOOL)swizzleMethod:(SEL)originalSEL withMethod:(SEL)targetSEL error:(NSError *__autoreleasing *)error { Method originalMethod = class_getInstanceMethod(self, originalSEL); if (originalMethod == nil) { return NO; } Method targetMethod = class_getInstanceMethod(self, targetSEL); if (targetMethod == nil) { return NO; } class_addMethod(self, originalSEL, class_getMethodImplementation(self, originalSEL), method_getTypeEncoding(originalMethod)); class_addMethod(self, targetSEL, class_getMethodImplementation(self, targetSEL), method_getTypeEncoding(targetMethod)); method_exchangeImplementations(class_getInstanceMethod(self, originalSEL), class_getInstanceMethod(self, targetSEL)); return YES; } + (BOOL)swizzleClassMethod:(SEL)originalSEL withMethod:(SEL)targetSEL error:(NSError *__autoreleasing *)error { Class metaClass = object_getClass((id)self); return [metaClass swizzleMethod:originalSEL withMethod:targetSEL error:error]; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 解耦关联对象——观察者模式详解
- Flink 维表关联系列之 Kafka 维表关联:广播方式
- Flink 维表关联系列之 Redis 维表关联:实时查询
- Flink 维表关联系列之 MySQL 维表关联:全量加载
- Flink 维表关联系列之 Hbase 维表关联:LRU 策略
- GORM 关联查询
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
小米生态链战地笔记
小米生态链谷仓学院 / 中信出版集团 / 2017-5 / 56.00
2013年下半年,小米开始做一件事,就是打造一个生态链布局IoT(物联网);2016年年底,小米生态链上已经拥有了77家企业,生态链企业整体销售额突破100亿元。这3年,是小米生态链快速奔跑的3年,也是小米在商场中不断厮杀着成长的3年。 3年,77家生态链企业,16家年销售额破亿,4家独角兽公司,边实战,边积累经验。 小米生态链是一个基于企业生态的智能硬件孵化器。过去的3年中,在毫无先......一起来看看 《小米生态链战地笔记》 这本书的介绍吧!