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 <Foundation/Foundation.h>

@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決定了堆中的變量的引用計數是增還是減,從而決定堆內存變量存亡。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章