Objc DisguisedPtr

DisguisedPtr

字面意思:伪装指。注释如下:

// DisguisedPtr<T> acts like pointer type T*, except the
// stored value is disguised to hide it from tools like leaks.
// nil is disguised as itself so zero-filled memory works as expected,
// which means 0x80..00 is also disguised as itself but we don't care.
// Note that weak_entry_t knows about this encoding.

源码

DisguisedPtr位于Objc源码工程的objc-private.h中,定义如下:

template <typename T>
class DisguisedPtr {
    uintptr_t value;

    static uintptr_t disguise(T* ptr) {
        return -(uintptr_t)ptr;
    }

    static T* undisguise(uintptr_t val) {
        return (T*)-val;
    }

 public:
    DisguisedPtr() { }
    DisguisedPtr(T* ptr) 
        : value(disguise(ptr)) { }
    DisguisedPtr(const DisguisedPtr<T>& ptr) 
        : value(ptr.value) { }

    DisguisedPtr<T>& operator = (T* rhs) {
        value = disguise(rhs);
        return *this;
    }
    DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs) {
        value = rhs.value;
        return *this;
    }

    operator T* () const {
        return undisguise(value);
    }
    T* operator -> () const { 
        return undisguise(value);
    }
    T& operator * () const { 
        return *undisguise(value);
    }
    T& operator [] (size_t i) const {
        return undisguise(value)[i];
    }

    // pointer arithmetic operators omitted 
    // because we don't currently use them anywhere
};

// fixme type id is weird and not identical to objc_object*
static inline bool operator == (DisguisedPtr<objc_object> lhs, id rhs) {
    return lhs == (objc_object *)rhs;
}
static inline bool operator != (DisguisedPtr<objc_object> lhs, id rhs) {
    return lhs != (objc_object *)rhs;
}

分析

指针的伪装

  • disguiseundisguise

    这两个函数就是将一个指针伪装一个uintptr_t变量。uintptr_t定义如下:

    typedef unsigned long           uintptr_t;
    

    他是一个无符号长整形类型,一般在64位操作系统下,它占8个字节,和一个指针所占字节相同(LP64)。

计算机基础

  • 原码、反码、补码

    数字在内存中是以补码的形式存储的,正数的补码和原码相同,负数的补码是是正数部分的反码+1。int类型-1在内存中的表示是 0xFFFFFFFF,计算如下:

    1. -1的正数部分是1,二进制表示为 00000000 00000000 00000000 00000001;
    2. 1的反码是11111111 11111111 11111111 11111110;
    3. 反码+1是11111111 11111111 11111111 11111111,换成十六进制就是0xFFFFFFFF。
  • 无符号类型值的负值

    无符号类型值的负值简单概括就是将内存中的值按位取反后加1,再使用对应的类型去解释

    int main(){
        
        unsigned long ul = 1;
        unsigned long ul2 = -ul;
    
        NSLog(@"%lu, %lu, %ld", ul, ul2, (long)ul2);
        return 0;
    }
    /// 1, 18446744073709551615, -1
    

运算符重载

我们仿照源码创建自己的MyDisguisesPtr。这里代码高度一致,就不再粘贴。

  • operator T* () const

    operator T* () const {
        return undisguise(value);
    }
    

    当T*发生隐式的类型转换时,会调用此重载方法。可以用来将一个DisguisedPtr<T>赋值给一个T*。下面是一个简单的例子:

    template<typename T>
    class test{
    public:
        operator T* () const{
            NSLog(@"调用了重载!!");
            return nullptr;
        }
    };
    
    int main(){
        test<int> ti = {};
        int *pi = ti;
        
        return 0;
    }
    
    2022-08-29 19:59:16.692033+0800 DisguisedPtr[50244:1534055] 调用了重载!!
    Program ended with exit code: 0
    

    替换成我们的MyDisguisedPtr<Type>就是下面这样:

    int main(){
        int i = 999;
        MyDisguisedPtr<int> prtI = MyDisguisedPtr<int>(&i);
        int* f = prtI;
        NSLog(@"%d", *f);
    }
    /// 999
    

    这里就是将一个DisguisedPtr<int>值赋值给了int*变量。

  • T* operator -> () const

    T* operator -> () const {
        return undisguise(value);
    }
    

    重载了->符号变得和结构体指针一样结构体查看成员变量的值。

    struct MySize{
        int H;
        int W;
    };
    
    int main(){
        struct MySize size = {10,20};
        struct MySize* pSize = &size;
        NSLog(@"%d, %d", pSize->H, pSize->W);
        MyDisguisedPtr<struct MySize> ptrStructSize = MyDisguisedPtr<struct MySize>(pSize);
        NSLog(@"%d, %d", ptrStructSize->H, ptrStructSize->W);
    }
    /// 10, 20
    /// 10, 20
    

    这里可以看出,我们可以像操作pSize指针一样方便的操作ptrStructSize变量。

  • T& operator * () const

    T& operator * () const {
        return *undisguise(value);
    }
    

    重载*返回一个引用,用在对变量进行解引用时。还是上面的例子,在输出结构题指针式,可以先解引用,然后使用.来访问成员变量。

    int main(){
        struct MySize size = {10,20};
        struct MySize* pSize = &size;
        NSLog(@"%d, %d", (*pSize).H, (*pSize).W);
    }
    /// 10, 20
    

    使用MyDiguisesPtr<struct MySize>封装之后,我们依然可以像这样去访问。

    int mian(){
        struct MySize size = {10,20};
        struct MySize* pSize = &size;
        // NSLog(@"%d, %d", (*pSize).H, (*pSize).W);
        MyDisguisedPtr<struct MySize> ptrStructSize = MyDisguisedPtr<struct MySize>(pSize);
        NSLog(@"%d, %d", (*ptrStructSize).H, (*ptrStructSize).W);
    }
    /// 10, 20
    
  • T& operator [] (size_t i) const

    T& operator [] (size_t i) const {
        return undisguise(value)[i];
    }
    

    重载[]来支持数组的访问。

    int main(){
        int arr[] = {1,2,3,4,5};
        MyDisguisedPtr<int> ptrArrI = MyDisguisedPtr<int>(arr);
        NSLog(@"%d, %d, %d", ptrArrI[0], ptrArrI[2], ptrArrI[4]);
    }
    /// 1, 3, 5
    
  • DisguisedPtr<T>& operator = (T* rhs)

    DisguisedPtr<T>& operator = (T* rhs){
        value = disguise(rhs);
        return *this;
    }
    

    重载赋值运算符=,使其能够对 value进行复制

    int main() {
        int i = 999;
        MyDisguisedPtr<int> prtI = &i;
        NSLog(@"%d", *prtI);
    }
    /// 999
    
  • DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs)

    DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs){
        value = rhs.value;
        return *this;
    }
    

    重载赋值运算符=,使其能够对 value进行复制

    int main(){
        int i = 999;
        MyDisguisedPtr<int> prtI = &i;
        MyDisguisedPtr<int> prtI2 = prtI;
        NSLog(@"%d", *prtI2);
        NSLog(@"%p, %p", &prtI, &prtI2);
    }
    /// 999
    /// 0x16fdff2b0, 0x16fdff2a8
    
  • static inline bool operator == (MyDisguisedPtr<objc_object> lhs, id rhs)

这里与源码略有不同

static inline bool operator == (MyDisguisedPtr<objc_object> lhs, id rhs) {
    NSLog(@"重载了%s", __func__);
    return lhs == (__bridge objc_object *)rhs;
}

在OC中id定义是typedef struct objc_object *id;,他是一个objc_object结构体的指针,而DisguisedPtr可以将变量伪装成对应类型的指针。重载比较运算符==来进行两个值的比较。

int main(){
    objc_object objc = {[NSObject class]};
    MyDisguisedPtr<objc_object> prtI = &objc;
    id dd = (__bridge id)&objc;
    NSLog(@"%d", prtI == dd);
}

注意:这里重载列两个两个函数

2022-08-29 21:51:49.946044+0800 DisguisedPtr[51187:1612411] 重载了operator==
2022-08-29 21:51:49.946246+0800 DisguisedPtr[51187:1612411] 重载了operator objc_object *
2022-08-29 21:51:58.622621+0800 DisguisedPtr[51187:1612411] 1

源码中没有__bridge操作,但我自己的代码不加__bridge会报错,不清楚原因。也有可能是因为__bridge导致调用objc_object *。欢迎指正。

  • static inline bool operator != (MyDisguisedPtr<objc_object> lhs, id rhs)

    这里与源码略有不同

    static inline bool operator != (MyDisguisedPtr<objc_object> lhs, id rhs) {
        return lhs != (__bridge objc_object *)rhs;
    }
    

C++ 默认参数

c++可以使用:来设置默认参数。一个简单的类定义如下:

// 原始类
class Test{
public:
    int i = 10;
public:
    Test(){}
};
int main(){
    Test t = Test();
    NSLog(@"%d", t.i);
}
/// 10

如果我们想让t.i变成20可以这样修改:

...
public:
    Test():i(20){}

相当于:

...
public:
    Test(){
        i = 20
    }

如果有参数,还可以利用参数,例如:

...
public:
    Test(int _i):i(_i+1000){}
...
int main(){
    Test t = Test(20);
    NSLog(@"%d", t.i);
}
/// 1020
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,431评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,637评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,555评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,900评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,629评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,976评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,976评论 3 448
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,139评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,686评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,411评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,641评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,129评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,820评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,233评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,567评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,362评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,604评论 2 380

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,755评论 0 9
  • Swift2.0 1.defer译为延缓、推迟之意类似栈 注意作用域,其次是调用顺序——即一个作用域结束(注意),...
    zeqinjie阅读 3,398评论 0 50
  • 导语 在上一篇中简单分析了 Weak 属性是如何被存储,获取和销毁的,其中的 SideTable 结构体当做黑盒进...
    iOSugarCom阅读 1,153评论 0 5
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,131评论 1 32
  • 目录 1.Runtime简介 2.NSObject起源 (1) isa_t结构体的具体实现 (2) cache...
    姜涛12345阅读 3,751评论 0 7