objc_object解读

群组
Flutter,iOS,Android,Python等,闲聊工作&技术&问题;
个人主页:https://waitwalker.cn
telegram群链接:https://t.me/joinchat/Ej0o0A1ntlq5ZFIMzzO5Pw

        Objective-C是面向对象的语言.面向对象语言有一句话说:”万物皆对象”,这个”对象”从哪里来呢?下面从Runtime源码实现来进一步查看.

一. objc_object声明相关

        首先我们先看runtime.h文件,这个文件相当于Runtime对外部提供的接口文件.包含方法,成员变量,分类,属性等结构声明,也包含了objc_class的完整声明.当我们使用Xcode创建一个类的时候一般都是调用的这里.

/**
    Runtime对外提供的接口,包含方法,成员变量,分类,属性等结构声明,也包含了objc_class的完整声明

 **/

/* Types */

#if !OBJC_TYPES_DEFINED

/// 表示一个类中的方法
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

/// 表示类中的一个成员变量
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

/// 表示一个分类
/// An opaque type that represents a category.
typedef struct objc_category *Category;

/// 表示一个属性
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

/// 类的声明结构
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;
/* Use `Class` instead of `struct objc_class *` */

可以看到objc_class的结构声明中有isa指针,这个指针是Class类型,然后我们顺藤摸瓜,继续查找Class的定义来源.然后找到了objc-private.h文件,进入文件后我们看到结构体objc_class和objc_object的不完整声明如下:

// MARK: - objc_class 和 objc_object 不完整声明
struct objc_class;
struct objc_object;

接着是用typedef 将这两个类型取了两个熟悉的名字:Class指针和id指针:

// MARK: - 将objc_class类型取名为Class指针类型;将objc_object类型取名为id指针类型
typedef struct objc_class *Class;
typedef struct objc_object *id;



往下就是isa的声明union(联合体),isa_t中有两个初始化函数,两个成员变量,cls和bits,还有一个结构体成员.其中在不同的os平台,有不同的声明.

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

// SUPPORT_PACKED_ISA
#endif


#if SUPPORT_INDEXED_ISA

# if  __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
    // armv7k or arm64_32

#   define ISA_INDEX_IS_NPI_BIT  0
#   define ISA_INDEX_IS_NPI_MASK 0x00000001
#   define ISA_INDEX_MASK        0x0001FFFC
#   define ISA_INDEX_SHIFT       2
#   define ISA_INDEX_BITS        15
#   define ISA_INDEX_COUNT       (1 << ISA_INDEX_BITS)
#   define ISA_INDEX_MAGIC_MASK  0x001E0001
#   define ISA_INDEX_MAGIC_VALUE 0x001C0001
#   define ISA_BITFIELD                         \
      uintptr_t nonpointer        : 1;          \
      uintptr_t has_assoc         : 1;          \
      uintptr_t indexcls          : 15;         \
      uintptr_t magic             : 4;          \
      uintptr_t has_cxx_dtor      : 1;          \
      uintptr_t weakly_referenced : 1;          \
      uintptr_t deallocating      : 1;          \
      uintptr_t has_sidetable_rc  : 1;          \
      uintptr_t extra_rc          : 7
#   define RC_ONE   (1ULL<<25)
#   define RC_HALF  (1ULL<<6)

nonpointer: 标记此isa是否是tagged pointer优化后的isa.占用1bit.值为1说明是优化后的isa.具体参考链接,tagged pointer是对实例对象的优化,默认是true,并且是isClass的判断,这是我看源码的理解,不对请指正

has_assoc:标记object是否有关联对象,没有,释放更快

has_cxx_dtor:标记是否有西沟函数,没有,释放更快

shiftcls:类对象(Class,meta-Class对象)内存地址信息

magic:标记object是否初始化完成

weakly_refrenced:标记object是否有weak指针指向它

deallocating:标记object是否正在释放

has_sidetable_rc:标记object的extra_rc位数能否存的下object的引用计数,存不下即has_sidetable_rc=1,存在全局的SideTable里面

extra_rc:存储object的引用计数,存不下,存在全局的SideTable里面

下面是object的完整声明:

// MARK: - object的完整声明
struct objc_object {
private:

    // 私有成员变量: isa指针
    isa_t isa;

// 公有函数
public:

    // 通过这个函数获取不支持tagged pointer的类指针
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();


    // 通过这个函数获取支持tagged pointer的类指针
    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

    // 以下几个函数是isa的初始化函数

    // initIsa()用来初始化一个新的对象的isa
    // initIsa() should be used to init the isa of new objects only.

    // changeIsa 修改一个对象的isa
    // If this object already has an isa, use changeIsa() for correctness.

    // initInstanceIsa()用来初始化一个实例对象的isa
    // initInstanceIsa(): objects with no custom RR/AWZ

    // initClassIsa()用来初始化一个类对象的isa
    // initClassIsa(): class objects

    // initProtocolIsa()用来初始化一个protocol的isa
    // initProtocolIsa(): protocol objects

    // initIsa()还用来初始化其它类型对象的isa
    // initIsa(): other objects
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);

    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);

    // tagged pointer 相关
    bool hasNonpointerIsa();
    bool isTaggedPointer();
    bool isBasicTaggedPointer();
    bool isExtTaggedPointer();

    // 是否是Class
    bool isClass();

    // 关联对象相关
    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // weak指针相关
    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    // 对象是否有析构函数
    // object may have -.cxx_destruct implementation?
    bool hasCxxDtor();

    // retain 和 release 操作对象的引用计数 声明函数
    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();


    // retain 和 release 操作对象的引用计数 实现函数
    // Implementations of retain/release methods
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // 释放销毁对象相关
    // Implementation of dealloc methods
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();

// 私有函数 上面一些公有函数的调用函数
private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);

    // Slow paths for inline control
    id rootAutorelease2();
    bool overrelease_error();

#if SUPPORT_NONPOINTER_ISA
    // Unified retain count manipulation for nonpointer isa
    id rootRetain(bool tryRetain, bool handleOverflow);
    bool rootRelease(bool performDealloc, bool handleUnderflow);
    id rootRetain_overflow(bool tryRetain);
    bool rootRelease_underflow(bool performDealloc);

    void clearDeallocating_slow();

    // Side table retain count overflow for nonpointer isa
    void sidetable_lock();
    void sidetable_unlock();

    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
    bool sidetable_addExtraRC_nolock(size_t delta_rc);
    size_t sidetable_subExtraRC_nolock(size_t delta_rc);
    size_t sidetable_getExtraRC_nolock();
#endif

    // Side-table-only retain count
    bool sidetable_isDeallocating();
    void sidetable_clearDeallocating();

    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();

    id sidetable_retain();
    id sidetable_retain_slow(SideTable& table);

    uintptr_t sidetable_release(bool performDealloc = true);
    uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);

    bool sidetable_tryRetain();

    uintptr_t sidetable_retainCount();
#if DEBUG
    bool sidetable_present();
#endif
};

以上就是objc_object的具体声明结构,里面有许多值得深究的问题,也是接下来会继续关注的问题.

二.实例对象的初始化

1.alloc

下面我们来看一个对象的实例化过程:

Class newClass = objc_allocateClassPair(objc_getClass("NSObject"), "newClass", 0);
        objc_registerClassPair(newClass);
        id newObject = [[newClass alloc]init];
        NSLog(@"%s",class_getName([newObject class]));
        NSLog(@"Hello, World!");

上面代码时创建一个newClass类,并且用这个新类实例化一个newObject对象.查看一下alloc方法和init方法的调用栈,其中省略了中间过程:

id _objc_rootAlloc(Class cls)
└── static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    └── id class_createInstance(Class cls, size_t extraBytes)
        └── id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct, size_t *outAllocatedSize)
            ├── size_t instanceSize(size_t extraBytes)
            ├── void    *calloc(size_t, size_t)
            └── inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)

其中NSObject.mm中对alloc的实现如下:

+ (id)alloc {
    return _objc_rootAlloc(self);
}

这里面调用了一个私有函数,返回一个id类型(objc_object)如下:

id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

NSObject.mm对callAlloc()实现:

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

其中有个class_createInstance()函数,这函数中会调用_class_createInstanceFromZone()函数:

static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    }
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}

这里面有:1)instanceSize()是给对象分配内存空间:

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    if (size < 16) size = 16;
    return size;
}

uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}

2)initIsa()初始化isa指针

inline void
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
    assert(!isTaggedPointer());

    // 不是tagged pointer
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

2.init

init()函数调用栈很简单,只是调用了_objc_rootInit()私有函数,并返回对象本身

- (id)init {
    return _objc_rootInit(self);
}

id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

对象的初始化过程可以总结为分配内存空间并且初始化isa_t结构的过程.

编译后源码库

编译后的源码放在Github, 如果对你有帮助,请给一个star吧!

博客地址&相关文章

博客地址: https://waitwalker.github.io/

系列文章:

1. Runtime源码编译

2. objc_object解读

3. Method解读

4. Class解读

5. Ivar objc_property_t Protocol解读

6. Block解读

7. Retain&Release解读

8. Autorelease解读

9. Weak解读

10. msgSend()解读

参考文献:

https://blog.devtang.com/2014/05/30/understand-tagged-pointer/


  转载请注明: waitwalker objc_object解读

  目录