iOS 開發刷題系列三:NSString 引用計數
下面的程式會輸出什麼?
NSMutableArray *ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"123456789"];
NSString *longStr = [NSString stringWithFormat:@"1234567890"];
[str retain];
[longStr retain];
[ary addObject:str];
[ary addObject:longStr];
NSLog(@"str = %ld", (unsigned long)[str retainCount]);
NSLog(@"longStr = %ld", (unsigned long)[longStr retainCount]);
[str retain];
[str release];
[str release];
[longStr retain];
[longStr release];
[longStr release];
NSLog(@"str = %ld", (unsigned long)[str retainCount]);
NSLog(@"longStr = %ld", (unsigned long)[longStr retainCount]);
[ary removeAllObjects];
NSLog(@"str = %ld", (unsigned long)[str retainCount]);
NSLog(@"longStr = %ld", (unsigned long)[longStr retainCount]);
輸出結果
2018-07-03 13:54:59.951143+0800 BlockTestDemo[13502:2107264] str = -1
2018-07-03 13:54:59.951374+0800 BlockTestDemo[13502:2107264] longStr = 3
2018-07-03 13:54:59.951613+0800 BlockTestDemo[13502:2107264] str = -1
2018-07-03 13:54:59.951717+0800 BlockTestDemo[13502:2107264] longStr = 2
2018-07-03 13:54:59.951956+0800 BlockTestDemo[13502:2107264] str = -1
2018-07-03 13:54:59.952044+0800 BlockTestDemo[13502:2107264] longStr = 1
在網上搜尋了一下,一般人給出的答案是:當字串長度小於10時,字串是儲存在常量區,沒有引用計數。如果長度大於等於10呢,就會被複制到堆去,有引用計數。
後來又出現了一個詞:Tagged Pointer 具體瞭解一下。 嘗試著輸出字串的class,發現兩者的類名是不同的:
NSString *str = [NSString stringWithFormat:@"123456789"];
NSString *longStr = [NSString stringWithFormat:@"1234567890"];
NSLog(@"str %s %p", object_getClassName(str), str);
NSLog(@"longStr %s %p", object_getClassName(longStr), longStr);
2018-07-03 13:54:59.950804+0800 BlockTestDemo[13502:2107264] str NSTaggedPointerString 0xa1ea1f72bb30ab19
2018-07-03 13:54:59.950979+0800 BlockTestDemo[13502:2107264] longStr __NSCFString 0x60c000224f20
Tagged Pointer專門用來儲存小的物件,例如NSNumber和NSDate
Tagged Pointer指標的值不再是地址了,而是真正的值。所以,實際上它不再是一個物件了,它只是一個披著物件皮的普通變數而已。所以,它的記憶體並不儲存在堆中,也不需要malloc和free。
這應該也是上面的NSString在長度小於10的時候,沒有引用計數的原因了。
引申
這種情況引申出另外一道題:
@property (nonatomic, strong) NSString *strongStr;
dispatch_queue_t queue = dispatch_queue_create("strongStr", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 100000; i++) {
dispatch_async(queue, ^{
self.strongStr = [NSString stringWithFormat:@"ab %d", i];
});
}
如果將dispatch_async 裡面的內容改成:
self.strongStr = [NSString stringWithFormat:@"abcdefghijklmn %d", i];
會如何?
前者不會crash, 而後者會crash。
我們來看一下strongStr的setter方法:
- (void)setStrongStr:(NSString *)strongStr {
if (strongStr == _strongStr) return;
id pre = _strongStr;
[strongStr retain];//1.先保留新值
_strongStr = strongStr;//2.再進行賦值
[pre release];//3.釋放舊值
}
結合上面的Tagged Pointer的解釋,呼叫retain or release時strongStr的引用計數一直都是-1;
而對於後者,strongStr實際上是一個物件,retain會使引用計數+1,release會使引用計數 -1;
而對於多執行緒非同步並行執行setStrongStr方法,可能會出現這種情況:多個執行緒拿到同一個舊值,然後給strongStr賦值不同的新值,然後在對舊值的release時候,出現多次release,程式crash;
相關文章
- iOS-NSStringiOS
- iOS引用計數管理之揭祕計數儲存iOS
- 刷題系列 - 計算爬樓梯不同步數的方法數
- iOS NSString中實用的方法iOS
- iOS引用轉換:Foundation與Core Foundation物件互相轉換(__CFString轉NSString,void *轉id等等)iOS物件
- 【IOS開發基礎系列】Cocoa基礎專題iOS
- 怎麼解決引用計數 GC 的迴圈引用問題?GC
- Leetcode刷題系列彙總LeetCode
- Leetcode刷題628. 三個數的最大乘積LeetCode
- iOS開發 - 動畫實踐系列iOS動畫
- 刷題系列 - K-th 語法
- 用 Rust 刷 leetcode 第三題RustLeetCode
- 小白刷題——迴文數
- Leetcode刷題——求眾數LeetCode
- swift自動引用計數Swift
- JVM 系列文章之 物件存活分析 - 引用計數 and 可達性分析JVM物件
- iOS迴圈引用iOS
- IOS 逆向開發(三)應用簽名iOS
- NSString使用stringWithFormatORM
- iOS數學題iOS
- iOS 開發者該認真思考的「三個問題」| 掘金年度徵文iOS
- 玩轉 iOS 開發:《iOS 設計模式 — 代理模式》iOS設計模式
- 物件的引用計數與dealloc物件
- Java引用計數與實現Java
- 視覺效果 -- iOS Core Animation 系列三視覺iOS
- 刷題系列 - 給出一個帕斯卡三角的行數,返回該行元素,要求複雜度為K(O)複雜度
- 劍指Offer系列刷題筆記彙總筆記
- [LeetCode 刷題] 1. 兩數之和LeetCode
- [LeetCode 刷題] 2. 兩數相加LeetCode
- 玩轉 iOS 開發:《iOS 設計模式 — 工廠模式》iOS設計模式
- NSString 遇到的坑
- [日常填坑系列]CAP食用指南-版本引用問題
- iOS 開發:『Runtime』詳解(三)Category 底層原理iOSGo
- 8天讓iOS開發者上手Flutter之三iOSFlutter
- 6年iOS開發常用的三方庫iOS
- iOS 元件化開發(三):載入資原始檔iOS元件化
- 移動開發—iOS日常面試問題移動開發iOS面試
- 【LeetCode刷題】896. 單調數列LeetCode