iOS 内存管理

废话少说,进入正题

内存管理其本身就是一个引用计数机制。
每次 创建 (alloc/new/copy/mutableCopy)或者持有对象(retain),使其引用计数 +1;
每次 释放(release)使其引用计数 -1;
当引用计数为 0 时,对象 销毁(dealloc);

内存管理的思考方式

  • 自己生成的对象,自己持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放

这四句话,引出了内存管理的本质:“谁持有,谁释放”
通过代码感受一下:

// 自己生成的对象,自己持有
id obj = [[NSObject alloc] init];

// 非自己生成的对象,自己也能持有
id obj1 = [NSMutableArray array];

// 不再需要自己持有的对象时释放
[obj release]; 
[obj1 release]; 

// 非自己持有的对象无法释放
// 注意 :obj 调用 release 之后已被释放,所以不再持有对象
[obj release];  // 引发崩溃,obj 不再持有对象,无法释放

ARC 和 MRC 的区别
MRC (manual reference counting 手动引用计数)

MRC 中,对象的创建、持有和释放都需要程序猿自己来管理,上代码:

- (void)test
{
	id obj = [[NSObject alloc] init]; // 此时 obj 的引用计数为 1
}

当上面的方法执行完,obj 因为引用计数为 1,系统不会回收,造成资源的浪费。
通常我们的做法是:

- (void)test
{
	id obj = [[NSObject alloc] init]; // 此时 obj 的引用计数为 1
	[obj retain]; // obj 的引用计数 +1,此时 obj 的引用计数为 2
	[obj release]; // obj 的引用计数 -1,此时 obj 的引用计数为 1
	[obj release]; // obj 的引用计数 -1,此时 obj 的引用计数为 0
}

obj 执行了 release 方法后,引用计数 -1,obj 的引用计数此时为 0,系统便会回收这块资源。

当有很多对象需要 release 的时候,脾气再好的程序猿也会焦虑,不仅是要选一个好地方调用,更要细心,以免写漏了

这时便出现了 autorelease,上代码:

- (void)test
{
	id obj = [[[NSObject alloc] init] autorelease]; // 此时 obj 的引用计数为 1
}

有了 autorelease 后,autorelease 会在方法的对象超出其作用域使其引用计数自动 -1。

- (void)test
{
	id obj = [[[NSObject alloc] init] autorelease]; // 此时 obj 的引用计数为 1
	[obj retain]; // obj 的引用计数 +1,此时 obj 的引用计数为 2
	[obj release]; // obj 的引用计数 -1,此时 obj 的引用计数为 1
}

obj 再方法执行完后,引用计数为 1,autorelease 此时会自动 release,系统便会回收此对象的资源。

但是,本着能躺着绝对不坐着的思想,下面介绍ARC

ARC (automatic reference counting 自引用用计数)

通过名字我们便知,手动引用计数(retain,release,autorelease)以后怕是无缘再见了。
上代码:

// 生产并持有对象
id obj = [[NSObject alloc] init];
// 生产并持有对象
id obj1 = [[NSObject alloc] init];

// obj1 赋值给 obj,此时 obj 和 obj1 共同持有同一个对象,
// obj 之前持有的对象立即被自动释放
obj = obj1;

在 ARC 环境下,超出作用域后自动释放对象,所以 obj 和 obj1 也会被释放。
还需要知道的是,在 ARC 中使用 retain、release、autorelease,会引发编译器报错。

ARC 既然这么好,我们就不再需要关心内存管理了吗?


循环引用

直接看案发现场:

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, strong) id strongObj;

- (void)setObject:(id)strongObj;
@end
---------------------------------------------
#import "Person.h"

@implementation Person

- (void)setObject:(id)strongObj
{
    _strongObj = strongObj;
}
@end
---------------------------------------------
#import <Foundation/Foundation.h>

@interface Dog : NSObject
@property (nonatomic, strong) id strongObj;

- (void)setObject:(id)strongObj;
@end
---------------------------------------------
#import "Dog.h"

@implementation Dog

- (void)setObject:(id)strongObj
{
    _strongObj = strongObj;
}
@end
---------------------------------------------
{
	Person *personObj = [[Person alloc] init];
	Dog *dogObj = [[Dog alloc] init];
        
	[personObj setObject:dogObj];
	[dogObj setObject:personObj];
}

上面的代码在超出作用域后,obj 和 obj1 变量自动释放。但是 obj 的 strongObj 强引用着 obj1 的 strongObj,obj1 的 strongObj 强引用着 obj 的 strongObj,就像两只大手,互相牵着,即使代码已超出作用域,对象还是无法得到释放,此时就会发生内存泄漏
循环引用

内存泄漏是指应该销毁的对象在超出其生命周期后依旧存在。

Strong / Weak

上面的例子引出了 strong 这个修饰符:

@property (nonatomic, strong) id strongObj; // 强引用

在声明成员变量时,有很多修饰符,怎样合理使用修饰符便是接下来要讲的。
既然有 strong 这样的强引用修饰符,那肯定会有弱引用修饰符 weak:

@property (nonatomic, weak) id weakObj; // 弱引用

上面的例子中,两个对象的成员变量都为强引用,在对象该被销毁时,因为两个对象的成员变量都强引用着对方,造成内存泄漏。解决这一问题,只需把其中一个对象的成员变量修饰符改成 weak 即可。

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, strong) id strongObj; // 强引用

- (void)setObject:(id)strongObj;
@end
---------------------------------------------
#import "Person.h"

@implementation Person

- (void)setObject:(id)strongObj
{
    _strongObj = strongObj;
}
@end
---------------------------------------------
#import <Foundation/Foundation.h>

@interface Dog : NSObject
@property (nonatomic, weak) id weakObj; // 弱引用

- (void)setObject:(id)strongObj;
@end
---------------------------------------------
#import "Dog.h"

@implementation Dog

- (void)setObject:(id)strongObj
{
    _strongObj = strongObj;
}
@end
---------------------------------------------
{
	Person *personObj = [[Person alloc] init];
	Dog *dogObj = [[Dog alloc] init];
        
	[personObj setObject:dogObj];
	[dogObj setObject:personObj];
}

现在把 Dog 的成员变量修饰符改为 weak,当上面的代码执行完,对象都会被销毁。我们来分析一下上面的执行步骤:

  1. personObj 生成并持有对象;
  2. dogObj 生成并持有对象;
  3. personObj 的成员变量强引用 dogObj;
  4. dogObj 的成员变量弱引用 personObj;

因为 weak 并不会真正持有对象,在方法执行完后,personObj 立即被销毁。dogObj 的成员变量因为是被 weak 修饰的,没有强引用 personObj,所以 dogObj 也会被销毁。
弱引用

常见的 weak 使用场景为 delegate、block、NSTimer 和 weak singleton

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值