iOS基礎(chǔ)知識之Tagged Pointer

舉例

描述tagged pointer技術(shù)前,先做一道題:以下兩個case,會crash嗎?
case1

@property (noatomic, strong) NSString* val1;

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
for (int i = 0; i < 1000; i++) { 
  dispatch_async(queue, ^{ 
    self.val1 = [NSString stringWithFormat:@"abcdefghijk"]; 
  }); 
}

case2

@property (noatomic, strong) NSString* val2;

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
for (int i = 0; i < 1000; i++) { 
  dispatch_async(queue, ^{ 
    self.val2 = [NSString stringWithFormat:@"abc"]; 
  }); 
}

實際運行會發(fā)現(xiàn),case1崩潰了,case2不會。case1會崩潰,報錯BAD_ACCESS,這個原因不解釋了,不清楚的可以看我的文章:http://m.itdecent.cn/p/d0e5ec2d0d85
那為啥case2沒有崩潰了,這個就和tagged pointer的優(yōu)化有關(guān)了。經(jīng)過通過打印發(fā)現(xiàn)val1的class是 __NSCFString,str2的class是 NSTaggedPointerString,是兩個不同的String類型。(明明寫的NSString,為啥會不一樣,這個涉及到 類簇的概念,后續(xù)文章會介紹)。

簡介

從64位開始,iOS引入了Tagged Pointer技術(shù),用于優(yōu)化NSNumber、NSDate、NSString等小對象的存儲。

  • 在沒有使用Tagged Pointer之前, NSNumber等對象需要動態(tài)分配內(nèi)存、維護引用計數(shù)等。
  • NSNumber指針存儲的是堆中NSNumber對象的地址值使用Tagged Pointer之后,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中。
  • 當(dāng)指針不夠存儲數(shù)據(jù)時,才會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)。
  • objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法,直接從指針提取數(shù)據(jù),節(jié)省了以前的調(diào)用開銷

可以理解為把指針指向的內(nèi)容直接放在了指針變量的內(nèi)存地址中,因為在 64 位環(huán)境下指針變量的大小達到了 8 字節(jié)足以容納一些長度較小的內(nèi)容。于是使用了標簽指針這種方式來優(yōu)化數(shù)據(jù)的存儲方式。

  • 從其的引用計數(shù)可以看出,這也是一個釋放不掉的單例常量對象。當(dāng)我們使用不同的字符串對象進行創(chuàng)建時當(dāng)內(nèi)容相同,其對象的地址也相同。在運行時根據(jù)實際情況創(chuàng)建。
  • 對于 NSString 對象來講,當(dāng)非字面值常量的數(shù)字,英文字母字符串的長度小于等于 9 的時候會自動成為 NSTaggedPointerString 類型。

如何判斷是否是Tagged Pointer

#if __arm64__
#   define OBJC_SPLIT_TAGGED_POINTERS 1
#else
#   define OBJC_SPLIT_TAGGED_POINTERS 0
#endif

#if (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_SPLIT_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#elif OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#else
#   define _OBJC_TAG_MASK 1UL
#endif

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

答疑

  • 如何判斷一個指針是否為Tagged Pointer?
    • iOS平臺,最高有效位是1(第64bit)
    • Mac平臺,最低有效位是1
  • Tagged Pointer的引用計數(shù)是多少?
    • 通過打印retainCount的值:2^64 - 1,可以理解成不會dealloc
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容