iOS

iOS底层探索之类的结构(下)

2021-06-23  本文已影响0人  俊而不逊
类的结构探索分析.png

在之前的几篇博客里面,已经介绍了,类的底层结构,还有isa的走位流程,元类的继承链,对象方法,类方法的存放位置,以及如何通过lldb获取成员和属性、对象方法,类方法。

iOS底层探索之类的结构(上)

iOS底层探索之类的结构(中)

本篇博客主要做一些补充和扩展。

成员变量和属性

开发年限比较久的iOS程序员,都知道在iOS5之前,经常看到一个大括号里面定义了成员变量,同时用了@property声明,而且还在@implementation中使用@synthesize方法。

其实,发生这种状况根本原因是苹果将默认编译器从GCC转换为LLVM(low level virtual machine),才不再需要为属性声明实例变量了。

在没有更改之前,属性的正常写法需要成员变量+ @property + @synthesize成员变量三个步骤。 更换为LLVM之后,编译器在编译过程中发现没有新的实例变量后,就会生成一个下划线开头的实例变量。因此现在我们不必在声明一个实例变量。

现在@property声明的属性不仅仅默认给我们生成一个_类型的成员变量,同时也会生成setter/getter方法。

类信息
clang源码

从以上clang底层源码也可以验证,@property声明的属性默认会生成一个_类型的成员变量,同时也会生成setter/getter方法。

objc_setProperty

但是我们从上面的底层源码发现了一个问题,有的set方法里面是通过objc_setProperty获取属性,有的是通过内存的平移。这是什么原因了呢?通过对比不同属性设置带有copy和不带有copy的底层源码分析,可以找到原因是copy这个设置的问题!那么为验证这个结论,我们去底层看看!

LLVM

那我们去llvm源码里面看看,objc_setProperty的实现逻辑,你怎么就知道去llvm不是去objc源码看呢?请看下图分析

在这里插入图片描述
首先在底层也就是下层,苹果的源码是固定的,是不会改变的(这里的不变是指不会根据你代码来改变,因为底层逻辑已经写好了)。在上层,我们程序开发人员,会写很多中属性,那么就会有各种各种的奇奇怪怪的代码出现,就有各种的set形式的代码,比如setName、setAge、setXX。。。等等。

这对编译器来说,只是_cmd的方法名字不一样,那就需要一个中间层来把这些奇奇怪怪的,做一下处理,以便于和底层去交互,因为底层逻辑已经封装好,不可能动态的改变的。那我们去研究objc_setProperty就不可能在运行时了,只能在编译时了,所以我们去就llvm里面看看!

那上层和objc_setProperty之间有着怎样的联系呢?

getGetPropertyFn
我们在llvm里面找到了getGetPropertyFn这个函数,是获取创建的CreateRuntimeFunction,里面有objc_setProperty这个参数。那么为什么创建呢?继续往下看看,在哪里调用了
getGetPropertyFn()
既然调用了,肯定是有条件的,是真的是是因为propertycopy吗?我们找到条件,就可以反推知道objc_setProperty的作用了。继续往下看
PropertyImplStrategy
看看上面的代码有没有发现什么?这有个属性imp的策略PropertyImplStrategy,那么再继续往下找
PropertyImplStrategy
看到没有,如果IsCopy为真就Kind = GetSetProperty,这也就验证了博客开篇的猜想,只有属性里面用copy修饰的,才会有objc_setProperty

代码获取类的信息

iOS底层探索之类的结构(上)
iOS底层探索之类的结构(中)
在之前的博客里面,我们已经知道了,可以在控制台通过lldb获取类的信息,那么现在我们通过代码来获取下。

class_copyMethodList

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(@"Method, name: %@", key);
    }
    free(methods);
}

class_getInstanceMethod

//获取实例方法
void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

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

class_getClassMethod

//获取类方法
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));

//    - (void)sayHello;
//    + (void)sayHappy;
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

class_getMethodImplementation

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));// 0
    // sel -> imp 方法的查找流程 imp_farw
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);

}

代码测试验证


代码测试验证

IMP,SEL

扩展

我们已经学了这么多类的相关的知识了,那么我们看看下面这个面试题

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
    BOOL re3 = [(id)[JPPerson class] isKindOfClass:[JPPerson class]];    
    BOOL re4 = [(id)[JPPerson class] isMemberOfClass:[JPPerson class]];    
 
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n,%hhd,%hhd",re1,re2,re3,re4,re9,re10);

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

代码运行结果


代码运行结果

+ isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- isKindOfClass

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

+ isMemberOfClass

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

- isMemberOfClass

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

分析

更多内容持续更新

🌹 请动动你的小手,点个赞👍🌹

🌹 喜欢的可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我,哈哈😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

上一篇下一篇

猜你喜欢

热点阅读