iOS-内存管理就这么简单2

来源:互联网 发布:信用卡可以在淘宝套现 编辑:程序博客网 时间:2024/04/29 00:54

文明转转和评论是对自己的尊重也是对学者的鼓励,谢谢

  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决定了堆中的变量的引用计数是增还是减,从而决定堆内存变量存亡。






1 0
原创粉丝点击