Runtime(一)

一、isa指针

1、在Objective-C对象的分类中说过无论是instance对象、class对象还是meta-class对象中都有一个isa指针。不同的是instance对象的isa指针指向class对象;class对象中的isa指向meta-class对象;meta-class对象指向基类的meta-class对象。不同的是

  • 在ARM64架构之前,isa就是一个普通的指针,存储者class对象、meta-class对象的内存地址
  • 在ARM64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多信息
  • ARM64架构下的isa指针类型
isa_t isa;
  • isa_t结构
union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        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
    struct {
        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

#   define ISA_INDEX_IS_NPI      1
#   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
    struct {
        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)
    };

# else
#   error unknown architecture for indexed isa
# endif

// SUPPORT_INDEXED_ISA
#endif

}
  • isa详解
- nonpointer
0:是代表普通的指针,存储着class、meta-class对象的内存地址
1:代表优化过,使用位域存储更多的信息

- has_assoc
是否有设置过关联对象,如果没有,释放时会更快

- has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放的更快

- shiftcls
存储着Class、Meta-Class对象的内存地址信息

- magic
用于在调试时分辨对象是否未完成初始化

- weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快

- deallocating
对象是否正在释放

- has_sidetable_rc
引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

- extra_rc
里面存储的值是引用计数器减1

2、通过代码来测试看一下

NSObject的isa指针.png
  • isa的位数
0x000001a1b7ab6ea1
11010000110110111101010110110111010100001
- 最后一位是1,所以代表优化过,使用位域存储更多的信息
  • 给obj绑定一个属性
    NSObject绑定属性后isa指针.png
  • isa的位数
0x000001a1b7ab6ea3
11010000110110111101010110110111010100011
- 倒数第二位是1,表示有设置关联对象

二、Class的结构

1、从源码来看Class的定义

typedef struct objc_class *Class;
  • 1、struct objc_class结构体
struct objc_class : objc_object {
    // Class ISA;
    //指向 父类
    Class superclass;
    //方法缓存
    cache_t cache;             // formerly cache pointer and vtable
    //bits是一个用于获取具体类的信息
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    //
    class_rw_t *data() { 
        return bits.data();
    }
    .
    .
    .
}
  • 2、(bits & FAST_DATA_MASK)会获得类的详细信息结构体,但是这是一个可读可写结构体struct 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_list_t<method_t>>;类的方法列表<method_list_t<method_t>>)
    method_array_t methods;
    //存储着类的属性列表是一个二维数组(分类的属性列表<property_list_t<property_t>>;类的方法列表<property_list_t<property_t>>)
    property_array_t properties;
    //存储着类的协议列表是一个二维数组(分类的协议列表<protocol_list_t<protocol_ref_t>>;类的方法列表<protocol_list_t<protocol_ref_t>>)
    protocol_array_t protocols;
    .
    .
    .
}
  • 3、类的原始数据结构struct 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_t>
    method_list_t * baseMethodList;
    //存储类的协议列表是一个数组<protocol_ref_t>
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    //存储类的属性列表是一个数组<property_t>
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
}
  • 4、三者的关系如下
    Class信息.png
    • a)通过Class对象中的bits与FAST_DATA_MASK掩码进行&操作,就可以获取到当前类的详细信息
    • b)ro是类中的原始信息,不包含分类或者动态绑定的一些属性
    • c)class_rw_t结构体中的methods、properties、protocols都是二维数组,他们把分类中的methods、properties、protocols当一个元素存储到其中;把ro中的baseMethodList、baseProtocols、baseProperties作为另一个元素存储到其中。
  • 5、class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
    class_rw_t中信息.png

三、Method_t

method_t是对方法\函数的封装

1、 method_t结构体

struct method_t {
    SEL name;//函数名
    const char *types;//返回值类型 参数类型...
    IMP imp;//指向函数的指针

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};
  • 1、SEL代表方法\函数名,一般叫做选择器,底层结构类似char *
    • 可以通过@selector()和sel_registerName()获得
    • 可以通过sel_getName()和NSStringFromSelector()转成字符串
    • 不同类中相同名字的方法,所对应的方法选择器是相同的
typedef struct objc_selector *SEL;
  • 2、types包含了函数返回值、参数编码的字符串
返回值  参数1  参数2 . . . 参数n
  • 3、IMP代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 

四、方法缓存结构体cache_t

  • cache_t结构体
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));
}
  • bucket_t结构体中存储者方法;方法名作为_key,方法地址作为_imp
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);
}
  • 小结:Class内部结构中有个方法缓存(cache_t),用山列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
    Class内部方法缓存.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容