OC底层探索(十四): 类的加载(二)

2022-05-14  本文已影响0人  ShawnAlex

所用版本:

建议先看下 OC底层探索(十三): 类的加载(一)

还是先看下类的初始化方法_objc_init代码以及示意图

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?

    // 环境变量初始化 
    environ_init();
    // 线程处理
    tls_init();
    // 运行C++静态构造函数。
    static_init();
    // runtime运行时初始化
    runtime_init();
    // objc异常处理系统初始化
    exception_init();
#if __OBJC2__
    // 缓存初始化
    cache_t::init();
#endif
    // 启动回调机制
    _imp_implementationWithBlock_init();
   // dyld通知注册
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
_objc_init

以及_read_images的示意图. _dyld_objc_notify_register(&map_images, load_images, unmap_image); 第一个参数: &map_imagesmap_images_nolock_read_images

read_image

_read_images

read_image做了:

_read_images

[修复重新映射的类]

修复重新映射的类

其中

// 类
GETSECT(_getObjc2ClassRefs,           Class,           "__objc_classrefs");  
// 父类
GETSECT(_getObjc2SuperRefs,           Class,           "__objc_superrefs");
remapClassRef remapClass image.png

循环判断, 如果找到未映射当前类 &父类, 进行修复。

[修复消息]

修复消息

一些特殊的消息处理, 正常消息已经被llvm给处理了, 这里主要修复一些特殊消息。例如: alloc的消息改为直接调用objc_alloc (而不是走alloc的实现) 这种。

fixupMessageRef

[协议读取]

协议读取
GETSECT(_getObjc2ProtocolList,        protocol_t * const,    "__objc_protolist");
readProtocol

稍微打印一下, 看一下:

printf("%s - 协议- %s\n",__func__,newproto->mangledName);
image.png

可发现系统的代理还是蛮多的 -_- !!!, 接下来我们自定义一个代理走下流程

// 自定义代理
@protocol SRTestDelegate <NSObject>

- (void)SRTestAction;

@end

很遗憾发现并没有出现


错误例子

为什么呢? 因为我并未对代理进行调用, 这里也体现了苹果严谨性。每调用还想让我给你个空床位, 做你大头梦 :)

正确例子

加个代理调用就行, 实现方法可以不写。, 接下来我们跟一下源码看下流程。

可看到, 直接走到这里


绑定isa指针

当然如果协议出现没加载情况, 也会有一步修复

修复协议

[分类处理]

分类处理

[类的加载处理]

[非懒加载类]
非懒加载类

只有非懒加载类才走, 正常添加的类不会走这里, 比如我加一个打印

            const char *mangledName = cls->nonlazyMangledName();
            
            if (strcmp(mangledName, "SRTest") == 0) {
                printf("%s 测试 %s\n",__func__,mangledName);
            }
正常类

可发现并没走。其中classref_t const *classlist = hi->nlclslist(&count);是从非懒加载类找数据。

nlclslist

其中_getObjc2NonlazyClassList是获取Mach-O的静态段__objc_nlclslist非懒加载类表

GETSECT(_getObjc2NonlazyClassList,    classref_t const,      "__objc_nlclslist");

这个里稍微提一下懒加载类与非懒加载类


懒加载类与非懒加载类

两者调用流程也有区别

懒加载类:
懒加载类

执行顺序:

非懒加载类:
非懒加载类

执行顺序:

[子类 +load]

子类的+load方法稍微看下, SRTestSubSRTest子类, 子类有load方法, 父类没有, 运行看一下

子类+load方法
可验证: 父类不会出现在__objc_nlclslist中, 但是子类在__objc_nlclslist
[分类 +load]

看下分类的+load, 分类如果忘记创建参考: OC分类创建

分类添加+load方法

分类添加+load方法 非懒加载并没有断住 分类

运行, 可发现

    // Category discovery MUST BE Late to avoid potential races
    // when other threads call the new category code before
    // this thread finishes its fixups.

    // +load handled by prepare_load_methods()

再回头看下官方注释, 可发现分类 +load也是在prepare_load_methods()执行的

prepare load methods

回到之前看一下, 其实我们是想看下非懒加载类的执行顺序, 那么我们在普通类写一个+load方法, 把它变成非懒加载类看源码流程.

addClassTableEntry

接着往下走了 realizeClassWithoutSwift

realizeClassWithoutSwift

类的初始化核心方法: realizeClassWithoutSwift

realizeClassWithoutSwift

实现类方法realizeClassWithoutSwift

/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls, 
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class. 
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}

继续探索前, 先了解下2个概念


clean memory 和 dirty memory

类结构一经使用就会变成dirty memory, 因为runtime会向它写入新的数据。例如: 创建一个新的方法缓存并从类中指向它。dirty memoryclean memory 要"昂贵"的多, 只要进程运行, 它就必须在。比如手机64G, 升级到128G是不是要添"马内", 侧面也表示了有多昂贵 (^ - ^)。同时clean memory可以进行移除从而节省更多的内存空间。因为如果你需要clean memory, 系统可以从磁盘从新加载。同时因为IOS不使用swap, 所以dirty memory在ios中代价是很大的。苹果更建议保持"清洁"的数据越多越好, 通过分类出那些永远不会更改的数据, 可以把大部分数据储存为clean memory.


realizeClassWithoutSwift会操作ro, rw信息。其中

class_ro_t, class_rw_t , class_rw_ext_t 结构体详细内容看末尾附录 (class_rw_ext_t 真是真滴少 :) )

因为ro 是沙盒路径加载, 只读不能操作, 那么当需要操作的时候会将ro 复制一份存放到rw上。

rw

这也体现了 Objective-C 是一门动态语言, 可以在运行时更改方法, 属性等。分类可以在不改变类设计的前提下, 将新方法添加到类中。

但是对于一个类, 我们需要大部分改变的就是methods, properties, protocols等 , 其他的例如父类关系, Flags等不会进行更改, 所以这部分"dirty memory" 很浪费的。由此苹果对class_rw_t进行进一步拆分出class_rw_ext_t用来存储这部分可能被修改的methods, properties, protocols等,class_rw_ext_t只有在需要的时候,才被创建, 相对于旧版runtime可以节省下的内存空间。

rwe

熟悉完下一章继续看 realizeClassWithoutSwift

附: class_ro_t, class_rw_t , class_rw_ext_t 详细

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    const char *getName() const {
        return name.load(std::memory_order_acquire);
    }

    class_ro_t *duplicate() const {
        bool hasSwiftInitializer = flags & RO_HAS_SWIFT_INITIALIZER;

        size_t size = sizeof(*this);
        if (hasSwiftInitializer)
            size += sizeof(_swiftMetadataInitializer_NEVER_USE[0]);

        class_ro_t *ro = (class_ro_t *)memdup(this, size);

        if (hasSwiftInitializer)
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];

#if __has_feature(ptrauth_calls)
        // Re-sign the method list pointer.
        ro->baseMethods = baseMethods;
#endif

        return ro;
    }

    Class getNonMetaclass() const {
        ASSERT(flags & RO_META);
        return nonMetaclass;
    }

    const uint8_t *getIvarLayout() const {
        if (flags & RO_META)
            return nullptr;
        return ivarLayout;
    }
};

[class_rw_ext_t]

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:
    void setFlags(uint32_t set)
    {
        __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
    }

    void clearFlags(uint32_t clear) 
    {
        __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        ASSERT((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};

[class_rw_ext_t]

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};

上一篇 下一篇

猜你喜欢

热点阅读