DisguisedPtr
字面意思:伪装指。注释如下:
// DisguisedPtr<T> acts like pointer type T*, except the
// stored value is disguised to hide it from tools likeleaks
.
// 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;
}
分析
指针的伪装
-
disguise
和undisguise
这两个函数就是将一个指针伪装一个
uintptr_t
变量。uintptr_t
定义如下:typedef unsigned long uintptr_t;
他是一个无符号长整形类型,一般在64位操作系统下,它占8个字节,和一个指针所占字节相同(LP64)。
计算机基础
-
原码、反码、补码
数字在内存中是以补码的形式存储的,正数的补码和原码相同,负数的补码是是正数部分的反码+1。int类型-1在内存中的表示是 0xFFFFFFFF,计算如下:
- -1的正数部分是1,二进制表示为 00000000 00000000 00000000 00000001;
- 1的反码是11111111 11111111 11111111 11111110;
- 反码+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