此文實際成于 2015/08/09
[objc explain]: Non-pointer isa
from objc explain: Non-pointer isa
On iOS for arm64, the isa field of Objective-C objects is no longer a pointer.
主要是因為雖然是 64 位的系統(tǒng),但是對象的指針不需要64位這么多位去編碼。
雖然相當(dāng)于原來的 isa 字段變成了一個總長為64位的蚤氏,以位為基本分配單元的數(shù)據(jù)結(jié)構(gòu)留潦。
此數(shù)據(jù)結(jié)構(gòu)還封裝了其他數(shù)據(jù)故觅,如一個對象的引用計算掏觉。是否是弱引用等繁堡。
它有兩個好處:
- 性能更好瞎饲。
- 速度更快口叙。
Why change it?
Performance. Re-purposing these otherwise unused bits increases speed and decreases memory size. On iOS 7 the focus is on optimizing retain/release and alloc/dealloc.
應(yīng)用的代碼應(yīng)該注意什么?
-
不要直接讀取
obj->isa
字段企软。如果這么做了編譯器會報錯庐扫。
應(yīng)該改為使用[obj class]
或者object_getClass(obj)
Trust the Compiler. The Compiler is your friend. Use [obj class] or object_getClass(obj) instead.
-
也不要直接對
obj->isa
字段賦值。 應(yīng)該使用object_setClass()
注意:The 64-bit iOS simulator currently does not use non-pointer isa. Test your code on a real arm64 device.
What does this mean for debugging?
The debugger knows how to decode the class from the isa field. You should not need to examine it directly in most cases.
You can run your code with environment variable OBJC_DISABLE_NONPOINTER_ISA=YES
to disable non-pointer isa for all classes. If your code works with this set and fails without it, you may be incorrectly accessing an isa field directly somewhere.
If you are writing a debugger-like tool, the Objective-C runtime exports some variables to help decode isa fields. objc_debug_isa_class_mask
describes which bits are the class pointer: (isa & class_mask) == class
pointer. objc_debug_isa_magic_mask
and objc_debug_isa_magic_value
describe some bits that help distinguish valid isa fields from other invalid values: (isa & magic_mask) == magic_value
for isa fields that are not raw class pointers. These variables may change in the future so do not use them in application code.
64 位 所代表的信息說明仗哨。
僅作參考 形庭,說不定現(xiàn)在的實現(xiàn)已經(jīng)改變了呢。
(LSB)
1 bit indexed 0 表示為原始的 isa , 1 表示是一個 non-pointer isa.
1 bit has_assoc 對象有過或者曾經(jīng)有過 關(guān)聯(lián)引用 厌漂,沒有關(guān)聯(lián)引用可以更快的 dealloc
1 bit has_cxx_dtor 對象是否有 C++ 或者 ARC 的析構(gòu)函數(shù)萨醒。沒有析構(gòu)函數(shù)可以更塊的 dealloc
30 bits shiftcls 對象指針的非 0 位
9 bits magic 為 0xd2. 用于識別是否是真正的對象而不是未初始化的沒用的東西。
1 bit weakly_referenced 對象有或曾經(jīng)被一個 ARC 的 weak 變量引用過苇倡。沒有引用過可以更快的dealloc
1 bit deallocating 對象當(dāng)前正在 deallocating
1 bit has_sidetable_rc 對象的引用計算值太大無法內(nèi)聯(lián)存儲富纸。
19 bits extra_rc 對象除了1之外的引用計算。(如假設(shè)些值為 5 旨椒,那么對象真實的引用計數(shù)為 6.)
(MSB)
根據(jù)最新的公開的
http://www.opensource.apple.com/tarballs/objc4/
Objective-C 源代碼晓褪。objc4-647
- objc_object 的isa屬性是一個
isa_t
的類型。 -
isa_t
是一個聯(lián)合综慎。 對于 arm64來說其定義如下:(對于x86-64來說不同)
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# define ISA_MASK 0x00000001fffffff8ULL
# define ISA_MAGIC_MASK 0x000003fe00000001ULL
# define ISA_MAGIC_VALUE 0x000001a400000001ULL
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 30; // MACH_VM_MAX_ADDRESS 0x1a0000000
uintptr_t magic : 9;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
ISA 與 Class
前面說到 isa字段最初是用來存儲對象所對應(yīng)的類的涣仿。后來isa字段的值變復(fù)雜的。
但是記錄所指向的類還是本值工作。isa 也是 is a
的聯(lián)合好港。
即此對象是一個什么愉镰?
objc_object
結(jié)構(gòu)有如下兩個公開的方法:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
他們返回的類型的都 Class
類型。
之前我們分析過钧汹,Class
是struct objc_class
的別名丈探。
struct objc_class
繼承自 struct objc_object
在深入了解其實現(xiàn),先了解幾點拔莱。
-
SUPPORT_TAGGED_POINTERS
在 arm64 或者 64 位時它真碗降。 - Tagged pointer 可以參考 Tagged pointer
簡單的來說,Tagged pointer 就是非單純的指針辨宠。它還帶有其他的數(shù)據(jù)信息遗锣。比如上面介紹的isa的指針。
Greg Parker 在這里有說明:舉例嗤形。 http://stackoverflow.com/questions/20362406/tagged-pointers-in-objective-c
更多說明見 Tagged Pointer.
initIsa
inline void
objc_object::initIsa(Class cls){
isa = (uintptr_t)cls;
}
將其類的指針賦值給objc_object
對象的 isa
指針精偿。
后面幾個函數(shù)都是同樣的操作:
```cpp
inline void objc_object::initClassIsa(Class cls) { initIsa(cls); }
inline void objc_object::initProtocolIsa(Class cls) { initIsa(cls); }
inline void objc_object::initInstanceIsa(Class cls, bool) { initIsa(cls); }
inline void objc_object::initIsa(Class cls, bool, bool) { initIsa(cls); }
```
- changeIsa
還可以更換一個對象的Class,isa
這倒是挺有意思的赋兵”恃剩看objc是怎么做的。
```cpp
inline Class
objc_object::changeIsa(Class cls)
{
assert(!isTaggedPointer());
isa_t oldisa, newisa;
newisa.cls = cls;
do {
oldisa = LoadExclusive(&isa.bits);
} while (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits));
if (oldisa.cls && oldisa.cls->instancesHaveAssociatedObjects()) {
cls->setInstancesHaveAssociatedObjects();
}
return oldisa.cls;
}
```
StoreExclusive的函數(shù)在ARM64 下是以匯編來實現(xiàn)的如下:
static ALWAYS_INLINE
bool
StoreExclusive(uintptr_t *dst, uintptr_t oldvalue __unused, uintptr_t value)
{
uint32_t result;
asm("stxr %w0, %x2, [%x3]"
: "=r" (result), "=m" (*dst)
: "r" (value), "r" (dst));
return !result;
}
原來changeIsa 的做法是將新的isa中數(shù)據(jù)復(fù)制到老的isa中霹期。同時也為設(shè)置關(guān)聯(lián)的對象叶组。
- ISA 與 MetaClass
更多說明見: Objective-C class