注意网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得 retainCount都是不准确的,特别是在对象拷贝操作之后你会发现二者取值也是不同的,因此如果大家要查看retainCount最好还是暂时关闭ARC。
内存管理的知识点
1.让对象引用计数增加的9个操作
alloc,new,copy,retain,addSubView,addObject,push,对象打点属性赋值,控件关联xib
2.引用计数减少的两个操作 release autorelease
3.要保证让对象引用计数增加的关键字跟让对象引用计数减少的关键字 配对
4.全局变量或者属性在dealloc中释放,局部变量在确定不使用以后就要释放
5.使用+号方法(静态方法)创建的对象不需要管理内存,内部自动-1
6.在方法中创建的对象如果要返回需要使用autorelease,在使用的时候手动retain
7.全局对象变量在初始化的时候一般使用-号方法创建,使用+号方法创建需要手动retain,使用字面量创建需要手动retain
8.分线程中的所有代码都用@autorelease括起来,防止内存泄露
9.对象在释放的时候除了[obj release];以外,为了安全起见,加上obj=nil;
10.属性直接self.obj = nil;即可,相当于[obj release];obj=nil;
ARC和MRC的关系
ARC是编译器特性,不是运行时特性.简单说就是代码在编译的时候自动加入了retain/release/autorelease,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了.因此ARC和MRC性能是一样的,有些时候还能更快,因为编译器还可以执行某些优化
内存管理的小技巧
1)利用你静态分析工具让系统自动检测内存泄露的代码(只能检测一部分)
1.选中项目工程文件
2.中间部分,选择蓝色的project
3.选中build settings,搜索Analyze During
4.把Analyze During ’Build’修改为YES
打印变量的地址,显示0x0是代表没有存储空间吗?
不是,标识地址位于0,但一般系统中的这个位置是不允许你的变量使用的,所以一般表示这个地址是无效的。
NSDate *today=nil;
NSLog(@"%p",today);
NSLog(@"today指针内存地址:%x",&today);
NSLog(@"today指针所指向对象的地址:%p",today);
today指针内存地址:5e991a10
today指针所指向对象的地址:0x0
:指针所指向对象为nil,程序输出0x0,堆上内存首地址,它一般表示0,today指针就是空对象的指针,而在堆上没有任何对象
栈是由有序的帧构成的,函数的帧在函数结束后会自动释放,而堆里的对象不会自动释放。因此管理堆很重要,程序占用堆大小是有限的。当堆中的对象没有被引用,这时就应该销毁。每个对象都会对指向自己的指针进行计数,当引用计数为0时,就会认为不需要该对象了。如果项目开启了ARC,编译器会自动给项目添加代码来计算每个对象的引用数。
只要有一个指针指向对象,这个对象就会继续存在。因此调用free()函数来销毁对象是清理最干净的方法。如果不再需要某个对象,则可以将指向它的指针设置成nil,或者销毁它的指针。
已经释放的数据再次使用/释放(过度释放)会崩溃,通常会出现Thread 1:EXC_BAD_ACCESS'(code=EXC_I386_GPFLT)错误,这是野指针错误,因为你访问了一块已经不属于你的内存 解决办法:[objrelease]; obj = nil;因为给空对象发送消息是不会引起错误的
NSString内存分配结论:
@“” 和 initWithString:方法生成的字符串分配在常量区,系统自动管理内存;
initWithFormat:和 stringWithFormat: 方法生成的字符串分配在堆区,autorelease
浅拷贝:只增加了一个指针指向已经存在的内存
深拷贝:增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存
关键字copy:
// 1、strong
@property (nonatomic,strong) NSString *str;
// 2、copy
@property (nonatomic,copy) NSString *str;
// 分别对1和2执行下述代码
NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"123"];
self.str = mutableStr;
[mutableStr appendString:@"456"];
NSLog(@"%@",self.str);
NSLog(@"%p",self.str);
NSLog(@"%@", mutableStr);
NSLog(@"%p", mutableStr);
// 结果1
2016-10-21 14:08:46.657 Memory[68242:5714288]123456
2016-10-21 14:08:46.657 Memory[68242:5714288]0x608000071040
2016-10-21 14:08:46.657 Memory[68242:5714288]123456
2016-10-21 14:08:46.657 Memory[68242:5714288]0x608000071040
// 结果2
2016-10-21 14:11:16.879 Memory[68264:5716282]123
2016-10-21 14:11:16.880 Memory[68264:5716282]0xa000000003332313
2016-10-21 14:11:16.880 Memory[68264:5716282]123456
2016-10-21 14:11:16.880 Memory[68264:5716282]0x60000007bbc0
- 结果1为strong修饰的结果,可见 [mutableStr appendString:@"456"]修改mutableStr造成了self.str的改变,显然不安全;结果2为copy修饰的结果,可见 [mutableStr appendString:@"456"]修改mutableStr未造成self.str的改变,显然安全。(从内存地址的变化也可以看出来)
- 这里可以推测出,copy关键字是在str属性的set方法里面返回了mutableStr的copy,而strong关键字仅仅是返回了mutableStr。
MyClass.h
@interface MyClass : NSObject {
MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end
MyClass.m
@synthesize myObject;
-(id)init{
if(self = [super init]){
MyObject * aMyObject = [[MyObject alloc] init];
self.myObject = aMyObject;
[aMyObject release];
}
return self;
}
有人就问, 为什么要这么复杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?
现在我们来看看内存管理的内容:
先看间接赋值的:
1.加self.:
MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
self.myObject = aMyObject; //myObject retainCount = 2;
[aMyObject release];//myObject retainCount = 1;
2. 不加self.:
MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
myObject = aMyObject; //myObject retainCount = 1;
[aMyObject release];//对象己经被释放
关于set方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:
assign,用于基本数据类型
-(void)setA:(int)a{
_a=a;
}
retain,通常用于非字符串对象
-(void)setA:(Car *)a{
if(_a!=a){
[_a release];
_a=[a retain];
}
}
copy,通常用于字符串对象
-(void)setA:(NSString *)a{
if(_a!=a){
[_a release];
_a=[a copy];
}
}
参考:http://www.jianshu.com/p/bab14c447aaf