舉例
描述tagged pointer技術前鲫忍,先做一道題:以下兩個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://www.reibang.com/p/d0e5ec2d0d85
那為啥case2沒有崩潰了筐摘,這個就和tagged pointer的優(yōu)化有關了卒茬。經(jīng)過通過打印發(fā)現(xiàn)val1的class是 __NSCFString
,str2的class是 NSTaggedPointerString
咖熟,是兩個不同的String類型圃酵。(明明寫的NSString,為啥會不一樣馍管,這個涉及到 類簇
的概念郭赐,后續(xù)文章會介紹)。
簡介
從64位開始确沸,iOS引入了Tagged Pointer技術捌锭,用于優(yōu)化NSNumber俘陷、NSDate、NSString等小對象的存儲观谦。
- 在沒有使用Tagged Pointer之前拉盾, NSNumber等對象需要動態(tài)分配內(nèi)存、維護引用計數(shù)等豁状。
- NSNumber指針存儲的是堆中NSNumber對象的地址值使用Tagged Pointer之后捉偏,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中泻红。
- 當指針不夠存儲數(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ù)可以看出剩彬,這也是一個釋放不掉的單例常量對象。當我們使用不同的字符串對象進行創(chuàng)建時當內(nèi)容相同矿卑,其對象的地址也相同喉恋。在運行時根據(jù)實際情況創(chuàng)建。
- 對于 NSString 對象來講母廷,當非字面值常量的數(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