003-non_pointer和OC对象

Class, id, NSObject

对象到底是什么, id 是什么, Class又是什么, 平常我们可以id a, 也可以Class a去接收一个对象.
01.png
02.png
03.png

由上图可知:

  • objc_class继承自objc_object的结构体
  • objc_object内部有一个Class类型的实例isa
  • Class是一个objc_class结构体指针
  • id是一个objc_object结构体指针

在001篇章中探索alloc的时候, 在_class_createInstanceFromZone方法中, 返回的obj是id类型的obj, 也就是返回了一个objc_object *的指针.
所以, 其实id和Class是一个东西, 但是id的权限和范围更广泛, 更大.

图一对应的实际的可以是:


05.png

看图04如下:


04.png

实际上isa是一个isa_t的一个东西, 实际上isa_t是什么呢, 我们先回到我们alloc创建的流程中, 看一下_class_createInstanceFromZone方法:


06.png

根据指引我们看一下initIsa做了点什么:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);

    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());


#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
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

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

看了这段代码, 我感兴趣的地方有三个部分

  • ASSERT(!isTaggedPointer());
  • isa_t
  • nonpointer

isTaggedPointer

inline bool 
objc_object::isTaggedPointer() 
{
    return _objc_isTaggedPointer(this);
}

//#   define _OBJC_TAG_MASK 1UL
static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

这段代码明显的意思就是, 当前的地址的64位低地址第一位是否是1, 如果是1 , 就是TaggedPointer如果不是1就不是. TaggedPointer其实是苹果对内存的一种.

苹果预留了环境变量OBJC_DISABLE_TAGGED_POINTERS,通过设置该变量的布尔值,可以将Tagged Pointer技术的启用与关闭的决定权交给开发者!如果禁用Tagged Pointer,只需设置环境变量OBJC_DISABLE_TAGGED_POINTERS为YES即可!
//thread backtrace 打印堆栈
Tagged Pointer

上面文章中写的是最高位判断, 但是我们最新的源码逻辑来看是最低位是否是1. 后续继续玩一下tag pointer.

2021-06-15 16:27:38.440812+0800 KCObjcBuild[10926:386502] number1 pointer is 0x96eff9fc62cfdc01
2021-06-15 16:27:38.441537+0800 KCObjcBuild[10926:386502] number2 pointer is 0x96eff9fc62cfdf01
2021-06-15 16:27:38.441696+0800 KCObjcBuild[10926:386502] number3 pointer is 0x96eff9fc62cfde01

我使用NSNumber创建了@1,@2,@3小数值的数据, 结果高低位都是1, 可以猜测一下到底是高位标识还是低位标识, 或者思考一下如果是你 你会选择高位标识还是低位标识.

isa_t

先看下代码的标识, 无用的就先处理掉了.

//uintptr_t就是无符号long, 8字节. 下面
typedef unsigned long           uintptr_t;

union isa_t {
//构造方法
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

//位域
    uintptr_t bits;

private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;

public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
...
#endif
...
};


# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_HAS_CXX_DTOR_BIT 1
#   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 unused            : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)
  • union中 bits和结构体ISA_BITFIELD共用64字节, 存储一些isa信息的额外值.

我们以模拟器的x86_64为例

  • nonpointer :是否是纯指针, 是否有优化, 0为纯指针, 1为优化
  • has_assoc :是否关联对象, 0没有, 1有
  • has_cxx_dtor :是否有析构函数, 有析构函数需要做一些处理逻辑, 没有析构函数, 可以更快的释放对象.
  • shiftcls :真正的class, 存储类的指针的值. 模拟器44, 手机33位.
  • magic 用于判断当前对象是真的对象, 还是未初始化的.
  • weakly_referenced 是否有弱引用计数, 如果有的话要从弱引用表里移除, 没有的话可以更快的释放.
  • unused 标志对象是否正在释放内存(摘要过来, 没验证)
  • has_sidetable_rc 引用计数大于2^8需要该变量存储进位.
  • extra_rc 表示该对象的引用计数值, 实际上是引用计数值-1.

并不知道怎么对unused, magic进行考究验证, 后续查查资料在看看到底是个什么作用.

extra_rc进行验证, 主要是因为extra_rc只有8位, 所以我在考虑的时候考虑的是超过了8位之后, 引用计数会怎么样呢?

下图是引用计数为301的情况


07.png

想要取shiftcls, 系统提供的就是对一个mask宏值进行与运算, 实际上就是对0b(17个0)(44个1)(3个0)进行与运算, 然后运算后的数据 就是真实的shiftcls数据了, 直接打印就是NSObject等类名.
系统现在有两种处理方式

  • 直接与一个值进行与运算得到想要的地址数据, 位域最合适
  • 直接右移, 左移把额外的数据挤出去也可以.
    单单isa_t来说, 其实是与运算最方便.

nonpointer

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


objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
 if (!nonpointer) {
        //只处理类的本身把class塞进去
        newisa.setClass(cls, this);
    } else {
      //做一系列的处理
  }
...
}


上述代码初始化nonpointer一直都是false, nonpointer其实标识的意思大致是是否是单纯的指针, 如是果是就直接setClass处理了, 如果不是就进入下面的对很多isa内部数据进行赋值操作等. 看上面的isa_t就了解了

总结

目前还未对magic, unused, 以及isTaggetPointer进行验证.

还是有很多不足吧, 但是时间又不多 , 写文章实在太慢了, 等知识成体系了在好好构一个框架.

继续努力吧, 感谢哭泣又一个坑, 明明extra_rc超过256才会失效, 文档写个10, 还能是失误写错了? 请你善良点朋友.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容