Object-c 之内存管理总结
以下仅仅是对内存管理学习的很简单的描述,由于时间问题,自己没有很好的总结整理。写这个很杂的内容 ,日后忘记了这方面的知识,方便可以快速了解这些知识。
主要以下重要几点
1)、引用计数和对象所有权
2)、点语法的内存管理
3)、自动释放池
4)、ARC和垃圾回收机制
5)、内存管理常见的错误
**
*引用计数 retainCount : 在整个程序中,对于内存的管理和对象的销毁,就是了解retainCount的变化。
*所有者,做了某个动作(创建和保留,赋值),他就拥有了一个对象所有权(ownership)
*alloc allocWithZone: copy copyWithZone:, mutableCopy mutableCopyWithZone:
*retain
*release autorelease (不需要某个对象后 ,要释放内存,销毁内存)
*初始化方法时, 直接向对象发送retain消息,retainCount + 1 ;持有对象所有权。
*设置方法中有三点注意:直接赋值,不保留对象; 直接保留对象,在dealloc方法中释放对象; 释放旧对象,保留新对象,在dealloc方法中释放对象
*内存管理规则
Objective-C内存管理准则:
我们可以把上面的接口按对retainCount的操作性质归为两类,
A类是加一操作:1,2,3 (alloc retain copy)
B类是减一操作:4,5(延时释放) (release autorelease)
内存管理准则如下:
1,A与B类的调用次数保持一制
2,为了很好的保障准则一,以实例对象为单位,谁A了就谁B,没有第二者参与
**/
/**
*dealloc方法详解: 当引用计数为0,系统会自动调用dealloc方法,回收内存。 一般都是调用父类的 dealloc 方法
*子类的某些实例是继承父类的,故,需调用父类的dealloc方法,来释放父类拥有的对象
*调用顺序, 子类的对象方法释放完,再父类的拥有的对象释放。与初始化相反
**/
/**
*赋值 assign :直接赋值,默认 retain 保留对象 copy 拷贝对象
*读写 readwrite 生成getter、setter 方法 默认 readonly 生成getter
*原子性 atomic 多线程环境下 ,存在线程保护,默认 nonatomic 不存在线程保护
*了解以上的各个属性 对应的setter and getter methods
* assign 直接赋值,只是一个别名而已
* retain 保留这个对象,两个对象指向同一位置
* copy 开辟了一个新的内存空间,分别指向了不同的内存空间
**/
/**
*自动释放池的解析 自动释放池还需要进一步理解
*1.[pool release] 和 [pool drain] 的区别 前者是要销毁池 ,后者不需要
*ARC技术,自动管理内存,不需要release 和 retain 等等
*他会自动加入内存的控制代码
*垃圾回收机制
**/
代码示例 :
CPU.h
#import <Foundation/Foundation.h>
@interface CPU : NSObject
{
@private
int _cID;
}
//@property int _cID;
-(void) setCID:(int) cID;
-(int) cID;
@end
//cpu.m
#import "CPU.h"
@implementation CPU
//@synthesize _cID;
-(void) setCID:(int) cID{
_cID = cID ;
}
-(int) cID{
return _cID;
}
-(void) dealloc{
NSLog(@"cpu dead : %d",_cID);
[super dealloc];
}
@end
//Laptop.h
#import <Foundation/Foundation.h>
@class CPU;
@interface Laptop : NSObject
{
@private
CPU *_cpu;
}
//@property (assign) CPU * _cpu;
-(void) setCPU :(CPU *) cpu;
-(CPU *) cpu;
@end
// Laptop.m
#import "Laptop.h"
#import "CPU.h"
@implementation Laptop
//@synthesize _cpu;
//-(void) setCPU :(CPU *) cpu{
// _cpu = cpu;
//} // 第一种情况 普通的赋值
//-(void) setCPU :(CPU *) cpu{
// _cpu = [cpu retain]; //2
// NSLog(@"retain :%ld" ,[_cpu retainCount]);
//} // 第二种情况 保留
-(void) setCPU :(CPU *) cpu{
if (_cpu != cpu) { //相同对象 不做任何事
[_cpu release]; //释放了旧对象 当然对 [nil release] 不会产生错误的 如果旧对象为0 了直接会到[cpu dealloc] 中区
_cpu = [cpu retain]; // 保留新对象
NSLog(@"retain :%ld" ,[_cpu retainCount]);
}
} // 第三种情况 保留 优化我们的代码了
-(CPU *) cpu{
return _cpu;
}
-(void) dealloc{
NSLog(@"laptop dead!!!");
[_cpu release]; //必须销毁 不然内存泄露
[super dealloc];
}
@end
//实例二 所有权的问题 如何持有对象
// 1.在没有改变CPU之前,情况是如何的:就是 _Cpu 与powePC 指向同一个对象,其中的释放过程要理解清楚
// 2.在改变了CPU之后,如果没做适当的处理,可能会造成内存泄露,,理解过程就是通过观察retainCount的变化
// 3._cpu 指向了interCpu
//
//4.当旧对象与新对象相同时,可以释放旧对象,保留新对象。当新旧对象相同时,不会做任何事情
//解决办法: 判断语句
CPU *powerPC = [[CPU alloc] init]; //1
[powerPC setCID:100];
Laptop * apple = [[Laptop alloc] init];
[apple setCPU:powerPC];
[powerPC release];
CPU *intel = [[CPU alloc] init];
[intel setCID: 102];
[apple setCPU:intel];
[intel release];
[apple release];
NSLog(@"apple destroy");
//NSLog(@"laptop cpu id : %d",[[apple cpu] cID]);
//dealloc 方法详解
NSString *carName = [[NSString alloc] initWithFormat:@"audi"];//1
Car *car =[[Car alloc] initWithName:carName]; //car 1
[carName release];//0
Engine *v6 = [[Engine alloc] init]; //engine 1
[car setEngine:v6]; //engine 1 但是拥有权交给了Car
[v6 release]; // engine 1 这里释放只是本身对对象的拥有权 ,但是car对象的拥有权
//do something
[car release];//0
//点语法的内存管理 。实例 点语法
Propertydemo *proper = [[Propertydemo alloc] init];
Engine *v7 = [[Engine alloc] init];
[proper setAge:10];
[proper setName:@"zhang"];
[proper setCount:13];
[proper setEngine:v7];
NSLog(@"age = %d, name = %@ , count = %d , engine = %@",[proper age],[proper name],[proper count],[proper engine]);
以上代码时看视频学习的 ,要真正的理解内存管理机制 ,就是要看retainCount的变化 ,要理解遵循内存管理规则,,要知道通过什么方法可以获得对象的拥有权。 在程序完了之后要记得释放内存,销毁对象,不然会造成内存泄露。 要真正的理解其中的原理 就是自己写个程序,通过调试,看变化
有Xcode5 以后 引入ARC 所以不需要手动添加内存的释放方法 , 会自动产生管理内存代码的。所以节省了对内存的考虑因素。
内存管理总结
1. 当使用了new .alloc copy 方法创建了一个对象时,该对象的引用计数为 1 。 当不再使用该对象时,要负责向该对象发送release 或者 autorelease 方法 ,这样该对象将在其使用结束时销毁
2.通过任何其他方法活动对象时,引用计数为 1, 而且已经被自动释放,不要执行任何方法来释放该对象, 如果打算在一短时间内拥有该对象,则需要保留它并确保操作完成时释放它。
3.在程序中 retain 和 release 方法使用次数必须一致