1.内存管理的黄金法则:The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retain is in charge of the corresponding [auto]release. 即:如果一个对象使用了alloc,[mutable] copy,retain,那么你必须使用相应的release或autonrelease.
2.规则:
1、Objective-C类中实现了引用计数器,对象知道自己当前被引用的次数
2、最初对象的计数器为1
3、如果需要引用对象,可以给对象发送一个retain消息,这样对象的计数器就加1
4、当不需要引用对象了,可以给对象发送release消息,这样对象计数器就减1
5、当计数器减到0,自动调用对象的dealloc函数,对象就会释放内存
6、计数器为0的对象不能再使用release和其他方法
3.内存问题主要有:内存溢出 内存过渡释放 野指针 三种
内存溢出(内存堆积):大量的开辟空间但并不立即释放,通常是由于使用autorelease产生的.
内存过渡释放:对象将内存已经还给系统,之后再次归还给系统这时就会出现内存过渡释放的问题.
野指针:对象指向的内存空间已经归还给系统但此时再次调用了指向该指针空间的指针,就会产生野指针的问题.
4.内存管理的形式:MRC (manual reference count) 人工引用计数机制 内存的开辟和释放都有手动代码进行控制.ARC(auto reference count )自动引用计数机制 不需要人工控制系统自动进行管理.
5.利用遛狗来理解内存问题 请参考博文中的遛狗问题分析 特别注意内存处理的setter和getter方法
+alloc 加号方法 买狗 开辟空间 引用计数+1 对应于release -retain 不买狗只在狗的身上套绳子 前提是要有对象 引用计数+1 对应于release-copy 按照原来狗的标准进行再买个狗 有了新的对象 开辟了新的空间相当于一个新的alloc 引用计数+1
-release 在对象原来基础上 引用计数立即-1 对应于retain-autorelease 将对象放到自动释放池里 当自动释放池结束时会执行release操作 引用计数-1
6.内存中的相关问题
1.只有在堆区才会有内存管理的问题,栈区 常量区 全局区等都不涉及内存管理的问题都是有系统自己来管理.
如下例子:
NSString *per/*(栈区) */= [[NSString alloc/*(堆区)*/] init];//0~1 [per retain];//计数1~2 [per retain];//计数2~3 内存没释放 内存泄露 此时栈区的对象没有释放 per = @"aa";//常量区将地址给per 地址发生变化 计数为无穷大 [per release];//常量区内存系统自动释放 不需要人工释放 [per release]; }
2.自动释放池.autorelease将对象放到离它最近的释放池中,当释放池销毁时,会想池中的每一个对象发送一个release消息,每一个对象计数减一.
如下例子: 特别注意输出是顺序 即:dealloc在什么时候执行的
重写的dealloc
- (void)dealloc{ NSLog(@"哈哈"); [super dealloc];}
自动释放池autorelease操作释放的时机
@autoreleasepool { NSLog(@"%lu",[per retainCount]);//1 [per release];//1~0立即减一 //autorelease作用:将对象放入离它最近的释放池中. //当自动释放池销毁时,会向池中的每一个对象发送release消息. [per autorelease];//1~0 /**3. * 1 test 哈哈(重写了dealloc方法) */ @autoreleasepool { [per autorelease]; //1~0放到小池子里 [per autorelease]; //过渡释放 /**2. * 1 哈哈(重写了dealloc方法)
test */ } NSLog(@"test"); [per autorelease]; //过渡释放 /**3. * 1 test 哈哈(重写了dealloc方法) */ }
例2:有没有内存问题,如果有请修改?
@autoreleasepool {
for (int i = 0; i < 10000000; i++) { Person *per = [[Person alloc] init]; [per autorelease]; }
}
有:内存堆积 只开辟空间 不释放 要理解autorelease的处理机制
1.处理方式一 autorelease是在销毁释放池是将引用计数减一
@autoreleasepool {
for (int i = 0; i < 10000000; i++) { @autoreleasepool { Person *per = [[Person alloc] init]; [per autorelease]; } }
}
2.处理方式二 release是立即将引用计数减1
@autoreleasepool {
for (int i = 0; i < 10000000; i++) { Person *per = [[Person alloc] init]; [per release]; }
}
7.对于野指针问题最好的解决方案是:将对象赋值=nil .
8.重写dealloc方法
当该类型的对象引用计数为零时 系统会自动调用dealloc方法来回收空间 系统自动调用 不需要手动回收.
验证对象空间有没有回收,只要查看该类的dealloc方法有没有执行即可,回收执行,不回收就不调用.- (void)dealloc{ NSLog(@"哈哈"); [super dealloc];}
9.内存管理高级
1.属性的内部实现原理和管理内存机制.
1.当实例变量属性声明为retain时,它的setter和getter方法及getter方法的内部实现是
@property (nonatomic,retain) Teacher1 *teach;
//setter以及getter方法内部是对实例变量进行赋值和取值操作,所以方法内部要操作实例变量//if判断是判断原有对象是同一个,如果是同一个人就没与必要重新赋值,否则就显release,release之后空间系统被回收 此时如果retain就成为野指针问题.- (void)setTeach:(Teacher *)teach{ if (_teach != teach) { //如果是不同的空间 //setter是给实例变量赋值来用 [_teach release];//先将原来的保有的所有权释放计数减1 解决内存泄露问题 //让实例变量保有新的所有权 解决野指针问题 _teach = [teach retain]; //对于将来要做的一些对象,可以持有一份 将引用计数加1 解决野指针问题 // self.teach = teach;死循环了 不能调用自己 }}
-(Teacher1 *)teach{ return _teach;}
对应的重写dealloc方法
- (void)dealloc{ //当该类对象的引用计数为零会自动调用该类的dealloc方法 //当调用dealloc方法时本类对象的空间将会被回收 在空间回收之前,将保有的其他对象的所有权给释放掉. [_teach release];//释放retain的所有权 引用计数-1操作 [_teach1 release];//只要有retain就要释放 NSLog(@"hahahha"); [super dealloc];//继承父类的dealloc方法.}
2.当实例变量属性声明为copy时,它的setter和getter方法及getter方法的内部实现是
@property (nonatomic,copy) Teacher1 *teach1;
//copy的内部实现形式-(void)setTeach1:(Teacher1 *)teach1{ if (_teach1 != teach1) { [_teach1 release];//如果相对一个对象copy操作,对象的类必须符合一个NSCopying协议,并且实现协议中的方法//解决方案:实现协议操作 _teach1 = [teach1 copy]; }}-(Teacher1 *)teach{ return _teach;}
如果要声明属性为copy时就要使用copy所要满足的协议
@interface Teacher1 : NSObject<NSCopying>{ //姓名 NSString *_name; //性别 NSString *_gender;}@property (nonatomic,retain) NSString *name;@property (nonatomic,retain) NSString *gender;- (id)initWithName:(NSString *)name gender:(NSString *)gender;+ (id)teacherWithName:(NSString *)name gender:(NSString *)gender;- (void)dealloc;@end
@implementation Teacher1//实现协议- (id)copyWithZone:(NSZone *)zone{ Teacher1 *newTeacher = [[Teacher1 allocWithZone:zone]init];//开辟空间 alloc操作引用计数加1 对象发生变化 newTeacher.name = self.name;//赋值操作 newTeacher.gender = self.gender; return newTeacher;//返回对象}- (id)initWithName:(NSString *)name gender:(NSString *)gender{ self = [super init]; if (self) { // _name = name;可能会出现野指针问题 相当于赋值操作 引用计数不会加1 self.name = name;//内部调用setter方法添加了retain操作 保有了对象的所有权 引用计数+1 self.gender = gender; } return self;}+ (id)teacherWithName:(NSString *)name gender:(NSString *)gender{ return [[[Teacher1 alloc]initWithName:name gender:gender ] autorelease];}- (void)dealloc{ NSLog(@"%@空间被回收了",self.name); [_gender release]; [_name release]; [super dealloc];}@end
2.便利构造器方法的内部实现及内存管理 内部采用的是autorelease所以会造成内存堆积 Teacher1 *tea = [Teacher1 teacherWithName:@"Frank" gender:@"man"];0 ~ 1 NSLog(@"%lu",[tea retainCount]); 下面的是内存堆积的问题 因为便利构造器内部用的是autorelease for (int i = 0; i < 100000000; i++) { NSString *str = [NSString stringWithFormat:@"aaaaa"]; }
便利构造器的内部实现
内部对应的有alloc 就对应的有autorelease 所以当销毁自动释放池时就将引用计数减一 对象所占的内存回收
+ (id)teacherWithName:(NSString *)name gender:(NSString *)gender{ return [[[Teacher1 alloc]initWithName:name gender:gender ] autorelease];}
3.collection的内存管理 自主管理内存
1.当一个对象加入集合(数组 字典 集合)时,对象的引用计数会retain操作
2.当一个对象移除集合(数组 字典 集合)时,对象的引用计数会release操作
3.当集合collection空间回收时,他们会向空间中每一个对象发送一个release消息(对应添加元素时的retain操作)