iOS底层探索5-类的原理分析下

273 阅读7分钟

[TOC]

总结isa走位图 本节揭晓 为什么要有元类??

image.png 总结思考 isa-的走位图 a: 对象->类->元类 b: 继承链LGPerson *p; LGTeacher *t ; t:p 继承只来自于类 类有继承关系 对象没有 c: LGTeacher -> LGPerson -> NSObject -> nil 无中生有 d: 元类是否也有isa -> 根元类isa -> 根元类(NSObject的元类) e: 元类是否也有继承链 元类 类 继承 LGPerson的元类 -> 根元类 -> NSObject -> nil 本节揭晓 为什么要有元类??

类的结构 内存平移 calss_data_bit 偏移0x20 32字节 坑点 没在objc源码工程中打印调试

objc_setProperty 和 内存平移赋值 两种形式

image.png

// objc_object VS objc_class objc_class : objc_object // person VS NSObject person本质是objc_object结构体 ,NSObject是objc_class结构体 // id 是 objc_object * 指针 // class 是objc_class * 指针

WWDC关于runtime的变化

类的结构 (developer.apple.com/videos/play…)

类的内存ro数据

image.png firstSubclass = nil 没有值 继续操作,操作一下子类LGTeacher.class image.png 这时 firstSubclass = LGTeacher 有了 类的加载 懒加载

image.png 此时给类添加成员变量和方法 继续操作

image.png

image.png 成员变量在ro里面 打印 ro( )方法,得到class_ro_t类型的数据

image.png 继续打印成员变量 ivars 可以看到count=3 有三个成员变量 hobby _age _name 下面探索 成员变量 和 属性 的区别 再下面探索 类方法

成员变量和属性以及编码

成员变量、 实例变量、 属性

image.png

@interface LGPerson : NSObject
{
    // STRING   int  double  float char bool
    NSString *hobby; // 字符串  成员变量
    int a;
    NSObject *objc;  // 结构体   实例变量
}

@property (nonatomic, copy) NSString *nickName;//属性
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;




@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;

@end

成员变量、 实例变量、 属性 区别: 属性 = 待下划线的成员变量 + setter + getter方法 实例变量 : 特殊的成员变量(类的实例化)

image.png clang 操作之后 属性被优化掉 生成对应的 _nickName 成员变量,与成员变量的区别是 属性还会生成相应的set、get方法

image.png 同时通过对比发现 @property (nonatomic, copy) NSString *nickName; @property (nonatomic, strong) NSString *name; 这两个属性的set方法不一样

为什么会有 objc_setProperty 和 内存平移赋值 两种形式??

image.png 还有这些编码的意思

{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
@16@0:8
1: @ : id
2: 16 : 所占用的内存
3: @ : id 参数
4: 0 : 从0号位置开始
5: : : SEL
6: 8 : 从8号位置开始

{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
v24@0:8@16
1: v void
2: 24 参数所占用内存
3: @ id 参数
4: 0 : 从0号位置开始
5: : : SEL
6: 8 : 从8号位置开始
7: @ id 传入的参数
8: 16 : 从16号位置开始

例如:ivar_getTypeEncoding()

image.png

进入Documentation搜索

image.png Type Encodings (developer.apple.com/library/arc…)

image.png

image.png

另外可以通过以下方法打印:

image.png

#pragma mark - 各种类型编码
void lgTypes(void){
    NSLog(@"char --> %s",@encode(char));
    NSLog(@"int --> %s",@encode(int));
    NSLog(@"short --> %s",@encode(short));
    NSLog(@"long --> %s",@encode(long));
    NSLog(@"long long --> %s",@encode(long long));
    NSLog(@"unsigned char --> %s",@encode(unsigned char));
    NSLog(@"unsigned int --> %s",@encode(unsigned int));
    NSLog(@"unsigned short --> %s",@encode(unsigned short));
    NSLog(@"unsigned long --> %s",@encode(unsigned long long));
    NSLog(@"float --> %s",@encode(float));
    NSLog(@"bool --> %s",@encode(bool));
    NSLog(@"void --> %s",@encode(void));
    NSLog(@"char * --> %s",@encode(char *));
    NSLog(@"id --> %s",@encode(id));
    NSLog(@"Class --> %s",@encode(Class));
    NSLog(@"SEL --> %s",@encode(SEL));
    int array[] = {1,2,3};
    NSLog(@"int[] --> %s",@encode(typeof(array)));
    typedef struct person{
        char *name;
        int age;
    }Person;
    NSLog(@"struct --> %s",@encode(Person));
    
    typedef union union_type{
        char *name;
        int a;
    }Union;
    NSLog(@"union --> %s",@encode(Union));

    int a = 2;
    int *b = {&a};
    NSLog(@"int[] --> %s",@encode(typeof(b)));
}

至于成员变量和属性的区别 可以通过下面方法打印获取

image.png

#import <objc/runtime.h>

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

void lgObjc_copyIvar_copyProperies(Class pClass){
    
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //获取实例变量名
        const char*cName = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:cName];
        LGLog(@"class_copyIvarList:%@",ivarName);
    }
    free(ivars);

    unsigned int pCount = 0;
    objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
    for (unsigned int i=0; i < pCount; i++) {
        objc_property_t const property = properties[i];
        //获取属性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        //获取属性值
        LGLog(@"class_copyProperiesList:%@",propertyName);
    }
    free(properties);
}

调用方法

image.png

LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
lgObjc_copyIvar_copyProperies(pClass);

class_copyIvarList:hobby
class_copyIvarList:a
class_copyIvarList:objc
class_copyIvarList:_nickName
class_copyIvarList:_acnickName
class_copyIvarList:_nnickName
class_copyIvarList:_anickName
class_copyIvarList:_name
class_copyIvarList:_aname
class_copyProperiesList:nickName
class_copyProperiesList:acnickName
class_copyProperiesList:nnickName
class_copyProperiesList:anickName
class_copyProperiesList:name
class_copyProperiesList:aname

setter方法的底层原理上

编译时代码已经确定了 不好绑定 llvm查看源码 在 运行时 进行 sel -> imp(objc_setProperty)

setter方法的底层原理下

##为什么会有 objc_setProperty 和 内存平移赋值 两种形式?? objc_setProperty 通过查阅llvm 发现一些列调用过程 copy的情况才会调用 objc_setProperty

可以继续进行 objc_getProperty 原理拓展

为何objc_getProperty 要查llvm 而不是看objc源码 runtime源码中objc_getProperty 和setName 没有关联 setName(imp) 编译时去绑定imp 如果setName 编译时 没有和imp绑定 ,则 运行时要先调用相应方法 有可能定位方法错误(machoView查看)

类方法的存储

(# 类的内存ro数据) 继续最初的class_rw_t的遍历

image.png

image.png

image.png

image.png 可以发现类方法 + (void)sayNB; 没有打印出来,类方法也有对应的实现,但就是没有打印出来 通过machoview可以看到类方法是有的

image.png

以上打印发现 - (void)saySomeThing; 实例方法、对象方法在 类里面(节省内存) (+ -) 方法在底层C C++统称函数,如果+-方法同名如何处理

- (void)saySomeThing;
+ (void)saySomeThing;

所以 元类由此诞生 类方法存放在元类中 6 66 66666 验证一下

image.png

image.png

下面探索不用llvm,用另一种方式验证

类方法的存储的API方式解析

通过runtime提供的api来验证

#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import "LGTeacher.h"
#import <objc/runtime.h>

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

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);
}

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);
}

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);
}

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__);

}

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        // LGTeacher *teacher = [LGTeacher alloc];
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        NSLog(@"*************");
        lgObjc_copyMethodList(metaClass);

        lgInstanceMethod_classToMetaclass(pClass);
        lgClassMethod_classToMetaclass(pClass);
        lgIMP_classToMetaclass(pClass);

        NSLog(@"Hello, World!");
    }
    return 0;
}

image.png

image.png

image.png

image.png

Method, name: sayHello
Method, name: obj
Method, name: setObj:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:
2021-06-25 18:22:41.130738+0800 002-类方法归属分析[31216:2542102] *************
Method, name: sayHappy
lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158
lgClassMethod_classToMetaclass - 0x0-0x0-0x100008158-0x100008158
2021-06-25 18:22:45.041969+0800 002-类方法归属分析[31216:2542102] 0x100003ad0-0x7fff6d5e3580-0x7fff6d5e3580-0x100003b10
2021-06-25 18:22:45.042185+0800 002-类方法归属分析[31216:2542102] lgIMP_classToMetaclass
2021-06-25 18:22:46.077799+0800 002-类方法归属分析[31216:2542102] Hello, World!

打印结果分析 lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158 实例方法!!! pClass sayHello方法有 metaClass sayHello方法没有 pClass sayHappy方法没有 metaClass sayHappy方法有(实例方法) lgClassMethod_classToMetaclass - 0x0-0x0-0x100008158-0x100008158 类方法!!! pClass sayHello方法没有 metaClass sayHello方法没有 pClass sayHappy方法有 metaClass sayHappy方法有(上面lgInstanceMethod_classToMetaclass的打印已经证明了类方法在元类中以对象方法存储,这里为何在元类中还是能打印出类方法???)

image.png

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

在底层 没有类方法 只有对象方法

lgIMP_classToMetaclass 0x100003ad0-0x7fff6d5e3580-0x7fff6d5e3580-0x100003b10 都有值 为何 元类的sayHello方法 imp有值? 为何 类的sayHappy方法imp有值? 查源码 _objc_msgForward 指针的原因

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));

image.png

isKindOfClass面试题

image.png

isKindOfClass补充

image.png 查看源码

image.png

isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}
//BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //1
//BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //0
cls元类 ->  cls元类的父类  
判断是否相等
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}
//BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //1
//BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //1

isMemberOfClass

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
//BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //0
//BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //0
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
//BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //1
//BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //1

image.png

打开汇编调试 发现isKindOfClass调用的不像源码的一样 调用的是objc_opt_isKindOfClass

image.png 待探索。。。