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 其实就是重置了 SELIMP 的映射关系。如下图所示:

Objective-C 关联对象与 Method Swizzling

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];
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

高性能HTML5

高性能HTML5

Jay Bryant、Mike Jones / 奇舞团 / 电子工业出版社 / 2014-5

《高性能html5》为读者讲解了如何用html5 从一开始就设计一个高性能的网站,以及如何对已有网站进行改良,使得它们具备优越的性能。《高性能html5》中总结了许多实践经验、关键技巧,并提供了丰富的示例,作者有意无意地将软件工程以及前端开发技术之道隐藏于朴实的描述中。 通过学习《高性能html5》,读者能够掌握如何创建自己的高性能网站。《高性能html5》适合于想创建自己网站的初学者,同样......一起来看看 《高性能HTML5》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具