美文网首页
iOS-OC底层12:类的加载

iOS-OC底层12:类的加载

作者: MonKey_Money | 来源:发表于2020-10-16 23:50 被阅读0次

前沿

我们已经了解了dyly和objc关联,接下来我们重点研究一下read_images下和我们息息相关的类的处理

read_images下的if (!doneOnce)当第一次进入这个方法时

1.DisableNonpointerIsa = true;当swift代码过旧,app过旧还有app包含__DATA,__objc_rawisa部分时
2.gdb_objc_realized_classes:初始化实现class的表
下面我们着重关注对class的处理
我们先做准备类

@interface MyPerson : NSObject

@property (nonatomic, copy) NSString *my_name;
@property (nonatomic, assign) int my_age;

-(void)myInstanceMethod2;

-(void)myInstanceMethod1;

-(void)myInstanceMethod3;

+(void)myClassMethod;

@end
@implementation MyPerson
+(void)load {
}
-(void)myInstanceMethod2 {
    NSLog(@"%s",__func__);
}

-(void)myInstanceMethod1{
    NSLog(@"%s",__func__);
}

-(void)myInstanceMethod3{
    NSLog(@"%s",__func__);
}

+(void)myClassMethod{
    NSLog(@"%s",__func__);
}
@end

readClass

我们在readClass做一些出来,来帮助我们针对性的研究我们自定义的类,方便我们打断点进行调试

    const char *mangledName  = cls->mangledName();
    const char *PersonName = "MyPerson";

    // printf("诶唷: %s \n ",mangledName);

    if (strcmp(mangledName, PersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data();
        printf("readClass: 这个是我要研究的 %s \n",PersonName);
    }

在我们的if判断内打断点,防止系统的类干扰我们研究,我们通过单步调试来到了

      addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);

addNamedClass

在跳用addNamedClass之前我们p cls的出(Class) $3 = 0x0000000100008268

static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked();
    Class old;
    if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
        inform_duplicate(name, old, cls);
        addNonMetaClass(cls);
    } else {
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
}

addNamedClass主要是把类名加到read_images中第一次进入初始化的表中,经过
addNamedClass之后我们p cls 的结果是(Class) $4 = MyPerson,我们可以得出addNamedClass是让cls的地址和类名关联起来。

addClassTableEntry

通过打断可知addClassTableEntry对MyPerson类没做任何处理

解惑

我们在read_images方法中的关于non-lazy进行代码处理

            Class cls = remapClass(classlist[i]);
            
            const char *mangledName  = cls->mangledName();
            const char *PersonName = "MyPerson";
           
            if (strcmp(mangledName, PersonName) == 0) {
                auto kc_ro = (const class_ro_t *)cls->data();
                printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",PersonName);
            }

通过在if打断点,程序没有走到if判断中。我们貌似对整个read_images中关于类的都研究完毕,但是我们发现这个注释关于load的方法,所以我们大胆尝试对MyPerson增加load方法,奇迹出现if中走到了

懒加载类与非懒加载类

官方在对类进行处理的时候, 为了提高对类处理的效率以及性能, 就对类进行了识别, 当类需要使用的时候, 系统才会对类进行实现. 如果没有使用就不会实现. 当需要实现才进行加载的类就被称为懒加载类. 反之无论是否使用到这个类, 都对这个类进行加载的类就被称为非懒加载类.
懒加载类和非懒加载类的区别就是是否实现load方法,实现load的方法是非懒加载类,没有实现load方法是懒加载类,另一个原因可能是在load_image中有调用load方法,如果类没有被加载完备,调用load方法可能会有问题。赖加载类在lookupImp是有realizeClass


image.png

realizeClassWithoutSwift

在此函数中我们也进行针对性的处理

    const char *mangledName  = cls->mangledName();
    const char *PersonName = "MyPerson";

    if (strcmp(mangledName, PersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data();
        auto kc_isMeta = kc_ro->flags & RO_META;
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,PersonName);
        }
    }

1.从cls中读ro数据 ,(ro数据是编译的时候写入到镜像文件,从镜像文件中读到的ro)。然后把ro设置到rw中,然后rw设置到cls内
2.写入此类的索引chooseClassArrayIndex

  1. realizeClassWithoutSwift父类和元类,递归
    4.设置其他的信息如isa,NonpointerIsa等等
    5.设置父类和元类 cls->superclass = supercls; cls->initClassIsa(metacls);
    6.methodizeClass方法化现在的类
    在对rw设值之前和之后打印cls
 x/4gx cls
0x100008280: 0x0000000100008258 0x000000010034c140
0x100008290: 0x0000000100346440 0x0000000000000000
 cls->setData(rw);之后
x/4gx cls
0x100008280: 0x0000000100008258 0x000000010034c140
0x100008290: 0x0000000100346440 0x0000000000000000

两次中的bit值为什么都是空的呢 0x0000000000000000,在后面我们再解答这个问题

methodizeClass

1.prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)中的addedLists为二维数组。fixupMethodList是对方法的排序,fixupMethodList中的排序是按照selector 的地址进行排序的
2.if (rwe) rwe->methods.attachLists(&list, 1); rwe为null
rwe值什么情况才有会有,rwe的初始化是cls->data()->extAllocIfNeeded(),全局搜索可知关联分类(attachCategories),类设置版本号(class_setVersion),增加方法(addMethod),增加协议(class_addProtocol),增加属性(_class_addProperty)

添加category后的流程

在main文件中添加MyPerson的分类

@interface MyPerson (LG)
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod3;
- (void)cate_instanceMethod2;
+ (void)cate_sayClassMethod;
@end
@implementation MyPerson (LG)
+ (void)load {
    
}
- (void)cate_instanceMethod1{
    NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod3{
    NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
    NSLog(@"%s",__func__);
}
+ (void)cate_sayClassMethod{
    NSLog(@"%s",__func__);
}
@end

探索分类的本质,通过clang -rewrite-objc main.m -o main2.cpp

struct _category_t {
    const char *name;
    struct _class_t *cls;
    const struct _method_list_t *instance_methods;
    const struct _method_list_t *class_methods;
    const struct _protocol_list_t *protocols;
    const struct _prop_list_t *properties;
};
static struct _category_t _OBJC_$_CATEGORY_MyPerson_$_LG __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "MyPerson", //name 
    0, // &OBJC_CLASS_$_MyPerson, //cls
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyPerson_$_LG, //instance_methods
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_MyPerson_$_LG, //class_methods
    0, //protocols
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyPerson_$_LG, //properties
};

分类中添加的属性不会有set和get方法,我们可以通过runtime的关联实现.

methodizeClass有对分类的处理,objc::unattachedCategories.attachToClass
attachToClass内有一个方法attachCategories,这个貌似是对分类的处理。但是在attachToClass中没有捕捉到对attachCategories的调用。但是我们可以在attachCategories做针对性分析

  if (strcmp(mangledName, LGPersonName) == 0) {
        bool kc_isMeta = cls->isMetaClass();
        auto kc_rw = cls->data();
        auto kc_ro = kc_rw->ro();
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
        }
    }

在写入的代码里打断点,看堆栈信息,


image.png

attachCategories

1.对rwe进行初始化auto rwe = cls->data()->extAllocIfNeeded();

相关文章

  • iOS-OC底层12:类的加载

    前沿 我们已经了解了dyly和objc关联[https://www.jianshu.com/p/b32beb211...

  • Java中的类加载器

    常见类加载器 BootstrapClassLoader 最底层的 启动类加载器,负责加载/l...

  • java类加载器(用户自定义类加载器实现)

    java类加载器主要分为如下几种: jvm提供的类加载器 根类加载器:底层实现,主要加载java核心类库(如:ja...

  • 02-类加载器及双亲委托机制

    Java虚拟机自带的类加载器 BootStrap ClassLoader(启动/根类加载器) 是由底层虚拟机来加载...

  • JAVA微服务技术栈相关 知识点梳理

    JVM jvm作为底层支撑,需要了解的有: 其中重点为类加载器与内存模型。类加载器中,主要自定义类加载器,加载自定...

  • Java类加载

    本篇笔记的目标是理解类加载器的架构,学会实现类加载器并理解热替换的底层原理。 什么是类加载 类从被加载到虚拟机内存...

  • OC底层原理14 - 类的加载之分类

    类的懒加载和非懒加载 在OC底层原理13 - 类的加载过程[https://www.jianshu.com/p/8...

  • JVM底层类加载

    Klass模型 Java中的每个类,在JVM中都有对应的Klass类实例与之对应,储存类的元信息如:常量池、属性信...

  • 类加载器

    类加载器按照层次,从顶层到底层,分为以下三种 启动类加载器(Bootstrap ClassLoader)(c++实...

  • iOS底层-类的加载

    在分析dyld和objc关联的时候,我们发现_read_images方法中有读取类的方法也有实现类的方法,我们这篇...

网友评论

      本文标题:iOS-OC底层12:类的加载

      本文链接:https://www.haomeiwen.com/subject/mfyopktx.html