OC底层原理

类和分类的加载

2020-02-27  本文已影响0人  只写Bug程序猿
懒加载类和非懒加载类

类的加载 篇章里边 我们说了加载了类,分类,协议等等一些事情,注释我们可以看出来这里加载的是非懒加载的类.那么怎么区分懒加载类和非懒加载类呢

  1. 非懒加载类:实现了+(void)load()方法
  2. 懒加载类 : 没有实现+(void)load()方法,并且非懒加载类中没有任何的引用或者使用
    比如
//person中为实现+(void)load()方法
@implementation LGPerson
- (void)saySomething{
    NSLog(@"%s",__func__);
}
+ (void)sayNB{
    NSLog(@"%s",__func__);
}
@end

@interface LGStudent : LGPerson
@property (nonatomic, strong) LGTeacher *teacher;
@end
@implementation LGStudent
static LGTeacher * te;
//teacher为非懒加载
+(void)load
{
    NSLog(@"%s",__func__);
}
@end

此时studentperson都为非懒加载,因为student继承了person

  1. 非懒加载类加载
    是在read_images时加载的.那么懒加载类呢
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
//简化后的代码
// 1:第一次进来 - 开始创建表
    // gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
    // allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
    if (!doneOnce) {
           doneOnce = YES;
        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        int namedClassesSize =
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        
        allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
    }
    
    // 2:类处理
    for (i = 0; i < count; i++) {
      Class cls = (Class)classlist[I];
      Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    }
    
    // 3: 方法编号处理
    for (EACH_HEADER) {
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
          const char *name = sel_cname(sels[i]);
          sels[i] = sel_registerNameNoLock(name, isBundle);
        }
    }

    // 4: 协议处理
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        NXMapTable *protocol_map = protocols();
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map,
                         isPreoptimized, isBundle);
        }
    }
    
    // 5: 非懒加载类处理
    for (EACH_HEADER) {
      classref_t *classlist =
          _getObjc2NonlazyClassList(hi, &count);
      addClassTableEntry(cls);
      realizeClassWithoutSwift(cls);
    }
    
    // 6: 待处理的类
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[I];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            realizeClassWithoutSwift(cls);
            cls->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }
    
    // 7:分类处理
   for (EACH_HEADER) {
       category_t **catlist =
           _getObjc2CategoryList(hi, &count);
       bool hasClassProperties = hi->info()->hasCategoryClassProperties();
       for (i = 0; i < count; i++) {
           category_t *cat = catlist[I];
           Class cls = remapClass(cat->cls);
       }
   }
}
  1. 懒加载类 和 initialize方法调用
    我们在研究消息发送的时候忽略了一点
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }
    runtimeLock.lock();
    checkIsKnownClass(cls);

    if (!cls->isRealized()) {
//如果cls未加载调用下边代码
 // !!!!!!! 证明懒加载的类是在第一次发送消息的时候加载的
       cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
    }
//!!!!!!!!! 根据注释我们可以看出来 `initialize `方法也是在第一次发送消息的时候调用的
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
        // If sel == initialize, _class_initialize will send +initialize and 
        // then the messenger will send +initialize again after this 
        // procedure finishes. Of course, if this is not being called 
        // from the messenger then it won't happen. 2778172
    }
}
分类的加载

1.0 初探 分类的本质
我们利用clang -rewrite-objc xxx.m -o xxx.cpp进行查看

image.png
他是一个结构体.那么我们在源码中搜索
struct category_t {
//分类的名字
    const char *name;
//谁的分类
    classref_t cls;
//实例方法列表
    struct method_list_t *instanceMethods;
//类方法列表
    struct method_list_t *classMethods;
//协议
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
1.1 懒加载类 + 懒加载分类

懒加载类是在第一次发消息时加载lookupImpOrForward方法,
lookupImpOrForward--->realizeClassMaybeSwiftAndLeaveLocked--->realizeClassMaybeSwiftMaybeRelock--->realizeClassWithoutSwift---->methodlizeClass。
然后class的data()中就已经存在懒加载的分类方法了。

1. 2非懒加载类 + 非懒加载分类

因为class为非懒加载,所以首先会加载初始化class;然后因为分类也是非懒加载的,所以会调用_read_images中的分类相关的加载初始化,在此过程中调用了addUnattachedCategoryForClass绑定分类到class的方法:

1.3 非懒加载类 + 懒加载分类

因为class为非懒加载,所以会直接会走正常的class的加载初始化流程:read_images---->realizeClassWithoutSwift---->methodlizeClass。
初始化完成后,class的ro中就已经存在懒加载的分类方法了。这里编译器已经自动的将category方法加进去了;

1.4 懒加载类 + 非懒加载分类

发送消息的时候就去读取 - realizeClassWithoutSwift - methodlizeClass
就是我的类要在消息发送的时候才有 - 但是我的分类提前了 - 需要加载 - read_images - addUnattachedCategoryForClass - 但是没有实现类 就会在下面 prepare_load_methods 实现 prepare_load_methods - realizeClassWithoutSwift 给你提前了实现类的信息 - unattachedCategoriesForClass

上一篇 下一篇

猜你喜欢

热点阅读