Under the analysis of the principle of 5-class of iOS bottom layer exploration

[TOC]

Summarizing the isa bitmap This section reveals why there is a metaclass? ?

image.pngSummarize and think about the bitmap of isa-a: Object -> Class -> Metaclass b: Inheritance chain LGPerson *p; LGTeacher *t ; t:p Inheritance only comes from classes with inheritance relationship objects without c: LGTeacher -> LGPerson -> NSObject -> nil from nothing d: Does the metaclass also have isa -> root metaclass isa -> root metaclass (the metaclass of NSObject) e: Does the metaclass also have an inheritance chain metaclass class inherits LGPerson's metaclass -> root metaclass- > NSObject -> nil This section explains why there is a metaclass? ?

Class structure memory translation calss_data_bit offset 0x20 32 bytes pits are not printed and debugged in the objc source code project

Two forms of objc_setProperty and memory translation assignment

image.png

// objc_object VS objc_class objc_class : objc_object // person VS NSObject person is essentially objc_object structure, NSObject is objc_class structure // id is objc_object * pointer // class is objc_class * pointer

WWDC changes about runtime

class structure ( developer.apple.com/videos/play… )

class's memory ro data

image.pngfirstSubclass = nil There is no value to continue operation, operate the subclass LGTeacher.class image.pngthen firstSubclass = LGTeacher has class loading lazy loading

image.pngAt this point, add member variables and methods to the class to continue the operation

image.png

image.pngThe member variable prints the ro( ) method in ro to get the data of type 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!
复制代码

Print result analysis lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158 Instance method! ! ! pClass sayHello method has metaClass sayHello method has no pClass sayHappy method has no metaClass sayHappy method has (instance method) lgClassMethod_classToMetaclass - 0x0-0x0-0x100008158-0x100008158 class method! ! ! pClass sayHello method has no metaClass sayHello method has no pClass sayHappy method has metaClass sayHappy method has (the printing of lgInstanceMethod_classToMetaclass above has proved that class methods are stored as object methods in the metaclass, why can the class methods still be printed in the metaclass??? )

image.png

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

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

复制代码

There are no class methods at the bottom, only object methods

lgIMP_classToMetaclass 0x100003ad0-0x7fff6d5e3580-0x7fff6d5e3580-0x100003b10 has a value Why does the sayHello method imp of the metaclass have a value? Why does the class sayHappy method imp have value? Check the reason for the source _objc_msgForward pointer

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 interview questions

image.png

isKindOfClass added

image.pngView source code

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

Open assembly debugging and find that isKindOfClass does not call objc_opt_isKindOfClass like the source code.

image.pngto be explored. . .

Guess you like

Origin juejin.im/post/6981806311147044901