美文网首页
Objective-C 对象的本质 01 - 底层实现

Objective-C 对象的本质 01 - 底层实现

作者: 石头89 | 来源:发表于2019-07-28 21:23 被阅读0次

Objective-C 对象的本质 01 - 底层实现

我们平时编写的 Objective-C 代码,底层实现为 C/C++ 代码。
Objective-C --> C/C++ --> 汇编 --> 机器码。
Objective-C 的面向对象是基于 C/C++ 的==结构体==实现的。

在终端里使用 ==xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc class.m -o class.cpp== 命令可以将 Objective-C 转换为 C++ 代码。

Objective-C 底层实现:

// Objective-C
@interface NSObject {
    Class isa;
}       
@end

// C++: NSObject Implementation
// 存储了 Class 对象的 isa 和 其他成员变量
struct NSObject_IMPL {
    Class isa; // 8 Byte
};

// Class 指针
typedef struct objc_class *Class;

// id,可以作为任何对象的指针
typedef struct objc_object *id;

// Tips:子类的的结构体实现中有一个成员为父类的结构体
struct Bar_IMPL {
    struct Foo_IMPL Foo_IVARS;
    // ivars...  
}

一个 Objective-C(假设没有属性) 对象占用多少内存?

  • 先抛结论:一个没有属性的 Objective-C 对象占用 16 Byte 的内存空间。
  • 使用 class_getInstanceSize() 函数可以查看一个类底层结构体占用的内存大小(结构体需要内存对齐),返回 8 Byte。
  • 使用 malloc_size() 函数可以查看一个对象占用的内存大小,16 Byte。

试验:在类中不断添加 int 属性(4 Byte),再次调用 class_getInstanceSize() 和 malloc_size()。

  1. 添加 1 ~ 2 个 int 属性:
    • class_getInstanceSize():return 16 Byte,
    • malloc_size():return 16 Byte,
  2. 添加 3 ~ 4 个 int 属性:
    • class_getInstanceSize():return 24 Byte,
    • malloc_size():return 32 Byte,
  3. 添加 5 ~ 6 个 int 属性:
    • class_getInstanceSize():return 32 Byte,
    • malloc_size():return 32 Byte,
  4. 添加 7 ~ 8 个 int 属性:
    • class_getInstanceSize():return 40 Byte,
    • malloc_size():return 48 Byte,

结论:

  1. class_getInstanceSize():在添加 int 属性的过程中,NSObject_IMPL 结构体中宽度最大的元素始终为 isa 指针变量,所以结构体的大小为 8 Byte 的整倍数 。
  2. malloc_size():实际在为对象分配内存空间时,大小为 16 Byte 的整倍数。

补充:通过在控制台使用 memory read 指令打印对象的内存数据,也可以看到长度为 16 Byte。

访问内存的 LLDB 命令:


通过上面的试验可以知道,实际上在实例化一个对象时,需要分配至少 16 Byte 的内存空间,接下来看看相关的源码。

在 objc4-723 版本的源码中,从 objc/Project-Headers/objc-runtime-new.h 文件中的 objc_class 结构体中,有一个 instanceSize() 函数(1279 ~ 1284 行),返回值 >= 16,1281 行的注释为:“// CF requires all objects be at least 16 bytes.”,通过跟踪类的 alloc 方法内部的调用链,可以看到在分配内存前调用了 instanceSize() 函数,calloc() 函数根据这个函数的返回值分配内存空间给对象使用,并且会保证内存大小为 16 Byte 的整倍数。

class_getInstanceSize() 函数在苹果开源的 objc4 源码中可以看到。

// objc/Source/objc-class.mm
//
// 查看一个类底层结构体占用的内存大小(结构体需要内存对齐)
size_t class_getInstanceSize(Class cls) {
    if (!cls) return 0;
    return cls->alignedInstanceSize(); // 调用 objc_class 结构体中的函数
}

核心源码

objc_object、objc_class

// objc/Project-Headers/objc-private.h
//
// objc_class 的基类,主要存储了 Meta-Class 对象的 isa
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();

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // 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);
};

// objc/Project-Headers/objc-runtime-new.h
// 
// Class、Meta-Class 都是通过 objc_class 实现的
//
// 作为 Class 使用时,存储了:
//     isa:Meta-Class 对象的 isa
//     superclass:父类的 Class 对象,如果没有父类,superclass 为 nil
//     bits.data().ro.ivars:成员变量信息(ivar_list_t)
//     bits.data().properties:属性信息(method_array_t)
//     bits.data().methods:成员方法信息 (property_array_t)
//     bits.data().protocols:协议信息(protocol_array_t)
//
// 作为 Meta-Class 使用时,存储了:
//     isa:基类的 Meta-Class 对象的 isa,基类的 Meta-Class 对象的 isa 指向自身
//     superclass:父类的 Meta-Class 对象,
//                 基类的 Meta-Class 对象的 superclass 指向基类的 Class 对象
//     bits.data().methods:类方法的信息   
// 
struct objc_class : objc_object {
    // Class ISA;                    // isa,继承自 objc_object
    Class superclass;          // 父类的 Class 对象或 Meta-Class 对象
    cache_t cache;              // 方法缓存
    class_data_bits_t bits;  // 存放 Class 或 Meta-Class 信息对象的指针

    class_rw_t *data() { 
        return bits.data();
    }
    
    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
            return size;
        }
    }
 };

RW_REALIZED、word_align()

// objc/Project-Headers/objc-runtime-new.h
//
// Values for class_rw_t->flags
// These are not emitted by the compiler and are never used in class_ro_t. 
// Their presence should be considered in future ABI versions.
// class_t->data is class_rw_t, not class_ro_t
#define RW_REALIZED           (1<<31)

// objc/Project-Headers/objc-os.h
//
#ifdef __LP64__
#   define WORD_SHIFT 3UL
#   define WORD_MASK 7UL
#   define WORD_BITS 64
#else
#   define WORD_SHIFT 2UL
#   define WORD_MASK 3UL
#   define WORD_BITS 32
#endif

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

class_data_bits_t、class_rw_t、class_ro_t

// objc/Project-Headers/objc-runtime-new.h
// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放类信息对象的指针
struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits; // typedef unsigned long uintptr_t;

    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放类的成员(变量、属性、方法)信息、协议信息或类方法信息,包含分类中的属性、方法、协议信息
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
};

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放类的成员(变量、属性、方法)信息、协议信息或类方法信息,不包含分类中的属性、方法、协议信息
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;
    }
};

SEL、IMP

// 方法名(选择器)
//
// 可以通过 @selector() 或 sel_registerName() 获取 SEL。
// 可以通过 sel_getName() 或 NSStringFromSelector() 将 SEL 转换为字符串。
// 不同类中的同名方法所对应的 SEL 是相同的。
//
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
// 函数指针
//
/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

bucket_t、cache_t

typedef unsigned long uintptr_t;

// 方法缓存中的 Key
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
// 方法缓存
struct bucket_t {
private:
    cache_key_t _key; // key 是方法的 SEL
    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);
};
// 方法缓存列表
// 
// 用散列表(哈希表)来缓存调用多的方法,可以提高查找方法的速度。
// 
// _mask 为什么是 _buckets 的长度 -1 ?
// -1 能确保 (key & _mask) <= (_buckets 的长度 - 1),用来作为 _buckets 的下标来取值。
// 在 cache_t::find() 函数中可以看到计算下标的代码。
// 
// _buckets 的动态扩容:
// 向 _buckets 添加最后一个缓存时会进行扩容。
// 每次扩容为原来的两倍(2^n)。
// 扩容时会将缓存清除,因为扩容后 _mask 发生了变化。
// 通过 cache_t::expand() 函数中进行扩容。
struct cache_t {
    struct bucket_t *_buckets; // 散列表
    mask_t _mask;              // 散列表的长度 -1
    mask_t _occupied;          // 已经缓存的方法数量(<= _mask)

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

其它源码

entsize_list_tt、list_array_tt

// 数组,ivar_list_t、method_list_t、property_list_t 都是 entsize_list_tt 的子类型。
/***********************************************************************
* entsize_list_tt<Element, List, FlagMask>
* Generic implementation of an array of non-fragile structs.
*
* Element is the struct type (e.g. method_t)
* List is the specialization of entsize_list_tt (e.g. method_list_t)
* FlagMask is used to stash extra bits in the entsize field
*   (e.g. method list fixup markers)
**********************************************************************/
template <typename Element, typename List, uint32_t FlagMask>
struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
    Element first;

    uint32_t entsize() const;
    uint32_t flags() const;

    Element& getOrEnd(uint32_t i) const;
    Element& get(uint32_t i) const;

    size_t byteSize() const;
    List* duplicate() const;

    struct iterator;

    const iterator begin() const;
    const iterator end() const;

    struct iterator {
        uint32_t entsize;
        uint32_t index;  // keeping track of this saves a divide in operator-
        Element* element;

        iterator() { }

        iterator(const List& list, uint32_t start = 0)
        : entsize(list.entsize())
        , index(start)
        , element(&list.getOrEnd(start))
        { }
    };
};
// 数组,property_array_t、method_array_t、protocol_array_t 都是 list_array_tt 的子类型。
/***********************************************************************
* list_array_tt<Element, List>
* Generic implementation for metadata that can be augmented by categories.
*
* Element is the underlying metadata type (e.g. method_t)
* List is the metadata's list type (e.g. method_list_t)
*
* A list_array_tt has one of three values:
* - empty
* - a pointer to a single list
* - an array of pointers to lists
*
* countLists/beginLists/endLists iterate the metadata lists
* count/begin/end iterate the underlying metadata elements
**********************************************************************/
template <typename Element, typename List>
class list_array_tt {
    struct array_t {
        uint32_t count;
        List* lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count * sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };

protected:
    class iterator {
        List **lists;
        List **listsEnd;
        typename List::iterator m, mEnd;
    };

public:
    uint32_t count();
    iterator begin();
    iterator end();

    uint32_t countLists();
    List** beginLists();
    List** endLists();

    template<typename Result>
    Result duplicate();
};

ivar_t、ivar_list_t

// 成员变量信息
struct ivar_t {
    int32_t *offset;
    const char *name;  // 变量名
    const char *type;  // 数据类型
    uint32_t alignment_raw;
    uint32_t size;     // 占用内存大小
    
     uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
    bool containsIvar(Ivar ivar) const {
        return (ivar >= (Ivar)&*begin()  &&  ivar < (Ivar)&*end());
    }
};

method_t、method_list_t、method_array_t

// 方法信息
// 
// *types 示意:| 返回值 | 参数1 | 参数2 | ... | 参数n |
// 例如一个没有返回值且没有参数的方法,它的 method_t 中的 *types 为 v16@:8
// | v16  | @0 | :8  |
// | void | id | SEL |
// 两个参数为默认参数,id 对应 self,SEL 对应 _cmd
// 
// *types 中的数字:
// 第一个数字:所有参数一共占用多少字节
// 后面的数字:当前参数从第几个字节开始
struct method_t {
    SEL name;          // 方法名
    const char *types; // 包含了返回值类型、参数类型的编码字符串
    IMP imp;           // 函数指针
};
// 方法信息列表(一维数组)
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
    bool isFixedUp() const;
    void setFixedUp();

    uint32_t indexOfMethod(const method_t *meth) const {
        uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
        assert(i < count);
        return i;
    }
};
// 方法信息列表(二维数组)
class method_array_t : public list_array_tt<method_t, method_list_t> {
    typedef list_array_tt<method_t, method_list_t> Super;

 public:
    method_list_t **beginCategoryMethodLists() {
        return beginLists();
    }
    
    method_list_t **endCategoryMethodLists(Class cls);

    method_array_t duplicate() {
        return Super::duplicate<method_array_t>();
    }
};

Type Encoding(通过 @encode() 可以查看指定类型的字符串编码)


property_t、property_list_t、property_array_t

struct property_t {
    const char *name;
    const char *attributes;
};
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
class property_array_t : public list_array_tt<property_t, property_list_t> {
    typedef list_array_tt<property_t, property_list_t> Super;

 public:
    property_array_t duplicate() {
        return Super::duplicate<property_array_t>();
    }
};

protocol_t、protocol_list_t、protocol_array_t

typedef uintptr_t protocol_ref_t;  // protocol_t *, but unremapped

// Values for protocol_t->flags
#define PROTOCOL_FIXED_UP_2 (1<<31)  // must never be set by compiler
#define PROTOCOL_FIXED_UP_1 (1<<30)  // must never be set by compiler
// Bits 0..15 are reserved for Swift's use.

#define PROTOCOL_FIXED_UP_MASK (PROTOCOL_FIXED_UP_1 | PROTOCOL_FIXED_UP_2)

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;

    const char *demangledName();

    const char *nameForLogging() {
        return demangledName();
    }

    bool isFixedUp() const;
    void setFixedUp();

#   define HAS_FIELD(f) (size >= offsetof(protocol_t, f) + sizeof(f))

    bool hasExtendedMethodTypesField() const {
        return HAS_FIELD(_extendedMethodTypes);
    }
    bool hasDemangledNameField() const {
        return HAS_FIELD(_demangledName);
    }
    bool hasClassPropertiesField() const {
        return HAS_FIELD(_classProperties);
    }

#   undef HAS_FIELD

    const char **extendedMethodTypes() const {
        return hasExtendedMethodTypesField() ? _extendedMethodTypes : nil;
    }

    property_list_t *classProperties() const {
        return hasClassPropertiesField() ? _classProperties : nil;
    }
};
struct protocol_list_t {
    // count is 64-bit by accident. 
    uintptr_t count;
    protocol_ref_t list[0]; // variable-size

    size_t byteSize() const {
        return sizeof(*this) + count*sizeof(list[0]);
    }

    protocol_list_t *duplicate() const {
        return (protocol_list_t *)memdup(this, this->byteSize());
    }

    typedef protocol_ref_t* iterator;
    typedef const protocol_ref_t* const_iterator;

    const_iterator begin() const {
        return list;
    }
    iterator begin() {
        return list;
    }
    const_iterator end() const {
        return list + count;
    }
    iterator end() {
        return list + count;
    }
};
class protocol_array_t : public list_array_tt<protocol_ref_t, protocol_list_t> {
    typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;

 public:
    protocol_array_t duplicate() {
        return Super::duplicate<protocol_array_t>();
    }
};

相关文章

  • Objective-C 对象的本质 01 - 底层实现

    Objective-C 对象的本质 01 - 底层实现 我们平时编写的 Objective-C 代码,底层实现为 ...

  • iOS底层知识之OC语法

    Objective-C对象本质 Objective-C的代码和对象底层是怎样实现的 Objective-C代码,底...

  • 对象和方法的本质

    对象的本质 Objective-C的对象本质其实是结构体。我们平时编写的objective-c代码,底层实现其实都...

  • 一个Objective-C对象占用内存空间原理分析

    Objective-C对象的本质 我们知道Objective-C代码,底层实现其实都是C\C++代码,所以这里为了...

  • 【iOS 底层原理】OC 对象的本质

    一、OC 对象的本质 探寻OC对象的本质,我们平时编写的Objective-C代码,底层实现其实都是C\C++代码...

  • iOS-浅谈OC对象的本质

    目录 Objective-C的本质NSObject的底层实现一个NSObject对象的底层内存分布情况内存占用查看...

  • iOS底层探索003-isa分析

    iOS底层探索-目录 探索OC对象的本质 我们知道平时编写的Objective-C代码,底层都是C/C++实现的,...

  • iOS-浅谈OC对象

    Objective-C的本质 我们平时编写的Objective-C代码底层是由C\C++实现的。OC的面向对象是基...

  • OC对象的本质

    OC对象的本质 我们平常编写的 Objective-C 代码,底层实现其实都是 C/C++ 代码 具体的实现过程,...

  • OC对象的本质

    OC对象的本质 我们平常编写的 Objective-C 代码,底层实现其实都是 C/C++ 代码 具体的实现过程,...

网友评论

      本文标题:Objective-C 对象的本质 01 - 底层实现

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