Tagged Pointer遐想


Tagged Pointer(64bit系統對 NSString、NSNumber 和 NSDate等對象進行優化的一種方式)

一、NSString

類名 存儲區域 初始化的引用計數 作用描述
NSString 堆區 1 開發者常用的不可變字符串類,編譯期間會轉換到其他類型
NSMutableString 堆區 1 開發者常用的可變字符串類,編譯期間會轉換到其他類型
__NSCFString 堆區 1 可變字符串NSMutableString類,編譯期間會轉換爲該類型
__NSCFConstantString 堆區 264-1 不可變字符串NSString類,編譯期間會轉換爲該類型
NSTaggedPointerString 堆區 264-1 Tagged Pointer對象,並不是真的對象

__NSCFConstantString

  • __NSCFConstantString
//    地址相同。類型爲__NSCFConstantString
    NSString *kNSCFConstantString = @"abc”;
    NSString *kNSCFConstantString1 = @"abc";
    NSString *kNSCFConstantString_initWithString1 = [[NSString alloc] initWithString:@"abc"];
    NSString *kNSCFConstantString_initWithString2 = [[NSString alloc] initWithString:kNSCFConstantString];
    NSString *kNSCFConstantString_stringWithString1 = [NSString stringWithString:@"abc"];
    NSString *kNSCFConstantString_stringWithString2 = [NSString stringWithString:kNSCFConstantString];
  • 這兩個地址相同
NSLog(@"%d",kNSCFConstantString == @"abc”);
  • 這兩個地址不相
po kNSCFConstantString == @"abc"

因爲

p @"abc"
(NSTaggedPointerString *) $2 = 0xbe850bfb6b5f72de @“abc"

NSTaggedPointerString

邊界9個,且都爲ASCII字符

    NSString *kNSCFConstantString = @"abc";//__NSCFConstantString
    //    地址相同。類型爲NSTaggedPointerString
    NSString *kNSTaggedPointerString_initWithFormat = [[NSString alloc] initWithFormat:kNSCFConstantString];
    NSString *kNSTaggedPointerString_stringWithFormat = [NSString stringWithFormat:kNSCFConstantString];

__NSCFString

    NSString *kNSCFConstantString = @"abcabcabca";//__NSCFConstantString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString];

copy

    NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
    NSString *kNSCFConstantString2 = @"abcabcabca";//__NSCFConstantString
    NSString *kNSTaggedPointerString = [NSString stringWithFormat:kNSCFConstantString1];//NSTaggedPointerString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString2];//__NSCFString
    // 地址和kNSCFConstantString1相同,類型爲__NSCFConstantString
    NSString *kNSCFConstantString_copy = [kNSCFConstantString1 copy];
    // 地址和kNSTaggedPointerString相同,類型爲NSTaggedPointerString
    NSString *kNSTaggedPointerString_copy = [kNSTaggedPointerString copy];
    // 地址和kNSCFString相同,類型爲__NSCFString
    NSString *kNSCFString_copy = [kNSCFString copy];

mutableCopy

    NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
    NSString *kNSCFConstantString2 = @"abcabcabca";//__NSCFConstantString
    NSString *kNSTaggedPointerString = [NSString stringWithFormat:kNSCFConstantString1];//NSTaggedPointerString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString2];//__NSCFString
    
//    地址變化,類型都爲__NSCFString
    NSMutableString *kNSCFConstantString_mutableCopy = [kNSCFConstantString1 mutableCopy];
    NSMutableString *kNSTaggedPointerString_mutableCopy = [kNSTaggedPointerString mutableCopy];
    NSMutableString *kNSCFString_mutableCopy = [kNSCFString mutableCopy];

中文或者特殊字符(非ASCII字符)

    NSString *kNSCFConstantString1 = @"中文";//__NSCFConstantString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString1];//__NSCFString

autorelease

autorelease的時候發現如果是Tagged Pointer是不會autorelease的


NSMutableString

    NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
//類型都是__NSCFString
    NSMutableString *kNSMutableString1 = [[NSMutableString alloc] initWithString:kNSCFConstantString1];
    NSMutableString *kNSMutableString2 = [NSMutableString stringWithString:kNSCFConstantString1];
    NSMutableString *kNSMutableString3 = [[NSMutableString alloc] initWithFormat:kNSCFConstantString1];
    NSMutableString *kNSMutableString4 = [NSMutableString stringWithFormat:kNSCFConstantString1];
    
    NSMutableString *kNSMutableString5 = [[NSMutableString alloc] initWithCapacity:0];
    NSMutableString *kNSMutableString6 = [[NSMutableString alloc] initWithCapacity:1];
    NSMutableString *kNSMutableString7 = [NSMutableString stringWithCapacity:0];
    NSMutableString *kNSMutableString8 = [NSMutableString stringWithCapacity:1];
    NSMutableString *kNSMutableString9 = [NSMutableString string];

    NSString *kNSMutableString_copy = [kNSMutableString1 copy];//NSTaggedPointerString
    NSMutableString *kNSMutableString_mutable = [kNSMutableString1 mutableCopy];//__NSCFString

string

    //    類型都是__NSCFConstantString,值爲@""
    //    kNSString1和kNSString3地址相同,與kNSString2不同
    NSString *kNSString1 = [NSString string];
    NSString *kNSString2 = @"";
    NSString *kNSString3 = [NSString string];

二、NSNumber

類名 存儲區域 初始化的引用計數 作用描述
NSValue 堆區 1 開發者常用的不可變字符串類,編譯期間會轉換到其他類型
NSNumber 堆區 1 開發者常用的可變字符串類,編譯期間會轉換到其他類型
__NSCFNumber 堆區、棧區 1、264-1 可變字符串NSMutableString類,編譯期間會轉換爲該類型
    //    NSNumber沒有mutableCopy
    NSNumber *number1 = @1;
    NSNumber *number2 = @2;
    NSNumber *number3 = @3;
    NSNumber *number4 = @(3.14159265);
    //    copy不改變地址
    NSNumber *number5 = [number1 copy];
    NSNumber *number6 = [number4 copy];

NSNumber看起來都是__NSCFNumber類型。
實際存儲會通過Tagged Pointer方式存小數字,並且遵循棧區+max retain count的原則。


三、 線程安全

@property (nonatomic, copy) NSString *testStr;
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for(int i=0; i<10000; i++) {
        dispatch_async(queue, ^{
            self.testStr = [NSString stringWithFormat:@"12666666666666666663"];
        });
    }
}

這段代碼執行的話會出現什麼事情呢?testStr是nonatomic的,在多個線程同時訪問的時候會出現BAD_ACCESS問題,當然你可以把testStr改成atomic來解決這個問題或者用串行queue。
一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源。比如多個線程訪問同一個對象、同一個變量、同一個文件,當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。
但是如果你將12666666666666666663改成123,那麼你會發現不會crash了,這是爲什麼呢?
因爲當string不長的時候,其實它是一個Tagged Pointer,那麼它其實沒有堆區對象,當我們賦值的時候就是改下指針(因爲指針裏存了值),所以其實一次讀寫就能完成,不用擔心線程安全的問題。

無法一次讀寫完成的例子如:
比如32位的系統中:
一個字節等於8位
一個Bool值佔1字節,可以在一次讀寫操作中完成賦值或取值,因此是線程安全的。
一個指針佔4字節,也可以在一次讀寫操作中完成賦值或取值,因此是線程安全的。
一個double佔8字節,則需要兩次讀寫操作才能完成賦值或取值,因此就會存在一個寫操作(需兩次寫操作才能完成),之後就是讀操作的可能,導致異常值的現象。


四、objc_msgSend

在進行方法調用時,objc_msgSend會進行識別,不會從isa查找cache、父類查找、消息轉發的過程了,直接從指針中取出值進行操作。
可以節省內存,使用上也優化了,不需要經歷消息發送這類的過程了。
需要注意,如果一個對象是TaggedPointer,就不存在isa指針了,不算真正的OC對象,只是看似對象的普通變量而已。要避免設計對isa的操作。
換成相應的方法調用如 isKindOfClass 和 object_getClass,只要避免在代碼中直接訪問對象的isa變量,即可避免這個問題。因爲現在已經不允許直接用isa,強制要求object_getClass,而object_getClass已經對tagged pointer進行特殊處理。


Todo:

NSDate


摘要

https://mp.weixin.qq.com/s/1otKcoLgByC0zPBVkp_uhg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章