前言
纯属探究,不对的地方请指出
为什么要探究这个,在学习runloop的过程中发现了一些,很难看的东西,在cf下可以看到充斥着各种各样的类似的函数
/* Bit 3 in the base reserved bits is used for invalid state in run loop objects */
CF_INLINE void __CFSetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
}
CF_INLINE void __CFUnsetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0);
}
复制代码
注释上已经很明确了,这个位代表着什么状态,于是乎我就猜想,是不是这个_cfinfo下面都有着通用的位来表示状态,就因此查看代码有了如下的结论。(咱们就不卖关子了啊,直接把结论写上,之后再说怎么看的)
_cfinfo[4]存储了什么信息
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
复制代码
isa指针不用多说,是个iOS开发都知道。
cfinfo[4]是个数组(这不TM废话么),类型是uint8_t,一个字节,跟char是一个样子的,能存储8位信息;4*8也就可以存储32位信息。
下面是结论:(不对请指出,但是不要喷啊,小编头一次发帖)
1-8位:
第8位:是否是系统默认分配的空间。kCFAllocatorSystemDefault
1~7位存储的信息都是不同的,看每个cf对象下的类型。
eg:(CFRunloopObserver,第一位firing,第二位repeats)
/* Bit 0 of the base reserved bits is used for firing state */
/* Bit 1 of the base reserved bits is used for repeats state */
CF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0);
}
CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 0);
}
CF_INLINE Boolean __CFRunLoopObserverRepeats(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1);
}
CF_INLINE void __CFRunLoopObserverSetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
}
复制代码
还有其中array是否是mutiable的在第七位表示,在__CFArrayInit可以看到,不过得自己计算,就不贴代码了。可以推想一下dict或者其他的collect的mutiable是否在第七位存储。
9-18位:CFTypeID
19-21位不详,快找瞎了ಥ_ಥ,求哪位大神看到可以补充一下
22位:deallocing
23位:dealloced
24位:是否是自定义引用计数的对象
25-32:引用计数
还可以看到在64位下,用_rc代表着引用计数,至于上面25-32位在64位用来干啥,暂时没看到,希望大神指出。
#if __LP64__
uint32_t _rc;
#endif
复制代码
关键宏以及一些注意点
CFRuntime.h
#if __BIG_ENDIAN__
#define INIT_CFRUNTIME_BASE(...) {0, {0, 0, 0, 0x80}}
#else
#define INIT_CFRUNTIME_BASE(...) {0, {0x80, 0, 0, 0}}
#endif
复制代码
CFInternal.h
#if defined(__BIG_ENDIAN__)
#define __CF_BIG_ENDIAN__ 1
#define __CF_LITTLE_ENDIAN__ 0
#endif
#if defined(__LITTLE_ENDIAN__)
#define __CF_LITTLE_ENDIAN__ 1
#define __CF_BIG_ENDIAN__ 0
#endif
复制代码
#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__) * 3)
#define CF_RC_BITS (!!(__CF_LITTLE_ENDIAN__) * 3)
复制代码
#define __CFBitfieldMask(N1, N2) ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1))
#define __CFBitfieldGetValue(V, N1, N2) (((V) & __CFBitfieldMask(N1, N2)) >> (N2))
#define __CFBitfieldSetValue(V, N1, N2, X) ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | (((X) << (N2)) & __CFBitfieldMask(N1, N2)))
#define __CFBitfieldMaxValue(N1, N2) __CFBitfieldGetValue(0xFFFFFFFFUL, (N1), (N2))
复制代码
BIG_ENDIAN 大端模式 高字节的存储在内存的低低地址
LITTLE_ENDIAN 小端模式 反之
具体的什么自行百度,直接举个例子 0x01 23 45 67
大端 67 45 23 01
小端 01 23 45 67
这个跟cpu有关,我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
所以CF_INFO_BITS在小端代表的就是0
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 0)
等价于
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[0], 0, 0, 0)
复制代码
__CFBitfieldSetValue(V, N1, N2, X)是个啥玩意?
我们存储的是位信息,你也可以用10进制来对cfinfo直接存储,但是不是太烧脑。所以通过移位来表示找到我们想要标记的位。
x来表示是不是对这个位置标记是1还是0,通过左移N2位之后同或之后的位为0。
至于N1可以先不用管它。N2就是表示你要标记为1的位。
说了这么多,还是自己推算一下才可以理解这个是怎么用的。
在哪里看到的
前面我bb了这么半天,你在哪看到的,你是不是在那忽悠我呢?
CFRuntime.c -> _CFRetain 函数这里基本上涵盖了全部,具体看注释部分
static CFTypeRef _CFRetain(CFTypeRef cf, Boolean tryR) {
uint32_t cfinfo = *(uint32_t *)&(((CFRuntimeBase *)cf)->_cfinfo);
if (cfinfo & 0x800000) { // '自定义引用计数'
if (tryR) return NULL;
CFTypeID typeID = (cfinfo >> 8) & 0x03FF; // mask up to 0x0FFF
CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID];
uint32_t (*refcount)(intptr_t, CFTypeRef) = cfClass->refcount;
if (!refcount || !(cfClass->version & _kCFRuntimeCustomRefCount) || (((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS] != 0xFF)) {
HALT; // bogus object
}
#if __LP64__
if (((CFRuntimeBase *)cf)->_rc != 0xFFFFFFFFU) {
HALT; // bogus object
}
#endif
refcount(+1, cf);
return cf;
}
Boolean didAuto = false;
if (tryR && (cfinfo & (0x400000 | 0x200000))) return NULL; //' deallocating or deallocated'
#if __LP64__
if (0 == ((CFRuntimeBase *)cf)->_rc && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef
uint32_t lowBits;
do {
lowBits = ((CFRuntimeBase *)cf)->_rc;
} while (!CAS32(lowBits, lowBits + 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc));
// GC: 0 --> 1 transition? then add a GC retain count, to root the object. well remove it on the 1 --> 0 transition.
if (lowBits == 0 && CF_IS_COLLECTABLE(cf)) {
auto_zone_retain(objc_collectableZone(), (void*)cf);
didAuto = true;
}
#else
#define RC_START 24
#define RC_END 31
CFIndex rcLowBits = __CFBitfieldGetValue(cfinfo, RC_END, RC_START);
if (__builtin_expect(0 == rcLowBits, 0) && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef
volatile uint32_t *infoLocation = (uint32_t *)&(((CFRuntimeBase *)cf)->_cfinfo);
bool success = 0;
do {
cfinfo = *infoLocation;
uint32_t prospectiveNewInfo = cfinfo; // dont want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory.
prospectiveNewInfo += (1 << RC_START);
rcLowBits = __CFBitfieldGetValue(prospectiveNewInfo, RC_END, RC_START);
if (__builtin_expect((rcLowBits & 0x7f) == 0, 0)) {
/* '
将另一位滚动到外部引用计数
实际参考计数=低7位信息[CF_RC_BITS] +外部参考计数<< 6
低位的第8位表示外部引用计数正在使用中。
外部参考计数移位6而不是7,这样我们可以将低位设置为1100 0000而不是1000 0000。
这样可以防止当复合保留计数恰好在1 << 7的倍数附近时,需要访问连续保留和释放的外部引用计数.'
*/
prospectiveNewInfo = cfinfo;
__CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 7) | (1 << 6)));
__CFLock(&__CFRuntimeExternRefCountTableLock);
success = CAS32(*(int32_t *)& cfinfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
if (__builtin_expect(success, 1)) {
__CFDoExternRefOperation(350, (id)cf);
}
__CFUnlock(&__CFRuntimeExternRefCountTableLock);
} else {
success = CAS32(*(int32_t *)& cfinfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
// XXX_PCB: 0 --> 1 transition? then add a GC retain count, to root the object. we ll remove it on the 1 --> 0 transition.
if (success && __CFBitfieldGetValue(cfinfo, RC_END, RC_START) == 0 && CF_IS_COLLECTABLE(cf)) {
auto_zone_retain(objc_collectableZone(), (void*)cf);
didAuto = true;
}
}
} while (__builtin_expect(!success, 0));
#endif
if (!didAuto && __builtin_expect(__CFOASafe, 0)) {
__CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, CFGetRetainCount(cf), NULL);
}
return cf;
}
复制代码
结语
对于这个帖子其实可有可无,不过我个人感觉理解了这些,至少在看CF框架的降低了一些难度,毕竟关于CF的帖子很少。这个帖子只是关于自己的一些问题的记录,其中肯定有不对的,还请小伙伴指正。
思考 & 疑问
1.cfinfo为什么这么写,而不是直接用一个32位表示?自己的猜测,可能跟历史原因,因为远古的时候地址总线是16位的有关系。
2.但出现了64位的cpu之后24-32位存储引用计数为什么不用了,而用了_rc来存储,难道是用来干其他的事么
3.19-21位这个还没找到对应的信息。。
4.iOS的64位下的isa的指针,除了内存地址另外的32位是否也是同样信息?