国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

iOS 進階—— iOS內存管理

移動開發 iOS
我將在本篇博文中詳細的從 ARC 解釋到 iOS 的內存管理,以及 Block 相關的原理、源碼。

1 似乎每個人在學習 iOS 過程中都考慮過的問題

  • alloc retain release delloc 做了什么?
  • autoreleasepool 是怎樣實現的?
  • __unsafe_unretained 是什么?
  • Block 是怎樣實現的
  • 什么時候會引起循環引用,什么時候不會引起循環引用?

所以我將在本篇博文中詳細的從 ARC 解釋到 iOS 的內存管理,以及 Block 相關的原理、源碼。

2 從 ARC 說起

說 iOS 的內存管理,就不得不從 ARC(Automatic Reference Counting / 自動引用計數) 說起, ARC 是 WWDC2011 和 iOS5 引入的變化。ARC 是 LLVM 3.0 編譯器的特性,用來自動管理內存。

與 Java 中 GC 不同,ARC 是編譯器特性,而不是基于運行時的,所以 ARC 其實是在編譯階段自動幫開發者插入了管理內存的代碼,而不是實時監控與回收內存。  

 

ARC 的內存管理規則可以簡述為:

  • 每個對象都有一個『被引用計數』
  • 對象被持有,『被引用計數』+1
  • 對象被放棄持有,『被引用計數』-1
  • 『引用計數』=0,釋放對象

3 你需要知道

  • 包含 NSObject 類的 Foundation 框架并沒有公開
  • Core Foundation 框架源代碼,以及通過 NSObject 進行內存管理的部分源代碼是公開的。
  • GNUstep 是 Foundation 框架的互換框架

GNUstep 也是 GNU 計劃之一。將 Cocoa Objective-C 軟件庫以自由軟件方式重新實現

某種意義上,GNUstep 和 Foundation 框架的實現是相似的

通過 GNUstep 的源碼來分析 Foundation 的內存管理

4 alloc retain release dealloc 的實現

4.1 GNU – alloc

查看 GNUStep 中的 alloc 函數。

GNUstep/modules/core/base/Source/NSObject.m alloc: 

  1. + (id) alloc 
  2.  
  3.  
  4. return [self allocWithZone: NSDefaultMallocZone()]; 
  5.  
  6.  
  7.   
  8.  
  9. + (id) allocWithZone: (NSZone*)z 
  10.  
  11.  
  12. return NSAllocateObject (self, 0, z); 
  13.  
  14.  

GNUstep/modules/core/base/Source/NSObject.m NSAllocateObject:

  1. struct obj_layout { 
  2.  
  3. NSUInteger retained; 
  4.  
  5. }; 
  6.  
  7.   
  8.  
  9. NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone) 
  10.  
  11.  
  12. int size = 計算容納對象所需內存大小; 
  13.  
  14. id new = NSZoneCalloc(zone, 1, size); 
  15.  
  16. memset (new, 0, size); 
  17.  
  18. new = (id)&((obj)new)[1]; 
  19.  
  20.  

NSAllocateObject 函數通過調用 NSZoneCalloc 函數來分配存放對象所需的空間,之后將該內存空間置為 nil,***返回作為對象而使用的指針。

我們將上面的代碼做簡化整理:

GNUstep/modules/core/base/Source/NSObject.m alloc 簡化版本:

  1. struct obj_layout { 
  2.  
  3. NSUInteger retained; 
  4.  
  5. }; 
  6.  
  7.   
  8.  
  9. + (id) alloc 
  10.  
  11.  
  12. int size = sizeof(struct obj_layout) + 對象大小; 
  13.  
  14. struct obj_layout *p = (struct obj_layout *)calloc(1, size); 
  15.  
  16. return (id)(p+1) 
  17.  
  18. return [self allocWithZone: NSDefaultMallocZone()]; 
  19.  
  20.  

alloc 類方法用 struct obj_layout 中的 retained 整數來保存引用計數,并將其寫入對象的內存頭部,該對象內存塊全部置為 0 后返回。

一個對象的表示便如下圖:

 

 

4.2 GNU – retain

GNUstep/modules/core/base/Source/NSObject.m retainCount:

  1. - (NSUInteger) retainCount 
  2.  
  3.  
  4. return NSExtraRefCount(self) + 1; 
  5.  
  6.   
  7.  
  8. inline NSUInteger 
  9.  
  10. NSExtraRefCount(id anObject) 
  11.  
  12.  
  13. return ((obj_layout)anObject)[-1].retained; 
  14.  
  15.  

GNUstep/modules/core/base/Source/NSObject.m retain:

  1. - (id) retain 
  2.  
  3.  
  4. NSIncrementExtraRefCount(self); 
  5.  
  6. return self; 
  7.  
  8.  
  9.   
  10.  
  11. inline void 
  12.  
  13. NSIncrementExtraRefCount(id anObject) 
  14.  
  15.  
  16. if (((obj)anObject)[-1].retained == UINT_MAX - 1) 
  17.  
  18. [NSException raise: NSInternalInconsistencyException 
  19.  
  20. format: @"NSIncrementExtraRefCount() asked to increment too far”]; 
  21.  
  22. ((obj_layout)anObject)[-1].retained++; 
  23.  
  24.  

以上代碼中, NSIncrementExtraRefCount 方法首先寫入了當 retained 變量超出***值時發生異常的代碼(因為 retained 是 NSUInteger 變量),然后進行 retain ++ 代碼。

4.3 GNU – release

和 retain 相應的,release 方法做的就是 retain --。

GNUstep/modules/core/base/Source/NSObject.m release

  1. - (oneway void) release 
  2.  
  3.  
  4. if (NSDecrementExtraRefCountWasZero(self)) 
  5.  
  6.  
  7. [self dealloc]; 
  8.  
  9.  
  10.  
  11.   
  12.  
  13. BOOL 
  14.  
  15. NSDecrementExtraRefCountWasZero(id anObject) 
  16.  
  17.  
  18. if (((obj)anObject)[-1].retained == 0) 
  19.  
  20.  
  21. return YES; 
  22.  
  23.  
  24. ((obj)anObject)[-1].retained--; 
  25.  
  26. return NO
  27.  
  28.  

4.4 GNU – dealloc

dealloc 將會對對象進行釋放。

GNUstep/modules/core/base/Source/NSObject.m dealloc:

  1. - (void) dealloc 
  2.  
  3.  
  4. NSDeallocateObject (self); 
  5.  
  6.   
  7.  
  8. inline void 
  9.  
  10. NSDeallocateObject(id anObject) 
  11.  
  12.  
  13. obj_layout o = &((obj_layout)anObject)[-1]; 
  14.  
  15. free(o); 
  16.  
  17.  

4.***pple 實現

在 Xcode 中 設置 Debug -> Debug Workflow -> Always Show Disassenbly 打開。這樣在打斷點后,可以看到更詳細的方法調用。

通過在 NSObject 類的 alloc 等方法上設置斷點追蹤可以看到幾個方法內部分別調用了:

retainCount

  1. __CFdoExternRefOperation 
  2. CFBasicHashGetCountOfKey  

retain

  1. __CFdoExternRefOperation 
  2. CFBasicHashAddValue  

release

  1. __CFdoExternRefOperation 
  2. CFBasicHashRemoveValue  

可以看到他們都調用了一個共同的 __CFdoExternRefOperation 方法。

該方法從前綴可以看到是包含在 Core Foundation,在 CFRuntime.c 中可以找到,做簡化后列出源碼:

CFRuntime.c __CFDoExternRefOperation:

  1. int __CFDoExternRefOperation(uintptr_t op, id obj) { 
  2.  
  3. CFBasicHashRef table = 取得對象的散列表(obj); 
  4.  
  5. int count
  6.  
  7.   
  8.  
  9. switch (op) { 
  10.  
  11. case OPERATION_retainCount: 
  12.  
  13. count = CFBasicHashGetCountOfKey(table, obj); 
  14.  
  15. return count
  16.  
  17. break; 
  18.  
  19. case OPERATION_retain: 
  20.  
  21. count = CFBasicHashAddValue(table, obj); 
  22.  
  23. return obj; 
  24.  
  25. case OPERATION_release: 
  26.  
  27. count = CFBasicHashRemoveValue(table, obj); 
  28.  
  29. return 0 == count
  30.  
  31.  
  32.  

所以 __CFDoExternRefOperation 是針對不同的操作,進行具體的方法調用,如果 op 是 OPERATION_retain,就去掉用具體實現 retain 的方法。

從 BasicHash 這樣的方法名可以看出,其實引用計數表就是散列表。

key 為 hash(對象的地址) value 為 引用計數。

下圖是 Apple 和 GNU 的實現對比: 

 

 

 

5 autorelease 和 autorelaesepool

在蘋果對于 NSAutoreleasePool 的文檔中表示:

每個線程(包括主線程),都維護了一個管理 NSAutoreleasePool 的棧。當創先新的 Pool 時,他們會被添加到棧頂。當 Pool 被銷毀時,他們會被從棧中移除。

autorelease 的對象會被添加到當前線程的棧頂的 Pool 中。當 Pool 被銷毀,其中的對象也會被釋放。

當線程結束時,所有的 Pool 被銷毀釋放。

對 NSAutoreleasePool 類方法和 autorelease 方法打斷點,查看其運行過程,可以看到調用了以下函數:

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  2.  
  3. // 等同于 objc_autoreleasePoolPush 
  4.  
  5.   
  6.  
  7. id obj = [[NSObject alloc] init]; 
  8.  
  9. [obj autorelease]; 
  10.  
  11. // 等同于 objc_autorelease(obj) 
  12.  
  13.   
  14.  
  15. [NSAutoreleasePool showPools]; 
  16.  
  17. // 查看 NSAutoreleasePool 狀況 
  18.  
  19.   
  20.  
  21. [pool drain]; 
  22.  
  23. // 等同于 objc_autoreleasePoolPop(pool)  

[NSAutoreleasePool showPools] 可以看到當前線程所有 pool 的情況:

  1. objc[21536]: ############## 
  2.  
  3. objc[21536]: AUTORELEASE POOLS for thread 0x10011e3c0 
  4.  
  5. objc[21536]: 2 releases pending. 
  6.  
  7. objc[21536]: [0x101802000] ................ PAGE (hot) (cold) 
  8.  
  9. objc[21536]: [0x101802038] ################ POOL 0x101802038 
  10.  
  11. objc[21536]: [0x101802040] 0x1003062e0 NSObject 
  12.  
  13. objc[21536]: ############## 
  14.  
  15. Program ended with exit code: 0  

在 objc4 中可以查看到 AutoreleasePoolPage:

  1. objc4/NSObject.mm AutoreleasePoolPage 
  2.  
  3.   
  4.  
  5. class AutoreleasePoolPage 
  6.  
  7.  
  8. static inline void *push() 
  9.  
  10.  
  11. 生成或者持有 NSAutoreleasePool 類對象 
  12.  
  13.  
  14. static inline void pop(void *token) 
  15.  
  16.  
  17. 廢棄 NSAutoreleasePool 類對象 
  18.  
  19. releaseAll(); 
  20.  
  21.  
  22. static inline id autorelease(id obj) 
  23.  
  24.  
  25. 相當于 NSAutoreleasePool 類的 addObject 類方法 
  26.  
  27. AutoreleasePoolPage *page = 取得正在使用的 AutoreleasePoolPage 實例; 
  28.  
  29.  
  30. id *add(id obj) 
  31.  
  32.  
  33. 將對象追加到內部數組 
  34.  
  35.  
  36. void releaseAll() 
  37.  
  38.  
  39. 調用內部數組中對象的 release 方法 
  40.  
  41.  
  42. }; 
  43.  
  44.   
  45.  
  46. void * 
  47.  
  48. objc_autoreleasePoolPush(void) 
  49.  
  50.  
  51. if (UseGC) return nil; 
  52.  
  53. return AutoreleasePoolPage::push(); 
  54.  
  55.  
  56.   
  57.  
  58. void 
  59.  
  60. objc_autoreleasePoolPop(void *ctxt) 
  61.  
  62.  
  63. if (UseGC) return
  64.  
  65. AutoreleasePoolPage::pop(ctxt); 
  66.  
  67.  

AutoreleasePoolPage 以雙向鏈表的形式組合而成(分別對應結構中的 parent 指針和 child 指針)。

thread 指針指向當前線程。

每個 AutoreleasePoolPage 對象會開辟4096字節內存(也就是虛擬內存一頁的大小),除了上面的實例變量所占空間,剩下的空間全部用來儲存autorelease對象的地址。

next 指針指向下一個 add 進來的 autorelease 的對象即將存放的位置。

一個 Page 的空間被占滿時,會新建一個 AutoreleasePoolPage 對象,連接鏈表。 

 

 

 

6 __unsafe_unretained

有時候我們除了 __weak 和 __strong 之外也會用到 __unsafe_unretained 這個修飾符,那么我們對 __unsafe_unretained 了解多少?

__unsafe_unretained 是不安全的所有權修飾符,盡管 ARC 的內存管理是編譯器的工作,但附有 __unsafe_unretained 修飾符的變量不屬于編譯器的內存管理對象。賦值時即不獲得強引用也不獲得弱引用。

來運行一段代碼:

  1. id __unsafe_unretained obj1 = nil; 
  2.  
  3.  
  4. id __strong obj0 = [[NSObject alloc] init];  
  5.   
  6.  
  7. obj1 = obj0;  
  8.   
  9.  
  10. NSLog(@"A: %@", obj1); 
  11.  
  12.  
  13.   
  14.  
  15. NSLog(@"B: %@", obj1);  

運行結果:

  1. 2017-01-12 19:24:47.245220 __unsafe_unretained[55726:4408416] A: 
  2.  
  3. 2017-01-12 19:24:47.246670 __unsafe_unretained[55726:4408416] B: 
  4.  
  5. Program ended with exit code: 0  

對代碼進行詳細分析:

  1. id __unsafe_unretained obj1 = nil; 
  2.  
  3.  
  4. // 自己生成并持有對象 
  5.  
  6. id __strong obj0 = [[NSObject alloc] init]; 
  7.  
  8.   
  9.  
  10. // 因為 obj0 變量為強引用, 
  11.  
  12. // 所以自己持有對象 
  13.  
  14. obj1 = obj0; 
  15.  
  16.   
  17.  
  18. // 雖然 obj0 變量賦值給 obj1 
  19.  
  20. // 但是 obj1 變量既不持有對象的強引用,也不持有對象的弱引用 
  21.  
  22. NSLog(@"A: %@", obj1); 
  23.  
  24. // 輸出 obj1 變量所表示的對象 
  25.  
  26.  
  27.   
  28.  
  29. NSLog(@"B: %@", obj1); 
  30.  
  31. // 輸出 obj1 變量所表示的對象 
  32.  
  33. // obj1 變量表示的對象已經被廢棄 
  34.  
  35. // 所以此時獲得的是懸垂指針 
  36.  
  37. // 錯誤訪問  

所以,***的 NSLog 只是碰巧正常運行,如果錯誤訪問,會造成 crash

在使用 __unsafe_unretained 修飾符時,賦值給附有 __strong 修飾符變量時,要確保對象確實存在 

責任編輯:龐桂玉 來源: iOS大全
相關推薦

2018-07-23 09:26:08

iOS內存優化

2011-07-21 14:42:45

iOS UIViewCont 內存

2015-03-13 09:30:23

iOS內存管理

2016-04-11 09:30:49

內存管理ios開發

2017-02-09 21:24:22

iOS內存管理

2017-03-07 09:45:43

iOSBlock開發

2015-06-25 09:47:20

iOS內存管理

2014-03-12 09:37:22

內存管理autoreleaseautorelease

2011-07-21 17:40:43

iOS 多核 內存

2011-08-05 16:41:48

iOS 隊列 內存

2016-03-03 10:07:39

ios內存管理面試總結

2011-08-22 11:07:16

IOS 開發多核內存

2017-01-19 19:07:28

iOS進階性能優化

2013-07-19 13:16:26

iOS中BlockiOS開發學習內存管理

2011-09-01 10:42:14

Objective-CCocoa內存管理

2011-07-28 10:01:19

IOS 內存優化

2011-08-22 16:39:15

iOS內存

2013-12-17 15:46:04

iOS開發iOS 內存泄漏

2012-02-01 13:57:40

內存緩存機制

2011-08-02 10:50:56

iOS開發 內存緩存
點贊
收藏

51CTO技術棧公眾號

亚洲女子a中天字幕| 欧美日本不卡高清| 欧美亚洲国产bt| 国产黄色免费网| 成人午夜短视频| 先锋影音欧美| 国产精品a级| 国产一区视频在线播放| 好吊妞视频这里有精品| 亚洲桃花岛网站| 55av亚洲| 日韩黄在线观看| 91超碰免费在线| 精品国产乱码久久久久久浪潮| 岛国视频免费在线观看| 亚洲成a人v欧美综合天堂下载 | 91精品国产色综合久久不卡粉嫩| 日韩大陆毛片av| 日韩激情电影| 亚洲视频在线观看视频| 欧美日韩在线精品一区二区三区激情综合 | 成人国产激情| 久久久av免费| 欧美三级一区| 欧美人与物videos| eeuss鲁片一区二区三区| 欧美怡红院视频一区二区三区| 伊人成综合网yiren22| 国产精品99久久99久久久二8| 欧美午夜精品一区二区三区电影| 国产精品黄视频| 亚洲视频高清| 五码日韩精品一区二区三区视频| 国产一区二区伦理| av视屏在线播放| 亚洲国产综合色| 日本成a人片在线观看| 亚洲国产精品va在线看黑人动漫 | 亚洲日日夜夜| 欧美黄色性视频| 欧美日韩中文字幕一区二区三区| 亚洲tv在线观看| 奇米四色…亚洲| 人人干视频在线| 亚洲欧洲性图库| 欧美精品少妇| 亚洲色图综合久久| 亚州综合一区| 女同一区二区| 成人综合婷婷国产精品久久蜜臀 | 国产一区二区三区视频在线播放 | 国产999精品久久久久久| 中文字幕无码不卡免费视频| 亚洲宅男天堂在线观看无病毒| 秋霞午夜在线观看| 伊人av综合网| 日韩欧美网站| 色婷婷精品国产一区二区三区| 成人精品免费看| jk破处视频在线| 日韩精品一区二区在线观看| 日韩精品一级毛片在线播放| 亚洲伊人一本大道中文字幕| 国产黄人亚洲片| 全部孕妇毛片丰满孕妇孕| 欧美精品一二三| 日本一区二区中文字幕| 91久久精品一区| 国产精品2024| 一个人免费视频www在线观看| 亚洲美女又黄又爽在线观看| 国产毛片一区二区三区| 中国成人在线视频| 亚洲一二三区在线观看| 中文在线а√天堂| 国产日韩精品综合网站| 国产又黄又大久久| 黄色小视频在线免费观看| 日韩中文字幕在线观看| 亚洲人体大胆视频| www.成人精品免费网站青椒| 亚洲欧美中文日韩v在线观看| 婷婷成人基地| 茄子视频成人免费观看| 欧美日韩国产综合久久| 欧美精品中文| 4444亚洲人成无码网在线观看| 亚洲电影中文字幕在线观看| 日韩高清中文字幕一区二区| 91一区二区三区| 中文字幕精品在线不卡| 亚洲黄色免费看| 九九九九九精品| 黄色一区二区在线| 美女视频免费精品| 日韩精品―中文字幕| 亚洲成人久久一区| 亚洲无线一线二线三线区别av| 8848hh四虎| 久久亚洲国产精品| 麻豆免费看一区二区三区| 免费a级毛片在线观看| 国产精品久久99久久| 久久久久久免费| av一区在线| 天天综合中文字幕| 欧美一级在线视频| 亚洲v在线看| 影音先锋在线影院| 97在线观看视频| 99视频一区二区| 久久久成人av毛片免费观看| 亚洲精品在线免费看| 欧美丰满少妇xxxbbb| 欧美午夜一区二区福利视频| 在线小视频网址| 26uuu亚洲国产精品| 中国av一区二区三区| 色天使综合视频| 蜜桃网站在线观看| 日韩精品视频观看| 久草精品在线观看| 另类视频在线| 亚洲欧美日韩不卡一区二区三区| 欧美日韩的一区二区| 99视频精品| av在线麻豆| 欧美一区二区福利| 日韩免费观看高清完整版在线观看| 亚洲激情不卡| 日本视频在线| 日韩欧美一区二区三区四区 | 久久aⅴ国产紧身牛仔裤| 蜜桃av在线免费观看| 精品免费视频123区| 91精品国产一区二区三区香蕉| 亚洲美女啪啪| 国产素人视频在线观看| 成人资源视频网站免费| 欧美性色黄大片| 丝袜诱惑制服诱惑色一区在线观看| 超碰caoporn久久| 视频一区视频二区视频| 亚洲欧美制服另类日韩| 97久久超碰国产精品电影| 综合久久成人| 老司机色在线视频| 国产在线欧美日韩| 亚洲国产婷婷香蕉久久久久久| 国产福利一区二区三区在线视频| 亚洲网站免费| 成人午夜激情| 国产一区免费视频| 日韩国产精品亚洲а∨天堂免| 99久久免费精品| 丝袜美腿综合| www.亚洲.com| 免费看av软件| 久久久久中文字幕2018| 亚洲成人动漫av| 欧美一级二区| 在线一区视频观看| 无套内精的网站| 俄罗斯精品一区二区| 精品亚洲男同gayvideo网站| 中文久久乱码一区二区| 欧美日韩久久| 一区二区视频免费完整版观看| 成人免费淫片免费观看| αv一区二区三区| 亚洲欧美日韩在线一区| 中文字幕在线一区| 99国产精品99久久久久久粉嫩| 成人av三级| 国产九一视频| 久久99导航| 久久色在线播放| 欧美日韩亚洲视频| 国产综合一区二区| 欧美日韩伦理| 免费观看欧美大片| 中国大陆高清aⅴ毛片| 亚洲一区二区三区精品动漫| 96精品视频在线| 欧美顶级少妇做爰| 中文字幕精品—区二区四季| 亚洲免费观看| 久久99精品久久久久久欧洲站| 欧美jizz18hd性欧美| 黄色片久久久久| 精品一区2区三区| 欧美黑人xxxx| 7777精品伊人久久久大香线蕉的| 久久精品日产第一区二区三区高清版 | 最新国产精品视频| 91吃瓜在线观看| 伊人网在线免费观看| 999在线观看视频| 国产精品免费区二区三区观看 | 国产亚洲精品超碰|