iOS之記憶體管理(ARC),ios記憶體管理arc
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.loc2 56 14 strr0, [sp, #4].loc2 57 12 is_stmt 1 ldrr0, [sp, #4]bl_objc_retainaddr1, sp, #4movsr2, #0.loc2 58 1 strr0, [sp] @ 4-byte Spillmovr0, r1movr1, r2bl_objc_storeStrongldrr0, [sp] @ 4-byte Reload.loc2 58 1 is_stmt 0 addsp, #16Ltmp3:pop.w{r7, lr}b.w_objc_autoreleaseReturnValueLtmp4:Lfunc_end1:
其中 objc_retainAutoreleasedReturnValue和objc_autoreleaseReturnValue主要用於最佳化程式運行。本來應該將返回的對象註冊到autoreleasepool中,但是有了這兩個函數,就可以不將對象註冊到autoreleasepool中,而是直接傳遞給調用方,這是效能調優的一個舉措。
我們再來看看調用createArrayAutorelease方法,如下:
Lfunc_begin0:... 省略bl_objc_retainAutoreleasedReturnValueaddr1, sp, #4movsr2, #0.loc2 44 8 strr0, [sp, #4].loc2 49 1 is_stmt 1 movr0, r1movr1, r2bl_objc_storeStrongaddsp, #24pop{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