内存管理

【内存管理是做什么事情的】

1.就是把代码中程序员自己开辟的空间释放掉
内存4个区域【栈区、堆区(程序员自己操控)、数据区、代码区】

【问题】
【这里的空间都是堆空间】
1.多次释放一个空间【重复释放】
2.过早释放空间【提前释放】
3.空间开辟没有释放【内存泄露】

【困难】
1.一个堆空间必须等所以使用这个空间的指针使用完毕之后才可以释放
2.一个对空间的释放必须确定哪些指针指向这个空间,这些指针只有一个能释放,避免重复释放
3.模块化处理的时候,到底是哪个模块去处理这个空间,是一个棘手的问题
4.多线程操作,无法确定哪个线程来释放掉这个空间

【解决方案】
1.教室来一个人,开灯,又来十个人,一个人离开不能够立即关灯,要等最后一个人离开才能关灯
2.聊天室;QQ讨论组,一个人建立讨论组,拉进来10个人,一个人推出讨论组我们不能取消讨论组,要等最后一个人退出才可以

【结论】
OC的内存管理,就是采用类似讨论组和教室开灯问题的计数方法,创建一个对象,就会将这个对象的引用计数设置为1,当多出了一个指向该对象的指针,那么计数加1,当少一个指向该对象的指针,该计数减1,减到0,就释放这个堆空间。引用计数,就是在统计指向该空间的指针有多少

【MRC】

手动内存管理 Manual Reference Counting 手动引用计数
1.关闭ARC
工程—>builid Settings—>搜索gar—>YES改为NO

【相关方法】

-(id)retain;
//引用计数+1,并且返回该对象的一个指针
-(void)(release)
//引用计数-1,如果在调用release的时候,引用计数已经为1,那么就释放该对象
-(NSUinteger)retainCount;
//返回当前引用计数
因为OC字符串是在内存的数据区,虽然继承NSObject,拥有引用计数成员变量,但是很遗憾,不能修改

//创建一个对象,这样做,引用计数会设置为1;
//OC中的类,都是直接或者间接继承于NSObject
//在NSObject里面,用一个成员变量count,就是引用计数
//查看引用计数        
NSLog(@"%ld",[dog retainCount]);
//如果我们又有了一个指针指向这个对象,不能直接赋值,需要将引用计数加1
//那么我们就用retain,引用计数加1,并且返回该对象的一个指针
 ZNDog * dog1 = [dog retain];
 dog = nil;//nil是谁都指不进去
//这样是没有意义的,因为dog已经release了
//在底层代码实现中,通过调用release引用计数为1,就直接释放掉了
//不会再去对引用计数-1【当引用计数为1时,调用release,将直接释放,若是不调用release,则不释放,可继续使用】
//NSLog(@"After release %ld",dog.retainCount);【这句话没有意义】【直接释放,没有count--】

【黄金法则】

1.凡是使用alloc/retain/new/copy(开头)/mutableCopy(开头)创建对象的时候,必须使用release或autorelease释放对象
2.谁写alloc,谁写release。哪个类alloc,哪个类release。

//析构函数【自己创建,必须自己释放,否则,会调用父类的release方法只会释放父类的,而不会释放自己的】
//所以,必须自己写release方法
-(void)dealloc{
//注——mArray不能再构造的时候释放,而且在外部函数好像还没有权限来释放
NSLog(@"班级消失了");
[_mArray release];
//当我们释放了子类的成员变量的时候,要调用父类的析构函数,对父类进行释放
[super dealloc];//释放父类
}

//相当于完成了set方法【不加的话会导致内存泄露】
@property (nonatomic,retain)NSMutableArray *mArray;
//如果在MRC中使用@property需要添加属性retain或者copy

MRC指针的偏移

//释放后置为nil,地址还存在,可以继续传参【但无任何操作】
-(void)setName:(NSString *)name//@"我是第一次的字符串"
{
//@"我是第二次的字符串"
//此时,多了一个指向外部传参name的指针
//引用计数器加1
if (_name!=name)//判断是不是新对象
{
    //释放旧的对象
    [_name release];//多次传参,不写这句话的时候,会将之前开辟的空间丢失,内存泄露
    //指向新的对象
    _name = [name retain];
}
}
//析构方法
-(void)dealloc
{
//self.name=nil;是调用set方法对该对象进行release
[_name release];
[_mArray release];//数组的释放
//是借助在系统中set方法中的release对该对象进行释放
//[self setName:nil];
[super dealloc];
}

【注】
1.在使用类的时候,使用构造方法创建的对象,必须使用析构方法进行释放
2.发生指针的转移的时候,首先要释放掉旧的对象指针,再去接受新的对象指针
3.copy mutableCopy:这2个方法只能用于字符串
copy,拷贝的字符串不可修改
mutableCopy,拷贝的字符串可修改
【思考】如果原串是NSString,是否还适合这个规则?【答案:适合】
4.当一个元素添加到数组当中去的时候,会有retain操作,如果我们要从数组中取出一个对象,短时间内使用,不用retain没有任何问题,如果长时间使用,那么就要retain,防止在使用中数组被释放

    //假设要取出数组中的元素
    //短时间持有这个对象首地址,下面这样写还好
    Dog * NoRetain = mArray[1];
    //如果要长时间持有这个对象那么我们需要retain
    Dog * HaveRetain = [mArray[2] retain];
    //防止我们在使用的过程中整个数组release
    //当数组进行release的时候,会自动调用成员的release

在数组中,提前释放,是置为nil,retain无效

【自动释放池】

延时释放

- (void)autorelease;

自动释放池相当于一个数组,用于储存延迟释放的对象地址池释放时会向池中每一个对象发送一次release消息,自动释放要结合自动释放池使用

在IOS开发中,为了迎合ARC,NSAutoreleasePool写成一个关键字

//构建自动释放池对象
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//autorelease自动释放,延时释放
//[dog2 autorelease];   
//[pool release];//pool release之后,count才会改变

 //Dog * dog = [[[Dog alloc]init]autorelease];
 //禁止以上做法
 //原因:在iOS程序中,有无数运行周期,每一个运行周期都会对自动释放池进行一次释放,会处理调用autorelease的对象
 //如上述写法,dog对象就成了局部变量

【自动释放autorelease 使用法则】

1.类方法创建的对象

NSString *str1 = [[NSString alloc]initWithUTF8String:"wer2342342"];
[str1 release];

2.子对象的getter方法必须使用autorelease

NSString *str2 = [dog Name];
[str2 release];

通过get方法,返回去一个成员变量,为了迎合MRC,我们的get方法会有一个自动释放池

二、自动内存管理

Automatic Reference Counting,简称ARC
1、若有MRC的文件,可以转换成ARC文件
【Edit】——>【Convert】——> 【To Objective-C ARC…】
2、ARC和MRC混编
[工程]—>[Build Phases]—>[Compile Sources]—> 双击不想参与ARC的文件-fno-objc-arc

【使用技巧】

1、四个关键字 修饰引用
__strong(强引用) 缺省属性,其修饰的对象指针,指向哪个对象,会对该对象retain,离开哪个对象,会对该对象release。
__weak(弱引用)其修饰的对象指针,指向任何对象都不会retain。这样的指针指向的对象随时可能消失。如果对象消失了,这个指针会自动变成nil。
//在iOS编程中,代理对象使用弱引用。

__unsafe_unretained 其修饰的对象指针,指向任何对象都不retain。当指向的对象消失,该指针不会变成nil,仍然指向已经释放的对象

__autoreleasing 只用来修饰需要被传入地址的指针。如:
__autoreleasing NSError * error; &error;
2.属性的()参数,原则上,不能写retain copy了,只能写strong,如果不想retain,写weak;但实际上ARC对这方面很宽松,写了retain也没关系。

3.id 指向对象,不能用void * p指向对象。

int a;
void * p = &a;
id p = [[NSObject alloc] init];

4.C的结构体中,不能声明对象指针。否则这个指针不会进行内存管理

struct sct{
id obj;
};

5.不能(显式)手动调用父类的dealloc

-(void)dealloc
{
self.name = nil;
//自动调用父类的dealloc
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值