Runtime 相关知识笔记
Runtime 相关知识笔记
- isa详解
struct objc_object {
private:isa_t isa;
}
union isa_t // arm64 架构
{Class cls;uintptr_t bits;struct {uintptr_t nonpointer : 1; // 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址1,代表优化过,使用位域存储更多的信息uintptr_t has_assoc : 1; // 是否有过关联对象uintptr_t has_cxx_dtor : 1; // 是否有C++析构函数uintptr_t shiftcls : 33; // 存储Class、Meta-Class的内存地址信息uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化uintptr_t weakly_referenced : 1; // 是否有弱引用uintptr_t deallocating : 1; // 对象是否正在释放uintptr_t has_sidetable_rc : 1; // 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中uintptr_t extra_rc : 19; // 里面存储的值是引用计数减1};
}
- Class的结构内容分析
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
struct class_rw_t {method_array_t methods;property_array_t properties;protocol_array_t protocols;
}class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
struct class_ro_t {method_list_t * baseMethodList;protocol_list_t * baseProtocols;property_list_t *baseProperties;
};method_t是对方法\函数的封装
struct method_t {SEL name; // 函数名const char *types; // 编码(返回值类型、参数类型)IMP imp; // 指向函数的指针(函数地址)
};IMP代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
typedef struct objc_selector *SEL;SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似
1. 可以通过@selector()和sel_registerName()获得
2. 可以通过sel_getName()和NSStringFromSelector()转成字符串
3. 不同类中相同名字的方法,所对应的方法选择器是相同的
Class底层结构图

Type Encoding
| Code | Meaning |
|---|---|
| c | A char |
| i | An int |
| s | A short |
| l | A long |
| q | A long long |
| C | An unsigned char |
| I | An unsigned int |
| S | An unsigned short |
| L | An unsigned long |
| Q | An unsigned long long |
| f | A float |
| d | A double |
| B | A C++ bool or a C99 _Bool |
| v | A void |
| * | A character string (char *) |
| @ | An object |
| # | A class object(Class) |
| : | A method selector (SEL) |
- 方法缓存
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度。
struct cache_t {struct bucket_t *_buckets; // 散列表mask_t _mask; // 散列表长度 - 1mask_t _occupied; // 已经缓存的方法数量
}struct bucket_t {
private:cache_key_t _key; // SEL作为keyIMP _imp; // 函数的内存地址
}// 缓存查找的源码
objc-cache.mm
bucket_t * cache_t::find(cache_key_t k, id receiver)
{assert(k != 0);bucket_t *b = buckets();mask_t m = mask();mask_t begin = cache_hash(k, m);mask_t i = begin;do {if (b[i].key() == 0 || b[i].key() == k) {return &b[i];}} while ((i = cache_next(i, m)) != begin);// hackClass cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));cache_t::bad_cache(receiver, (SEL)k, cls);
}
static inline mask_t cache_hash(cache_key_t key, mask_t mask)
{return (mask_t)(key & mask);
}static inline mask_t cache_next(mask_t i, mask_t mask) {return i ? i-1 : mask;
}
- objc_msgSend的执行流程
OC中的方法调用,其实都是转换为objc_msgSend函数的调用
objc_msgSend的执行流程可以分为3大阶段:
消息发送、动态方法解析、消息转发
objc_msgSend执行流程 – 源码跟读
objc-msg-arm64.s
1. ENTRY _objc_msgSend
2. b.le LNilOrTagged
3. CacheLookup NORMAL
4. .macro CacheLookup
5. .macro CheckMiss
6. STATIC_ENTRY __objc_msgSend_uncached
7. .macro MethodTableLookup
8. __class_lookupMethodAndLoadCache3objc-runtime-new.mm
1. _class_lookupMethodAndLoadCache3
2. lookUpImpOrForward
3. getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
4. cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
5. _class_resolveInstanceMethod
6. _objc_msgForward_impcacheobjc-msg-arm64.s
1. STATIC_ENTRY __objc_msgForward_impcache
2. ENTRY __objc_msgForwardCore Foundation
__forwarding__(不开源)阅读流程:objc-msg-arm64.s
1、首先在objc-msg-arm64.s找到ENTRY _objc_msgSend 方法,比较cmp x0, #0,如果x0为空,则return(x0是消息接收者)
2、然后查看CacheLookup NORMAL的实现找到.macro CheckMiss,然后调用__objc_msgSend_uncached,最后查看MethodTableLookup的实现,发现调用__class_lookupMethodAndLoadCache3方法,该方法的实现在objc-runtime-new.mm中objc-runtime-new.mm代码流程:
1、首先查看lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/)方法的实现
2、其次调用cache_getImp在方法缓存中查找方法,如果没有在方法列表中查找->getMethodNoSuper_nolock获取方法列表->search_method_list->findMethodInSortedMethodList查找方法-> 如果找到方法写入缓存log_and_fill_cache
3、如果没有找到方法就在super的方法缓存查找方法(Class curClass = cls->superclass, cache_getImp),如果找到该方法写入子类方法缓存中(log_and_fill_cache),如果没有找到则在父类方法列表中查找(getMethodNoSuper_nolock),如果找到则写入子类方法缓存中
4、如果没有找到方法的实现则会调用_class_resolveMethod(实际上是查看对象是否实现了resolveInstanceMethod),如果实现动态解析,则会触发以上的方法查找流程
5、如果没有则会调用_objc_msgForward_impcache(实际上是看对象是否实现forwardingTargetForSelector方法),触发消息转发流程// 伪代码
int __forwarding__(void *frameStackPointer, int isStret) {id receiver = *(id *)frameStackPointer;SEL sel = *(SEL *)(frameStackPointer + 8);const char *selName = sel_getName(sel);Class receiverClass = object_getClass(receiver);// 调用 forwardingTargetForSelector:if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {id forwardingTarget = [receiver forwardingTargetForSelector:sel];if (forwardingTarget && forwardingTarget != receiver) {if (isStret == 1) {int ret;objc_msgSend_stret(&ret,forwardingTarget, sel, ...);return ret;}return objc_msgSend(forwardingTarget, sel, ...);}}// 僵尸对象const char *className = class_getName(receiverClass);const char *zombiePrefix = "_NSZombie_";size_t prefixLen = strlen(zombiePrefix); // 0xaif (strncmp(className, zombiePrefix, prefixLen) == 0) {CFLog(kCFLogLevelError,@"*** -[%s %s]: message sent to deallocated instance %p",className + prefixLen,selName,receiver);}// 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocationif (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];if (methodSignature) {BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;if (signatureIsStret != isStret) {CFLog(kCFLogLevelWarning ,@"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'. Signature thinks it does%s return a struct, and compiler thinks it does%s.",selName,signatureIsStret ? "" : not,isStret ? "" : not);}if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];[receiver forwardInvocation:invocation];void *returnValue = NULL;[invocation getReturnValue:&value];return returnValue;} else {CFLog(kCFLogLevelWarning ,@"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",receiver,className);return 0;}}}SEL *registeredSel = sel_getUid(selName);// selector 是否已经在 Runtime 注册过if (sel != registeredSel) {CFLog(kCFLogLevelWarning ,@"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",sel,selName,registeredSel);} // doesNotRecognizeSelectorelse if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {[receiver doesNotRecognizeSelector:sel];}else {CFLog(kCFLogLevelWarning ,@"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",receiver,className);}// The point of no return.kill(getpid(), 9);
}
objc_msgSend消息发送流程:
1、首先从消息接收对象(receiver)的cache方法列表中查找,如果没有找到
2、再从该对象中的方法列表(class_rw_t)中查找,如果找到缓存到当前对象的cache方法列表中,如果没有找到
3、再从接收对象的父类中的cache方法列表中查找,如果找到,将方法缓存到对象的cache列表中,如果没有找到
4、再从该接收对象的父类的方法列表中查找,如果找到就缓存到该对象的cache方法列表中,如果没有找到
5、触发方法解析流程
如果是从class_rw_t中查找方法
已经排序的,二分查找
没有排序的,遍历查找
receiver通过isa指针找到receiverClass
receiverClass通过superclass指针找到superClass
objc_msgSend的动态方法解析流程:
1、判断该方法是否动态解析,如果解析就出发消息转发,如果没有
2、再调用resolveInstanceMethod: 或者resolveClassMethod:方法来动态解析
3、标记为已经动态解析然后重新走消息发送流程
objc_msgSend的消息转发流程:
1、调用forwardingTargetForSelector,如果返回值不为空,则触发消息发送流程,如果返回值为空
2、则调用methodSignatureForSelector,如果返回nil,则触发doesNotRecognizeSelector异常,如果返回值不为nil
3、则调用forwardInvocation方法(方法中自定义任何逻辑)
消息转发流程

void testIMP (void){NSLog(@"testIMP invoke");
}+ (BOOL)resolveInstanceMethod:(SEL)sel
{if (sel == @selector(test)) {NSLog(@"resolveInstanceMethod:");// 第一种。利用Runtime动态添加方法实现,返回yes,//class_addMethod([self class], @selector(test), (IMP)testIMP, "v@:");//return YES;// 第二种返回NOreturn NO;}return [super resolveInstanceMethod:sel];
}// 查看有没有其他对象处理该消息
- (id)forwardingTargetForSelector:(SEL)aSelector{if (aSelector == @selector(test)) {NSLog(@"forwardingTargetForSelector:");// 返回给其他实现了 test 方法的对象处理(在.h中没有定义test方法也可以)// Other * other = [[Other alloc] init];// return other;return nil;}return [super forwardingTargetForSelector:aSelector];
}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{if (aSelector == @selector(test)) {NSLog(@"methodSignatureForSelector:");// 返回方法签名 + 实现forwardInvocation方法处理该消息return [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
}- (void)forwardInvocation:(NSInvocation *)anInvocation
{NSLog(@"forwardInvocation:");
}
- super的本质
super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
struct objc_super2、SEL
[super test];
转换成c++的编译代码:
objc_msgSendSuper({self,class_getSuperclass(objc_getClass("SubTestNSObject"))}, sel_registerName("test"));struct objc_super {id receiver; // 消息接受者Class super_class;
};NSLog(@"self class] = %@", [self class]);
NSLog(@"super class] = %@", [super class]);
NSLog(@"self superclass] = %@", [self superclass]);
NSLog(@"super superclass] = %@", [super superclass]);class和superclass的底层实现
+ (Class)class {return self;
}
- (Class)class {return object_getClass(self);
}
+ (Class)superclass {return self->superclass;
}
- (Class)superclass {return [self class]->superclass;
}根据以上分析super的消息接受者还是当前类的实例对象,只不过super查找方法是从父类开始寻找的,所以[super class]返回的是当前实例对象的类对象
1.消息接收者仍然是子类对象
2.从父类开始查找方法的实现
isMemberOfClass和isKindOfClass的区别:
源码实现
- (BOOL)isMemberOfClass:(Class)cls {return [self class] == cls;
}- (BOOL)isKindOfClass:(Class)cls {for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {if (tcls == cls) return YES;}return NO;
}isMemberOfClass是判断self和传进来的类是否相同
isKindOfClass是判断self的类对象是否是传进来的类或子类
- 讲一下 OC 的消息机制
OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
objc_msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发
- 什么是Runtime?平时项目中有用过么?
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
平时编写的OC代码,底层都是转换成了Runtime API进行调用
具体应用
利用关联对象(AssociatedObject)给分类添加属性
遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
交换方法实现(交换系统的方法)
利用消息转发机制解决方法找不到的异常问题
- Runtime常用API
类:
动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
销毁一个类
void objc_disposeClassPair(Class cls)
获取isa指向的Class
Class object_getClass(id obj)
设置isa指向的Class
Class object_setClass(id obj, Class cls)
判断一个OC对象是否为Class
BOOL object_isClass(id obj)
判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)
获取父类
Class class_getSuperclass(Class cls)成员变量:
获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)属性:
获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)
动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)
获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)方法:
获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) 拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
