文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

IOS开发Objective-C Runtime如何使用

2023-07-05 03:05

关注

这篇文章主要介绍“IOS开发Objective-C Runtime如何使用”,在日常操作中,相信很多人在IOS开发Objective-C Runtime如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”IOS开发Objective-C Runtime如何使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

前言

Runtime 是使用 C 和汇编实现的运行时代码库,Objective-C 中有很多语言特性都是通过它来实现。了解 Runtime 开发可以帮助我们更灵活的使用 Objective-C 这门语言,我们可以将程序功能推迟到运行时再去决定怎么做,还可以利用 Runtime 来解决项目开发中的一些设计和技术问题,使开发过程更加具有灵活性。

一些关键字

IOS开发Objective-C Runtime如何使用

消息传递 (Messaging)

Objective-C 对于调用对象的某个方法这种行为叫做给对象发送消息,实际上就是沿着它的 isa 指针去查找真正的函数地址。下面我们来了解一下这个过程:

我们写一个给对象发送消息的代码

[array insertObject:obj atIndex:5];

编译器首先会将上面代码翻译成这种样子

objc_msgSend(array, @selector(insertObject:atIndex:), obj, 5);

系统在运行时会通过 array 对象的 isa 指针找到对应的 class(如果是给类发消息,则找到的是metaclass),然后在 class 的 cache 方法列表中用 SEL 去找对应 method,如果找不到便去 class 的方法列表中去找,如果在方法列表中也找不对对应 method 时,便沿着继承体系继续向上查找,找到后将 method 放入 cache,以便下次能快速定位,然后再去执行 method 的 IMP,找不到时系统便报错:unrecognized selector sent to insertObject:atIndex:

Runtime 提供了三种方法避免因为找不到方法而崩溃

当找不到方法实现时,Runtime 会先发送 +resolveInstanceMethod: 或 +resolveClassMethod: 消息,我们可以重写它然后为对象指定一个处理方法。

void dynamicXXXMethod(id obj, SEL _cmd) {    NSLog(@"ok...");}+ (BOOL)resolveInstanceMethod:(SEL)aSEL {    if(aSEL == @selector(xxx:)) {        class_addMethod([self class], aSEL, (IMP)dynamicXXXMethod, "v@:");        return YES;    }    return [super resolveInstanceMethod];}

class_addMethod 方法的最后一个参数用来指定所添加方法的参数及返回值,叫 Type Encodings。

如果 resolve 方法返回 NO,Runtime 会发送 -forwardingTargetForSelector: 消息,允许我们将消息转发给能处理它的其它对象。

- (id)forwardingTargetForSelector:(SEL)aSelector {    if(aSelector == @selector(xxx:)){        return otherObject;    }    return [super forwardingTargetForSelector:aSelector];}

当 -forwardingTargetForSelector: 返回 nil 时,Runtime 会发送 -methodSignatureForSelector: 和 -forwardInvocation: 消息。我们可以选择忽略消息、抛出异常、将消息转由当前对象或其它对象的任意消息来处理。

//根据 SEL 生成 NSInvocation 对象,然后再由 -forwardInvocation: 方法进行转发。- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];    if (!signature) {        signature = [otherObject instanceMethodSignatureForSelector:aSelector];    }    return signature;}- (void)forwardInvocation:(NSInvocation *)invocation {    SEL sel = invocation.selector;    if([otherObject respondsToSelector:sel]) {        [invocation invokeWithTarget:otherObject]; // 转发消息    }     else {        [self doesNotRecognizeSelector:sel]; // 抛出异常    }}

KVO

当我们为对象添加观察者后,Runtime 会在运行时创建这个对象所在类的子类,并且将该对象的 isa 指针指向这个子类,然后重写监听属性的 set 方法并在方法中调用 -willChangeValueForKey: 和 -didChangeValueForKey: 来通知观察者,所以如果直接修改实例变量便不会触发监听方法。当移除观察者后,Runtime 便会将这个子类删除。

所以 isa 指针并不总是指向实例对象所属的类,也有可能指向一个中间类,所以不能依靠它来确定类型,而是应该用 class 方法来确定实例对象的类。

关联对象 (Associated Objects)

在 Category 中可以为类添加实例方法或类方法,但是不支持添加实例变量,所以即使我们在 Category 中为类添加了 property,也不能直接使用它,Runtime 可以解决这个问题,我们只需要定义一个指针,然后通过 objc_setAssociatedObject 方法将指针与对象进行关联并指定内存管理方式,数据以 KeyValue 的形式存储在一个 HashMap 里。

Objc 中的类和对象都是结构体,Category 也是这样,定义的方法和属性在结构体中的存储,并在运行时按倒序添加到主类中(添加的方法会放在方法列表的上面),所以如果添加的方法与原类中的一样,那么在调用此方法时,优先找到的便是我们添加的这个方法。如果有多个 Category 添加同样名称的方法,那么这些方法在方法列表中的顺序取决于他们的编译顺序,也就是这些 Category 文件在 Compile Sources 中的顺序。

@interface NSObject (JC)@property (nonatomic, copy) NSString *ID;@end@implementation NSObject (JC)static const void *IDKey;- (NSString *)ID {    return objc_getAssociatedObject(self, &IDKey);}- (void)setID:(NSString *)ID {    objc_setAssociatedObject(self, &IDKey, ID, OBJC_ASSOCIATION_COPY_NONATOMIC);}@end

AOP(Method Swizzling)

我们可以通过继承、Category、AOP 方式来扩展类的功能。

在 Objective-C 中,可以通过 Method Swizzling 技术来实现 AOP,下面我们通过交换两个方法的实现代码来向已存在的方法中添加其它功能。

#import <objc/runtime.h> @implementation UIViewController (Tracking) + (void)load {     static dispatch_once_t onceToken;     dispatch_once(&onceToken, ^{         Class aClass = [self class];         SEL originalSelector = @selector(viewWillAppear:);         SEL swizzledSelector = @selector(swizzled_viewWillAppear:);         Method originalMethod = class_getInstanceMethod(aClass, originalSelector);         Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);         // 如果要对类方法进行交换,使用下面注释的代码        // Class aClass = object_getClass((id)self);        //         // Method originalMethod = class_getClassMethod(aClass, originalSelector);        // Method swizzledMethod = class_getClassMethod(aClass, swizzledSelector); // 交换两个方法的实现 // 防止 aClass 不存在 originalSelector,所以添加一下试试,但指向地址为新方法地址        BOOL didAddMethod = class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));         if (didAddMethod) {         // 添加成功,说明 aClass 不存在 originalSelector,所以替换 swizzledSelector 的 IMP 为 originalMethod,实质上它们都指向 swizzledMethod            class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));         }         else {          // 添加失败,说明 aClass 存在 originalSelector,直接交换            method_exchangeImplementations(originalMethod, swizzledMethod);         }     }); } #pragma mark - Method Swizzling // 由于方法实现已经被交换,所以系统在调用 viewWillAppear: 时,实际上会调用 swizzled_viewWillAppear:- (void)swizzled_viewWillAppear:(BOOL)animated { // 下面代码表面上看起来会引起递归调用,由于函数实现已经被交换,实际上会调用 viewWillAppear:   [self swizzled_viewWillAppear:animated]; // 在原有基础上添加其它功能(写日志等)} @end

使用 Method Swizzling 需要注意下面几个问题

其它

我们可以通过 Runtime 特性来获得类的所有属性名称和类型,然后再通过 KVC 将 JSON 中的值填充给该类的对象。还可以在程序运行时为类添加方法或替换方法从而使对象能够更灵活的根据需要来选择实现方法。总之 Runtime 库就象一堆积木,只要发挥想象力便能实现各种各样的功能,但前提是你需要了解它。

到此,关于“IOS开发Objective-C Runtime如何使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯