iOS的記憶體管理,相信大家都不陌生,之前是使用的MRC,由開發人員手動來管理記憶體,後來使用了ARC,來由系統管理記憶體。本文主要講講Autorelease,Core Foundation對象在記憶體管理方面要註意的地方。 Autorelease 提到記憶體管理,就不得不提autorelease,雖然我們平 ...
iOS的記憶體管理,相信大家都不陌生,之前是使用的MRC,由開發人員手動來管理記憶體,後來使用了ARC,來由系統管理記憶體。本文主要講講Autorelease,Core Foundation對象在記憶體管理方面要註意的地方。
Autorelease
提到記憶體管理,就不得不提autorelease,雖然我們平時開發中很少會感知到它的存在。autorelease就是自動釋放的意思,如果變數使用autorelease來修飾,就表明變數的釋放由系統來完成。
autoreleasepool是由runloop在開啟或者喚醒的時候創建的,當runloop進入睡眠或者釋放掉的時候,autoreleasepool會給pool中的所有對象發送release消息。那麼,由此便引申出一個問題,如果runloop不進入睡眠或者不釋放(例如:主線程,或者某些常駐線程),pool裡面的對象也便不會被釋放,他們會堆積在記憶體中,但是系統會做一些優化,如下:
- (NSMutableArray*)createArrayNoAutorelease{ id arr = [NSMutableArray arrayWithCapacity:3]; return arr; } - (NSMutableArray*)createArrayAutorelease{ return [NSMutableArray arrayWithCapacity:3]; }
上面的兩個方法都是表明的要返回NSMutableArray這個對象,但是兩種寫法不同,系統做的處理也不相同。
我們先調用第一個方法 createArrayNoAutorelease方法,然後使用xcode的Product->Perform Action->Assemble xxx來看看,生成如下代碼:
Lfunc_begin1: ....前面省略 bl _objc_retainAutoreleasedReturnValue .loc 2 56 14 str r0, [sp, #4] .loc 2 57 12 is_stmt 1 ldr r0, [sp, #4] bl _objc_retain add r1, sp, #4 movs r2, #0 .loc 2 58 1 str r0, [sp] @ 4-byte Spill mov r0, r1 mov r1, r2 bl _objc_storeStrong ldr r0, [sp] @ 4-byte Reload .loc 2 58 1 is_stmt 0 add sp, #16 Ltmp3: pop.w {r7, lr} b.w _objc_autoreleaseReturnValue Ltmp4: Lfunc_end1:
其中 objc_retainAutoreleasedReturnValue和objc_autoreleaseReturnValue主要用於優化程式運行。本來應該將返回的對象註冊到autoreleasepool中,但是有了這兩個函數,就可以不將對象註冊到autoreleasepool中,而是直接傳遞給調用方,這是性能調優的一個舉措。
我們再來看看調用createArrayAutorelease方法,如下:
Lfunc_begin0: ... 省略 bl _objc_retainAutoreleasedReturnValue add r1, sp, #4 movs r2, #0 .loc 2 44 8 str r0, [sp, #4] .loc 2 49 1 is_stmt 1 mov r0, r1 mov r1, r2 bl _objc_storeStrong add sp, #24 pop {r7, pc} Ltmp1: Lfunc_end0:
現在對象被註冊到了autoreleasepool中。我們可以使用:
po [NSAutoreleasePool showPools]
來看看當前autoreleasepool的狀況,會發現多出了一個對象,如下圖:
(我只截取了一部分)
註意:對於alloc/new/copy/mutableCopy這樣的方法作為返回對象,編譯器會將他們優化為createArrayNoAutorelease相同的情況
關於autoreleasepool的內部結構,實現原理等,可以參看:
http://www.cocoachina.com/ios/20150610/12093.html
Core Foundation
Core Foundation對象是一組由c語言介面,可以跟Foundation框架的OC對象相互轉換。
要搞清楚Core Foundation對象的記憶體管理,就需要搞清楚:__bridge, __bridge_retained, __bridge_transfer; CFRetain(), CFRelease() 這幾個關鍵詞的概念。
CFRetain(), CFRelease() :Core Foundation對象的記憶體管理方式,跟之前MRC時代的retain和release很像。
{ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8); NSLog(@"%@", str); CFRelease(str); }
註意:這裡要調用CFRelease(str)方法,不然會有記憶體泄漏。
__bridge:只做類型轉換,不修改對象持有者。如下:
{ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8); NSString *obj = (__bridge NSString*)str; NSLog(@"%@", obj); // CFRelease(str); }
對於從CF轉換為OC對象,一定要調用CFRelease(str)方法,不然會有記憶體泄漏,因為只是做了簡單的指針地址變換,str仍然沒有釋放。
來看一個野指針的例子:
CFMutableArrayRef cfObject = NULL; { id obj = [[NSMutableArray alloc] init]; cfObject = (__bridge CFMutableArrayRef)obj; CFShow(cfObject); } CFRelease(cfObject);
因為__bridge不持有obj對象,所以當大括弧結束以後,obj被釋放,cfObject就成為了野指針,在調用CFRelease方法時就會引發程式崩潰。
__bridge_retained:用於將OC對象轉換為CF對象,持有者也發生改變,需要調用CFRelease方法。
__bridge_transfer:將CF對象轉換為OC對象,同時將對象持有權交給ARC,不需要調用CFRelease方法,如下:
{ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8); NSString *obj = (__bridge_transfer NSString*)str; NSLog(@"%@", obj); // CFRelease(str); }
所以在使用CF對象時,要特別註意記憶體問題。
參考文章:
https://yq.aliyun.com/articles/58964
https://juejin.im/entry/579bfdfe5bbb500064d18aca