OC类的底层探索(随笔)

2020-06-09  本文已影响0人  三国韩信
image.png
窥探App启动到main函数前的过程(通过符号断点来看调用栈)

_dyld_start => libSystem_initializer => libdispatch_init => _objc_init 在_objc_init中会对类进行初始化。

BEPerson* p = [BEPerson alloc]init];
那么对象的创建中,alloc做了啥,init又做了啥呢?

image.png
image.png
苹果开源的部分源码下载网站(https://opensource.apple.com/tarballs/
从苹果开源的libobjc.A.dylib的动态库的源码上看,init方法啥都没做,就是返回了自己。其作用就是给开发者初始化类的时候去复写这个方法。
那么很显然类的初始化工作是alloc这个函数来做的,那么alloc做了啥呢?
从源码上看来揭晓alloc函数调用的流程:
alloc函数的流程图.png
注:其中分为了有重写了alloc或allocWithZone方法的情况和没有重写的情况
关于class_getInstanceSize 和malloc_size的区别(内存对齐)

class_getInstanceSize:可以理解为是获取了ivar(成员变量)所占据的空间(和属性排列无关),且该函数里有做了一次8字节的对齐。所以该函数返回的结果肯定是8的倍数。(class_getInstanceSize这个函数没有最小16字节的硬编码)
malloc_size:是系统为这个类所开辟的真正的空间,那么它存在的内存对齐的情况:
(内存中一个对象真正的内存大小,是指malloc_size返回的值。这种就有内存优化了)
1、oc的硬编码里规定了<16的话,开辟的空间就为16
2、内存对齐,结果须为16的倍数。calloc里面做的。具体代码在malloc源码里,做了一次>>4再<<4(先左移四位再右移4位)
3、属性的位置排列ios系统是做了优化的(malloc_size),会尽可能的利用内存,不一定是按顺序排序属性。

isa是一个联合体union,里面加了个结构体位域结构。一个isa占8个字节,总共64位,其中位域的每一位都有其含义的。

(这里通过位域而不会属性的形式来表达各种信息是为了节省空间、优化内存的结构等等作用)
uintptr_t nonpointer : 1; /// 后面的1表示占了1位
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19

每一位代表的意思如下:
通过lldb来探索类结构
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             
    class_data_bits_t bits;   

    class_rw_t *data() { 
        return bits.data();
    }
    .......//其他的方法
}
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
    ......//其他的方法
}
image.png
image.png
image.png
通过runtimeAPI来获取类结构的内容
#import <objc/runtime.h>
void testObjc_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];
        NSLog(@"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)];
        //获取属性值
        NSLog(@"class_copyProperiesList:%@",propertyName);
    }
    free(properties);
}

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

void testInstanceMethod_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));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

void testClassMethod_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)); // ?
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

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

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        
        testObjc_copyIvar_copyProperies(pClass);
        testObjc_copyMethodList(pClass);

        testInstanceMethod_classToMetaclass(pClass);
        testClassMethod_classToMetaclass(pClass);
        
        NSLog(@"%@ - %p",person,pClass);
    }
    return 0;
}
最后我还想说

我们在探索oc底层的时候,免不了要去看源码,可是苹果是封闭的生态系统,它不是开源的,那么我们怎么去研究它呢?基于分享的态度,苹果baba还是开源了部分代码的,我们可以在苹果官方去找到这些源码。(但仅仅只是部分,像UIKit,Foundation等核心的库也是没有开源的)
那对于没有开源的,我们怎么去研究学习呢?
这里我介绍一个叫GnuStep的项目,它是GNU计划下的一个项目,它是将苹果的Cocoa的oc库重新开源实现了一遍(就是GNU这个组织自己根据对苹果iOS的探究,自己实现了一遍苹果Cocoa的OC库。换句话说某个大神自己凭经验实现的源码,嘻嘻嘻!)。虽然GnuStep不是苹果官方的源码,但还是具有一定的参考价值。

GNUStep.png
上一篇 下一篇

猜你喜欢

热点阅读