Objc DisguisedPtr

DisguisedPtr

  • 源码
  • 分析
    • 指针的伪装
    • 计算机基础
    • 运算符重载
    • C++ 默认参数

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

// DisguisedPtr 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 
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& ptr) 
        : value(ptr.value) { }

    DisguisedPtr& operator = (T* rhs) {
        value = disguise(rhs);
        return *this;
    }
    DisguisedPtr& operator = (const DisguisedPtr& 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 lhs, id rhs) {
    return lhs == (objc_object *)rhs;
}
static inline bool operator != (DisguisedPtr 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*。下面是一个简单的例子:

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

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

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

    这里就是将一个DisguisedPtr值赋值给了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 ptrStructSize = MyDisguisedPtr(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封装之后,我们依然可以像这样去访问。

    int mian(){
        struct MySize size = {10,20};
        struct MySize* pSize = &size;
        // NSLog(@"%d, %d", (*pSize).H, (*pSize).W);
        MyDisguisedPtr ptrStructSize = MyDisguisedPtr(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 ptrArrI = MyDisguisedPtr(arr);
        NSLog(@"%d, %d, %d", ptrArrI[0], ptrArrI[2], ptrArrI[4]);
    }
    /// 1, 3, 5
    
  • DisguisedPtr& operator = (T* rhs)

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

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

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

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

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

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

这里与源码略有不同

static inline bool operator == (MyDisguisedPtr 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 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 lhs, id rhs)

    这里与源码略有不同

    static inline bool operator != (MyDisguisedPtr 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

你可能感兴趣的:(Objc DisguisedPtr)