iOS-記憶體管理就這麼簡單2
文明轉轉和評論是對自己的尊重也是對學者的鼓勵,謝謝
iOS-記憶體管理就這麼簡單2
今天討論的主要內容copy,mutableCopy.
一.copy,mutableCopy的使用1.
NSArray *arr = [[NSArray alloc]initWithObjects:@"hello", nil]; NSArray *arr2 = [arr copy]; NSObject *obj = [[NSObject alloc]init]; NSLog(@"arr retainCout %ld",[arr retainCount]); NSLog(@"arr2 retainCout %ld",[arr2 retainCount]); NSLog(@"arr = %ld arr2 =%ld ",arr,arr2);
運行結果:
2015-01-17 23:26:04.977 MemoryManager[907:40309] arr retainCout 2
2015-01-17 23:26:04.977 MemoryManager[907:40309] arr2 retainCout 2
2015-01-17 23:26:04.977 MemoryManager[907:40309] arr = 140677793151936 arr2 =140677793151936
說明通過copy得到的是同一個對象,copy使堆記憶體對象的引用計數加一了。
2.
NSArray *arr = [[NSArray alloc]initWithObjects:@"hello", nil]; NSArray *arr2 = [arr mutableCopy]; NSObject *obj = [[NSObject alloc]init]; NSLog(@"arr retainCout %ld",[arr retainCount]); NSLog(@"arr2 retainCout %ld",[arr2 retainCount]); NSLog(@"arr = %ld arr2 =%ld ",arr,arr2);
運行結果:
2015-01-17 23:28:12.817 MemoryManager[927:41217] arr retainCout 1
2015-01-17 23:28:12.817 MemoryManager[927:41217] arr2 retainCout 1
2015-01-17 23:28:12.817 MemoryManager[927:41217] arr = 140285358613872 arr2 =140285358605248
說明mutableCopy使把arr中的內容完全的賦值一份並存在新分配的堆記憶體對象中,所以arr和arr2指向的是不同的記憶體對象;
NSArray *arr = [[NSArray alloc]initWithObjects:@"hello", nil]; NSArray *arr2 = [arr mutableCopy]; if([arr2 respondsToSelector:@selector(addObject:)]){ NSLog(@"changed"); } NSObject *obj = [[NSObject alloc]init]; NSLog(@"arr retainCout %ld",[arr retainCount]); NSLog(@"arr2 retainCout %ld",[arr2 retainCount]); NSLog(@"arr = %ld arr2 =%ld ",arr,arr2);
運行結果:
2015-01-17 23:32:03.639 MemoryManager[950:42492] changed
2015-01-17 23:32:03.640 MemoryManager[950:42492] arr retainCout 1
2015-01-17 23:32:03.640 MemoryManager[950:42492] arr2 retainCout 1
2015-01-17 23:32:03.640 MemoryManager[950:42492] arr = 140367229411264 arr2 =140367229427648
說明mutableCopy的作用相當於,
NSMutableArray * arr2 = [[NSMutableArrayalloc]initWithArray:arr];
3.
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:@"hello", nil]; NSMutableArray *arr2 = [arr mutableCopy]; if([arr2 respondsToSelector:@selector(addObject:)]){ NSLog(@"changed"); } NSObject *obj = [[NSObject alloc]init]; NSLog(@"arr retainCout %ld",[arr retainCount]); NSLog(@"arr2 retainCout %ld",[arr2 retainCount]); NSLog(@"arr = %ld arr2 =%ld ",arr,arr2);
運行結果:
2015-01-17 23:38:24.378 MemoryManager[1034:44814] changed
2015-01-17 23:38:24.379 MemoryManager[1034:44814] arr retainCout 1
2015-01-17 23:38:24.379 MemoryManager[1034:44814] arr2 retainCout 1
2015-01-17 23:38:24.379 MemoryManager[1034:44814] arr = 140316907672672 arr2 =140316907807120
結果分析:可變數組mutableCopy還是可變數組,相當於
NSMutableArray *arr2 = [[NSMutableArray alloc]initWithArray:arr];
arr和arr2還是指向不同堆中的對象,但它們的內容完全相同
note:可變數組的mutableCopy之後還是可變的
5.
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:@"hello", nil]; NSMutableArray *arr2 = [arr copy]; if([arr2 respondsToSelector:@selector(addObject:)]){ NSLog(@"changed"); } NSObject *obj = [[NSObject alloc]init]; NSLog(@"arr retainCout %ld",[arr retainCount]); NSLog(@"arr2 retainCout %ld",[arr2 retainCount]); NSLog(@"arr = %ld arr2 =%ld ",arr,arr2);
運行結果:
2015-01-17 23:41:36.505 MemoryManager[1058:46071] arr retainCout 1
2015-01-17 23:41:36.506 MemoryManager[1058:46071] arr2 retainCout 1
2015-01-17 23:41:36.506 MemoryManager[1058:46071] arr = 140719784280096 arr2 =140719784275712
說明可變的數組通過copy函數相當於:
NSArray *arr2 = [[NSArray alloc]initWithArray:arr];
不同堆對象相同內容,這就是所謂的深拷貝和淺拷貝,但要注意拷貝之後的對象的屬性性質的變化,其他的集合類和字串也存在通過深拷貝和淺拷貝及對象性質的變化,所以在應用時要小心。
note:可變的數組copy之後就成了不可變的。
二.循環參考和弱引用解決循環參考的記憶體流失循環參考的出現:首先要明確的是堆上的記憶體對象只有應用計數為0才會調用相當於C++語言中的虛構函數進行記憶體清理,如果兩個對象都各持有對方的強引用,這樣就會形成一個引用環一直存在記憶體中這樣就會造成記憶體的泄漏。
CycleRetain類:
@interface CycleRetain : NSObject@property (nonatomic,retain)id retainObject;@end@implementation CycleRetain@end
CycleRetain *obj1 = [[CycleRetain alloc]init];//此時 obj1指向的堆對象的引用計數為1 CycleRetain *obj2 = [[CycleRetain alloc]init];//此時 obj2指向的堆對象的引用計數為1 NSLog(@"before "); NSLog(@"obj1 retainCount=%ld obj2 retainCount=%ld",obj1.retainCount,obj2.retainCount); obj1.retainObject = obj2; //因為retainObject是retain屬性所以obj2指向的堆記憶體對象的引用計數要加1 ,此時為2 obj2.retainObject = obj1; //因為retainObject是retain屬性所以obj1指向的堆記憶體對象的引用計數要加1 ,此時為2 NSLog(@"after"); NSLog(@"obj1 retainCount=%ld obj2 retainCount=%ld",obj1.retainCount,obj2.retainCount); //當改函數運行結束返回,此時obj1和obj2的聲明周期也就結束了 //記憶體管理原則什麼作用範圍內建立就在什麼範圍內釋放 [obj1 release]; //obj1指向的堆記憶體對象的應用計數減1,此時引用計數為1,此時的引用計數的所有者為obj2的retainOject屬性 [obj2 release]; //obj2指向的堆記憶體對象的應用計數減1,此時引用計數為1,此時的引用計數的所有者為obj1的retainOject屬性 // 這個函數都結束了,堆記憶體中還存在兩個相互引用的對象,沒有棧上的指標變數引用它們,無法獲得它們,就無法釋放它們,這樣就找出了記憶體流失
運行結果:
2015-01-18 00:12:13.260 MemoryManager[1186:56593] before
2015-01-18 00:12:13.260 MemoryManager[1186:56593] obj1 retainCount=1 obj2 retainCount=1
2015-01-18 00:12:13.261 MemoryManager[1186:56593] after
2015-01-18 00:12:13.261 MemoryManager[1186:56593] obj1 retainCount=2 obj2 retainCount=2
要想解決改問題就把其中一個設定為弱引用就行了,所以在程式設計時要注意循環參考的出現造成記憶體流失,適當的使用assign屬性來避免循環參考。在使用block的使用,
Block_copy(<#...#>)也會造成迴圈應用,如果在block中使用了一些block外部的變數,你需要通過__weak來修飾弱引用這樣才不會造成循環參考。
如:
__weak ViewController *weakVc = self; dispatch_async(dispatch_get_main_queue(), ^{ weakVc.view.backgroundColor =[UIColor redColor]; });
二.屬性被copy,retain,strong,assign修飾的含義assign修飾的屬性:
#import @interface CycleRetain : NSObject@property (nonatomic,assign)id retainObject;@end
CycleRetain *obj1 = [[CycleRetain alloc]init];//此時 obj1指向的堆對象的引用計數為1 CycleRetain *obj2 = [[CycleRetain alloc]init];//此時 obj2指向的堆對象的引用計數為1 NSLog(@"before "); NSLog(@"obj1 retainCount=%ld obj2 retainCount=%ld",obj1.retainCount,obj2.retainCount); obj1.retainObject = obj2; //因為retainObject是assign屬性所以obj2指向的堆記憶體對象的引用計數此時為1 obj2.retainObject = obj1; //因為retainObject是assign屬性所以obj1指向的堆記憶體對象的引用計數此時為1 NSLog(@"after"); NSLog(@"obj1 retainCount=%ld obj2 retainCount=%ld",obj1.retainCount,obj2.retainCount);
運行結果:
MemoryManager[633:14446] obj1 retainCount=1 obj2 retainCount=1
strong和retain修飾的屬性效果一樣都使引用計數加一:
copy修飾的屬性對象的類型必須實現NSCopying協議,否則會出錯,
總結:記憶體管理就是棧記憶體變數的銷毀,堆記憶體對象如何適當的銷毀,只要記住你想擁有它就retain,或者copy, 你的生命週期結束了就release,如果你對並不是絕對的擁有就使用assign,但是你不能release.所以在編程是把運算式分解為堆棧變數分析,決定堆記憶體變數的生命週期是OC記憶體管理的本質所在。棧上的變數的retain,copy,assign,release決定了堆中的變數的引用計數是增還是減,從而決定堆記憶體變數存亡。