iOS底层iOS 底层探索之路

iOS 底层探索:类中方法的举例分析

2020-09-16  本文已影响0人  欧德尔丶胡

文集:iOS 底层探索之路

前言

准备工作

一 、例子1:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif

@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end

@implementation LGPerson

- (void)sayHello{
    LGLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
    LGLog(@"LGPerson say : Happy!!!");
}
@end

void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[I];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        LGLog(@"打印1-Method, name: %@", key);
    }
    free(methods);
}

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello)); // 1
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello)); // 0

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy)); // 0
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));// 1
    
    LGLog(@"打印2 - %s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"打印3 -%s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    LGLog(@"打印4:%s -%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool 
{
        LGPerson *person = [LGPerson alloc];
        Class pClass  = object_getClass(person);
       //执行1        
        lgObjc_copyMethodList(pClass);
        //执行2
        lgInstanceMethod_classToMetaclass(pClass);
        //执行3
        lgClassMethod_classToMetaclass(pClass);
       //执行4
        lgIMP_classToMetaclass(pClass);
    }
    return 0;
}

问题:打印输出什么?

方法注释:

分析:

LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
pClass :这里是person对象的类LGPerson

-(void)sayHello; //对象方法 存在类中
+(void)sayHappy; //类方法 存在元类中

执行1

void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 2;
//获取类中方法列表
    Method *methods = class_copyMethodList(pClass, &count);
//&count是count的指针 传进class_copyMethodList方法中 只找到一个对象方法赋值 count = 1
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[I];
        //类中获取方法名  只能是对象方法sayHello
        NSString *key = NSStringFromSelector(method_getName(method));
        //所以只打印sayHello
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

结果: sayHello

执行2

void lgInstanceMethod_classToMetaclass(Class pClass){
 //class_getName 获得到的className 是LGPerson类
    const char *className = class_getName(pClass);
 //objc_getMetaClass 获得到的metaClass 是LGPerson的元类
    Class metaClass = objc_getMetaClass(className);
//获取到类的实例方法sayHello的对象是存在的 所以打印sayHello方法的地址
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
//获取到元类的实例方法sayHello的对象是不存在的 所以打印0x0
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
//获取到类的类方法sayHello的对象是不存在的 所以打印0x0
    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
//获取到元类的类方法sayHello的对象是存在的 所以打印sayHappy方法地址
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

结果:
lgInstanceMethod_classToMetaclass
-0x1000031b0
-0x0
-0x0
-0x100003148

执行3

void lgClassMethod_classToMetaclass(Class pClass){
   
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
//`class_getClassMethod`:获取到类的类方法;
//LGPerson类中获取不到类方法所以打印 0x0
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
//元类中不存在实例方法所以打印 0x0
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
//LGPerson类中获取类方法所以打印sayHappy的地址
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
//元类中获取类方法所以打印sayHappy的地址
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"打印3 -%s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

源码中分析:class_getClassMethod

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
//真实传入的是 cls的元类 ,元类对象中的方法是类方法
    return class_getInstanceMethod(cls->getMeta(), sel);
}

结果:
lgClassMethod_classToMetaclass
-0x0
-0x0
-0x100003148
-0x100003148

执行3

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
// LGPerson类中可以获取实例方法的实现 
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
// 通过源码分析如下,以上全部能找到方法的imp
    LGLog(@"打印4:%s -%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}

源码中分析:class_getMethodImplementation

IMP object_getMethodImplementation(id obj, SEL name)
{
//这里有个递归 如果通过传入的obj 和 name在class_getMethodImplementation方法中找不到imp ,
//就调用obj->getIsa() 根据isa的指向去寻找obj的元类,根元类,根根元类,根根元类自己。
//_objc_msgForward  是存在汇编里的 类的所有信息都存在汇编中 
//而汇编中不区分类方法和对象方法,所以在汇编中总能找到对应的方法imp,
//因此调用这个方法必然能找到对应的imp。
// 所以以上全部能找到方法的imp
    Class cls = (obj ? obj->getIsa() : nil);
    return class_getMethodImplementation(cls, name);
}
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;
    if (!cls  ||  !sel) return nil;
    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
    if (!imp) {
        return _objc_msgForward;
    }
    return imp;
}

OBJC_EXPORT void
_objc_msgForward(void /* id receiver, SEL sel, ... */ ) 
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

结果:
lgIMP_classToMetaclass
-0x100000e10
-0x1002c2440
-0x1002c2440
-0x100000de0

提出疑问:为什么 imp2 imp3打印的结果都是 -0x7fff6c09c580?
通过源码配合lldb 调试分析如下:


总结:lgIMP_classToMetaclass在系统底层通过_objc_msgForward做了消息转发的处理。

这个例子的目的:

  • 1.考察实例方法和类方法的存在
  • 2.考察isa流程图

二 、例子2:isKindOfClass 和 isMemberOfClass

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface LGPerson : NSObject
@end

@implementation LGPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
        BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
        BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
        NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

        BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
        BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
        BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
        BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
        NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
    }
    return 0;
}

问题:打印输出什么?

源码解析:

类方法调用:查看isa流程 如果isa 指向cls 则为真

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

对象方法调用:查看[self class],如果两个类是一样的则为真

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

类方法调用:查看isa流程,从类到元类,根元类的递归对比, 如果isa 指向cls 则为真

+ (BOOL)isKindOfClass:(Class)cls {
    // 类 vs 元类
    // 根元类 vs NSObject
    // NSObject vs NSObject
    // LGPerson vs 元类 (根元类) (NSObject)
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

对象方法调用:如果 [self class] 是cls 的子类或者本类就为真

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

方法注释:

分析:

[NSObject class] 返回self 也就是 NSObject类
[LGPerson class] 返回self 也就是 LGPerson类
[NSObject alloc] 返回的是<NSObject: 0x10101c700> 对象
[LGPerson alloc] 返回的是<LGPerson: 0x10060b260>对象

注:
-1、2、3、4 是属于类方法的判断

分析1:

BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];

分析2:

 BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 

分析3:

 BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; 

分析4:

  BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];   

分析5:

 BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; 

注:

分析5、6、7、8 :

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];   
    BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       
    BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];      

结果

1 0 0 0 1 1 1 1

一顿操作猛如虎!打起脸来啪啪啪!!

惊呆了。
进入源码断点:

+ (BOOL)isKindOfClass:(Class)cls {
    // 类 vs 元类
    // 根元类 vs NSObject
    // NSObject vs NSObject
    // LGPerson vs 元类 (根元类) (NSObject)
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}


- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

这两个方法根本不走!虽然分析是对的。但是isKindOfClass这个方法根本不执行
继续查看源码分析

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

objc_opt_isKindOfClass 这个方法 里面执行方法综合了isKindOfClass的对象方法和类方法。这是底层对isKindOfClass 的优化处理。

三 、总结:

读懂源码的同时需要手动断点调试,搞懂方法的本质,就很好理解比较。

上一篇 下一篇

猜你喜欢

热点阅读