博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
玩转iOS开发:iOS开发中的装逼技术 - RunTime(一)
阅读量:6880 次
发布时间:2019-06-27

本文共 12865 字,大约阅读时间需要 42 分钟。

文章分享至我的个人技术博客:https://cainluo.github.io/15033286127687.html


RunTimeObjective-C的特性, 如果用别的话来说, 就是因为Objective-C是动态语言, 然后RunTime就是它的运行时机制这些这些, 然后就没然后了...

但是对于我这些渣渣来说, 个人认为就是一堆C语言写的东西, 废话少说了, 直接来撸吧.

转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.


objc_msgSend

在我们平常的使用当中, 会经常声明一个函数, 然后去调用, 但里面做了什么操作, 我们并不知道, 现在我们来看一段代码:

#import "RunTimeModel.h"#import 
#import
@implementation RunTimeModel- (instancetype)init { self = [super init]; if (self) { [self sendMessage]; [self sendMessage:100]; } return self;}- (void)sendMessage { NSLog(@"Message");}- (void)sendMessage:(NSInteger)messageCount { NSLog(@"Message: %ld", messageCount);}@end复制代码

这段代码, 是我们正常写的Objective-C代码, 我们可以通过终端的命令行, 进行重编:

clang -rewrite-objc RunTimeModel.m复制代码

然后就会得到一个RunTimeModel.cpp的文件, 里面有90000+行代码, 这里面我们要找到一段东西:

static instancetype _I_RunTimeModel_init(RunTimeModel * self, SEL _cmd) {    self = ((RunTimeModel *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("RunTimeModel"))}, sel_registerName("init"));    if (self) {        ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage"));        ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage:"), (NSInteger)100);    }    return self;}复制代码
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage"));((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage:"), (NSInteger)100);复制代码

这就是我们在.m文件里调用方法时所进行的操作, 会转化成消息发送的形式进行通信, objc_msgSend是在#import <objc/message.h>文件中, 声明方式:

OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);复制代码

这里是有两个基础参数, 分别是idSEL.


id / SEL

idSEL都是定义在#include <objc/objc.h>中:

typedef struct objc_object *id;typedef struct objc_selector *SEL;复制代码
  • SEL: 本质就是一个映射到方法的C字符串, 我们可以用Objective-C@selector()或者RunTime里的sel_registerName来获取一个SEL类型的方法选择器.
  • id: 它是一个结构体指针类型, 可以指向Objective-C中的任何对象.

objc_object定义:

struct objc_object {    Class isa  OBJC_ISA_AVAILABILITY;};复制代码

其实这才是对象本来的面貌, 不要给漂亮的外表给蒙骗了咯.

这个结构体就只有一个isa成员变量, 对象是可以通过isa指针找到自己所属的类, 看到这里, 我们就不禁疑惑, isa是一个Class的成员变量, 那Class又是啥?


Class

我们在#include <objc/objc.h>中其实是有看到Class的声明:

typedef struct objc_class *Class;复制代码

但实际上Class是定义在#include <objc/runtime.h>中:

struct objc_class {    Class isa  OBJC_ISA_AVAILABILITY;#if !__OBJC2__    Class super_class                                        OBJC2_UNAVAILABLE;    const char *name                                         OBJC2_UNAVAILABLE;    long version                                             OBJC2_UNAVAILABLE;    long info                                                OBJC2_UNAVAILABLE;    long instance_size                                       OBJC2_UNAVAILABLE;    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;复制代码

这里解释一下里面的东东:

  • Class: 也有一个isa指针, 指向所属的meta(元类).
  • super_class: 指向的是它的超类.
  • name: 类名.
  • version: 类的版本信息.
  • info: 类的详情信息.
  • instance_size: 这个类的示例对象的大小.
  • ivars: 指向这个类的成员变量列表, 包括内部的变量.
  • methodLists: 指向这个类的示例方法列表, 它将方法选择器和方法实现地址联系在一起.
  • cache: Runtime会把被调用的方法存到cache中, 下次查找的时候效率更高, 其实就是这个方法第一次被调用了之后, 为了以后还会被调用的可能而做的缓存.
  • protocols: 指向这个类的协议列表.

这里的methodLists需要注意一下, 它是指向objc_method_list指针的指针, 也就是说可以动态修改methodLists的值来添加成员方法, 我们经常用的Category就是酱紫来的, 也因为这个东西, Category一般是没办法添加属性, 需要我们自己写写写.

看到这里, 基本的东西我们都差不多了解完了, 现在加个补刀, 看看整个运行的过程:

  • Runtime会把我们的方法调用转化为消息发送, 也就是我们刚刚说的objc_msgSend, 并且把方法的调用者和方法选择器, 当做参数传递过去.
  • 这个时候方法的调用者会通过isa指针来找到方法所属的类, 然后在cache或者methodLists查找被调用的方法, 找到了就跳转到对应的方法去执行.
    • 如果在类中没有找到该方法, 就会通过super_class往更上一级的超类中查找, 查找到了就执行(如果找不到呢? 这个后面会有补充).

说完这里, 有些人肯定会很奇怪, 这里的methodLists装的是实例方法, 那类方法呢?

其实, 类方法是被存储在元类中, Class会通过isa指针找到所属的元类, 这些类方法就是存在这里了, 具体怎么获取类方法, 我们可以看看代码:

- (void)getClassMethods {        NSObject *obj = [[NSObject alloc] init];        unsigned int methodCount = 0;        const char *className = class_getName([obj class]);        Class metaClass = objc_getMetaClass(className);        Method *methodList = class_copyMethodList(metaClass, &methodCount);        for (int i = 0; i < methodCount; i++) {                Method method = methodList[i];                SEL selector = method_getName(method);                const char *methodName = sel_getName(selector);                NSLog(@"%s", methodName);    }}复制代码

打印出来的结果:vim

2017-08-22 13:24:19.455 1.RunTime[32885:2667202] _installAppearanceSwizzlesForSetter:2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityGuidedAccessStateEnabled2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityGuidedAccessRestrictionStateForIdentifier:2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityRequestGuidedAccessSession:completion:2017-08-22 13:24:19.456 1.RunTime[32885:2667202] isSelectorExcludedFromWebScript:2017-08-22 13:24:19.457 1.RunTime[32885:2667202] isKeyExcludedFromWebScript:2017-08-22 13:24:19.457 1.RunTime[32885:2667202] _webkit_invokeOnMainThread2017-08-22 13:24:19.457 1.RunTime[32885:2667202] sbs_dataFromObject:2017-08-22 13:24:19.457 1.RunTime[32885:2667202] sbs_objectFromData:2017-08-22 13:24:19.458 1.RunTime[32885:2667202] sbs_dataWithValue:2017-08-22 13:24:19.458 1.RunTime[32885:2667202] sbs_valueFromData:ofType:2017-08-22 13:24:19.458 1.RunTime[32885:2667202] CA_automaticallyNotifiesObservers:2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_setterForProperty:2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_getterForProperty:2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_encodesPropertyConditionally:type:2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_CAMLPropertyForKey:2017-08-22 13:24:19.459 1.RunTime[32885:2667202] bs_decodedFromData:2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_objectFromData:2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureObjectFromData:ofClass:2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureObjectFromData:ofClasses:2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_synchronousWrapper:timeout:2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureDataFromObject:2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_dataFromObject:2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_secureDecodedFromData:withAdditionalClasses:2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_secureDecodedFromData:2017-08-22 13:24:19.506 1.RunTime[32885:2667202] replacementObjectForPortCoder:2017-08-22 13:24:19.506 1.RunTime[32885:2667202] instanceMethodDescriptionForSelector:2017-08-22 13:24:19.507 1.RunTime[32885:2667202] methodDescriptionForSelector:2017-08-22 13:24:19.507 1.RunTime[32885:2667202] _localClassNameForClass2017-08-22 13:24:19.507 1.RunTime[32885:2667202] cancelPreviousPerformRequestsWithTarget:selector:object:2017-08-22 13:24:19.507 1.RunTime[32885:2667202] cancelPreviousPerformRequestsWithTarget:2017-08-22 13:24:19.507 1.RunTime[32885:2667202] setVersion:2017-08-22 13:24:19.508 1.RunTime[32885:2667202] implementsSelector:2017-08-22 13:24:19.508 1.RunTime[32885:2667202] instancesImplementSelector:2017-08-22 13:24:19.508 1.RunTime[32885:2667202] load2017-08-22 13:24:19.508 1.RunTime[32885:2667202] version2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classForKeyedUnarchiver2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classFallbacksForKeyedArchiver2017-08-22 13:24:19.509 1.RunTime[32885:2667202] _shouldAddObservationForwardersForKey:2017-08-22 13:24:19.509 1.RunTime[32885:2667202] setKeys:triggerChangeNotificationsForDependentKey:2017-08-22 13:24:19.510 1.RunTime[32885:2667202] automaticallyNotifiesObserversForKey:2017-08-22 13:24:19.510 1.RunTime[32885:2667202] _keysForValuesAffectingValueForKey:2017-08-22 13:24:19.510 1.RunTime[32885:2667202] keyPathsForValuesAffectingValueForKey:2017-08-22 13:24:19.510 1.RunTime[32885:2667202] _createValueGetterWithContainerClassID:key:2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createValueSetterWithContainerClassID:key:2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createMutableOrderedSetValueGetterWithContainerClassID:key:2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createMutableSetValueGetterWithContainerClassID:key:2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createValuePrimitiveGetterWithContainerClassID:key:2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createValuePrimitiveSetterWithContainerClassID:key:2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createOtherValueGetterWithContainerClassID:key:2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createOtherValueSetterWithContainerClassID:key:2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createMutableArrayValueGetterWithContainerClassID:key:2017-08-22 13:24:19.513 1.RunTime[32885:2667202] accessInstanceVariablesDirectly2017-08-22 13:24:19.513 1.RunTime[32885:2667202] instanceMethodSignatureForSelector:2017-08-22 13:24:19.513 1.RunTime[32885:2667202] load2017-08-22 13:24:19.513 1.RunTime[32885:2667202] dealloc2017-08-22 13:24:19.514 1.RunTime[32885:2667202] doesNotRecognizeSelector:2017-08-22 13:24:19.514 1.RunTime[32885:2667202] description2017-08-22 13:24:19.514 1.RunTime[32885:2667202] methodSignatureForSelector:2017-08-22 13:24:19.514 1.RunTime[32885:2667202] __allocWithZone_OA:2017-08-22 13:24:19.515 1.RunTime[32885:2667202] _copyDescription2017-08-22 13:24:19.515 1.RunTime[32885:2667202] init2017-08-22 13:24:19.515 1.RunTime[32885:2667202] zone2017-08-22 13:24:19.515 1.RunTime[32885:2667202] instancesRespondToSelector:2017-08-22 13:24:19.516 1.RunTime[32885:2667202] instanceMethodForSelector:2017-08-22 13:24:19.516 1.RunTime[32885:2667202] isAncestorOfObject:2017-08-22 13:24:19.516 1.RunTime[32885:2667202] instanceMethodSignatureForSelector:2017-08-22 13:24:19.516 1.RunTime[32885:2667202] load2017-08-22 13:24:19.517 1.RunTime[32885:2667202] initialize2017-08-22 13:24:19.517 1.RunTime[32885:2667202] resolveInstanceMethod:2017-08-22 13:24:19.517 1.RunTime[32885:2667202] resolveClassMethod:2017-08-22 13:24:19.517 1.RunTime[32885:2667202] retain2017-08-22 13:24:19.518 1.RunTime[32885:2667202] release2017-08-22 13:24:19.518 1.RunTime[32885:2667202] autorelease2017-08-22 13:24:19.518 1.RunTime[32885:2667202] retainCount2017-08-22 13:24:19.518 1.RunTime[32885:2667202] alloc2017-08-22 13:24:19.519 1.RunTime[32885:2667202] allocWithZone:2017-08-22 13:24:19.519 1.RunTime[32885:2667202] dealloc2017-08-22 13:24:19.519 1.RunTime[32885:2667202] copy2017-08-22 13:24:19.519 1.RunTime[32885:2667202] new2017-08-22 13:24:19.520 1.RunTime[32885:2667202] forwardInvocation:2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _tryRetain2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _isDeallocating2017-08-22 13:24:19.520 1.RunTime[32885:2667202] retainWeakReference2017-08-22 13:24:19.521 1.RunTime[32885:2667202] allowsWeakReference2017-08-22 13:24:19.521 1.RunTime[32885:2667202] copyWithZone:2017-08-22 13:24:19.521 1.RunTime[32885:2667202] mutableCopyWithZone:2017-08-22 13:24:19.522 1.RunTime[32885:2667202] doesNotRecognizeSelector:2017-08-22 13:24:19.522 1.RunTime[32885:2667202] description2017-08-22 13:24:19.522 1.RunTime[32885:2667202] isFault2017-08-22 13:24:19.522 1.RunTime[32885:2667202] mutableCopy2017-08-22 13:24:19.523 1.RunTime[32885:2667202] performSelector:withObject:2017-08-22 13:24:19.523 1.RunTime[32885:2667202] isMemberOfClass:2017-08-22 13:24:19.524 1.RunTime[32885:2667202] hash2017-08-22 13:24:19.524 1.RunTime[32885:2667202] isEqual:2017-08-22 13:24:19.524 1.RunTime[32885:2667202] self2017-08-22 13:24:19.524 1.RunTime[32885:2667202] performSelector:2017-08-22 13:24:19.525 1.RunTime[32885:2667202] conformsToProtocol:2017-08-22 13:24:19.525 1.RunTime[32885:2667202] methodSignatureForSelector:2017-08-22 13:24:19.525 1.RunTime[32885:2667202] forwardingTargetForSelector:2017-08-22 13:24:19.525 1.RunTime[32885:2667202] methodForSelector:2017-08-22 13:24:19.526 1.RunTime[32885:2667202] performSelector:withObject:withObject:2017-08-22 13:24:19.526 1.RunTime[32885:2667202] superclass2017-08-22 13:24:19.526 1.RunTime[32885:2667202] isSubclassOfClass:2017-08-22 13:24:19.527 1.RunTime[32885:2667202] class2017-08-22 13:24:19.527 1.RunTime[32885:2667202] init2017-08-22 13:24:19.528 1.RunTime[32885:2667202] debugDescription2017-08-22 13:24:19.528 1.RunTime[32885:2667202] isProxy2017-08-22 13:24:19.529 1.RunTime[32885:2667202] respondsToSelector:2017-08-22 13:24:19.529 1.RunTime[32885:2667202] isKindOfClass:复制代码

isa的补充

这里顺带补充一下isa指针的指向:

  • isa指针指向的是元类.
  • 元类isa指针指向的是根类.
  • 如果根类或者是元类的超类是NSObject, 那么就是指向自己.
  • NSObject是没有超类的.


工程地址

项目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/玩转iOS开发:iOS中的RunTime(一)

注意: RunTimeModel.cpp在目录中, 我并没有放到工程里.


最后

码字很费脑, 看官赏点饭钱可好

你可能感兴趣的文章
PAT - L2-010. 排座位(并查集)
查看>>
HDU - 5269【SBBBBBB Trie】
查看>>
sql server 日志文件结构及误操作数据找回
查看>>
JUnit 3一个例子就懂
查看>>
Mongodb相关 (Shell命令 / mongoose)
查看>>
Web API的Log问题
查看>>
leetcode Second Highest Salary
查看>>
【LeetCode每天一题】Word Break()
查看>>
关于centerOS下修改网络连接
查看>>
牛客暑假多校第二场 K carpet
查看>>
Linux下chkconfig命令详解(转)
查看>>
EF中,保存实体报错:Validation failed for one or more entities. 如何知道具体错误在哪?...
查看>>
和积式
查看>>
你不能错过.net 并发解决方案
查看>>
[PHP] 超全局变量$_FILES上传文件
查看>>
linux如何添加telnet服务
查看>>
解决Windows对JDK默认版本切换问题
查看>>
HTML5本地存储localStorage与seesionStorage
查看>>
06笨小猴(1.9)
查看>>
UNIX网络编程——原始套接字的魔力【上】
查看>>