iOS基础---OC runtime篇一之基本概念

本文深入探讨Objective-C的运行时机制,重点讲解OC基于C语言实现的动态特性,包括struct和union的区别,OC基础数据结构如Class、id、SEL、IMP的定义,以及objc_class、objc_object等关键结构体的组成,揭示方法缓存和动态数据存储的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OC的runtime是基于C语言实现的objective-C 的动态运行时机制,是iOS开发者最基础的知识,虽然互联网上相关的介绍多如牛毛,但是这种最基础的机制,每个开发者都需要有自己的理解与整理。

基本概念

第一、struct和union

由于runtime是基于C实现的,里面有大量的C预研数据结构,如果对C语言不太熟悉,理解起来未免费劲。所以,这里首先需要搞清楚这两个概念:

struct和union都是数据结构的标示方式,用于描述一种状态。二者最大区别是内存分配的差异:struct中的各个数据成员内存是单独分配的,而union内存只分配一份,各个数据成员共享。所以struct在遵循字节对齐的原则下,其内存是各个数据成员的总和,而union的内存大小是其数据成员中占内存最大的成员的内存大小。

下面这篇文章对Union的解释比较透彻:https://www.cnblogs.com/x_wukong/p/5912682.html

第二、OC的基础数据结构

Class: Class是一个指向objc_class结构体的指针

typedef struct objc_class *Class;

而objc_class结构体,苹果目前的实现是

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

大部分情况下,我们看到的资料对类结构的理解都是这样的:

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

但是上述实现OBJC2以前的。当前的实现其实只是(我只所以这么说,主要是目前最新的系统头文件还是这种定义)

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

} OBJC2_UNAVAILABLE;

即objc_class中只有isa指针,这里的isa指向实例所属的类。

而在最新的objc-runtime-new.h中,objc_class的实现为:


struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    //结构体中的方法在这里省略了
}

可以看出objc_class继承自objc_object,objc_object的实现如下:

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    .....
}

这里我们先不展开描述objc_object的具体实现,但是可以确定是objc_class也是一种对象。

这里,我们有必要稍微深入一下最新的Class的实现,先看看cache的实现:

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(cache_key_t key, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};

而_buckets的类型为

struct bucket_t {
private:
    cache_key_t _key;
    IMP _imp;

public:
    inline cache_key_t key() const { return _key; }
    inline IMP imp() const { return (IMP)_imp; }
    inline void setKey(cache_key_t newKey) { _key = newKey; }
    inline void setImp(IMP newImp) { _imp = newImp; }

    void set(cache_key_t newKey, IMP newImp);
};

所以,这里_buckets中其实缓存的方法实现,很显然,这种机制可以提升方法的访问效率。

Class中另一个重要的成员为class_data_bits_t 类型的bits,它扮演了class存储数据的功能,bits中最重要的是class_rw_t结构的data,我们看看class_rw_t的结构:

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;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

    void setFlags(uint32_t set) 
    {
        OSAtomicOr32Barrier(set, &flags);
    }

    void clearFlags(uint32_t clear) 
    {
        OSAtomicXor32Barrier(clear, &flags);
    }

    // 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));
    }
};

这里我们看到了熟悉的methods,properties,protocols,但是没有看到ivars。但是还有一个const class_ro_t 的变量,查看class_ro_t的结构:

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

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

此结构体包含了实例的初始地址,实例大小,成员变量的布局,我们在这里看到ivars,且是const类型。那么这个ro为何物?回过头我们看看它是const修饰的,这部分其实是实例的不可变部分,或者说是类在编译期就确定的基本成分。

而methods,properties,protocols则可以在动态运行时修改。

id:OC中指向对象的类型,其定义为:

typedef struct objc_object *id;

它就是指向objc_object结构体的一个指针。

SEL:这是指向objc_selector结构体的一个指针


/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

值得一提的是: struct objc_selector 的定义确实在runtime的源码中不可见,我们可以猜测其内部实现至少包含一个char指针,指向selector的名称。

IMP:方法实现指针

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

从定义上看不是结构体,而是个函数指针,在老的定义中,相关变量的类型为void,而在新的定义中,相关的变量类型为id

以上是runtime中最基本的几个概念,了解他们是分析runtime源码的基础,特此mark

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值