Objective-C内存管理

objective-c内存管理ARC与MRC

内存管理 MRC与ARC

Objective-c中提供了两种内存管理机制MRC(MannulReference Counting)和ARC(Automatic Reference Counting)。

引用计数机制:每一个对象负责维护对象所引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数为0时,该对象就将释放占有的资源。

内存管理概述:为了对一个对象的内存进行释放,引入了引用计数器。有些方法会对计数器加1(retain),有些方法会对计数器减1(release),当对象的引用计数器的值为0时,才会释放内存。并不是每一次release就会销毁对象。

MRC

MRC 如果需要持有一个对象,那么对其发送retain 如果之后不再使用该对象,那么需要对其发送release(或者autorealse) 每一次对retain,alloc或者new的调用,需要对应一次release或autorealse调用。

在MRC的内存管理模式下,与对变量的管理相关的方法有:retain,release和autorelease。retain和release方法操作的是引用记数,当引用记数为零时,便自动释放内存。并且可以用NSAutoreleasePool对象,对加入自动释放池(autorelease调用)的变量进行管理,当drain时回收内存。1

一个对象的创建可以通过alloc分配内存或者copy复制对象,这关系到的方法有:alloc、allocWithZone:、copy、copyWithZone:、mutableCopy、mutableCopyWithZone:这些方法可以使引用计数为1,retain会使引用计数加1,release会使引用计数减1。谁申请的内存,谁就要负责release,必须成对儿出现,否则会出现内存泄漏或者空指针。

重写dealloc方法: 目的是为了释放对象的成员变量。这对象引用计数为0的情况下,系统会调用dealloc方法释放内存。调用dealloc方法时,会先把类对象的成员变量都给释放掉,再释放类对象自身。

Objective-C code 1.0 重写dealloc方法

- (void) dealloc
{
	NSLog(@"释放song对象...");
	[title release];
	[artist release];
	[super dealloc];		//调用父类的释放方法,把父类释放了,也就把自己释放了
}

自动释放池:objective-c提供了一个对象容器,每次对象发送autorelease消息时,对象的引用计数并不真正发生变化,而是向内存释放池中添加一条记录。引用计数由池来控制。对象要想纳入内存释放池对象,必须要发送autorelease消息。

Objective-C code 1.1 自动释放池autorelease

//手动将一个对象放到自动释放池中
#import <Foundation/Foundation.h>

int main (int argc, const char *argv[])
{
//创建一个自动释放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

//...需要进行自动释放的对象
//类集构造函数...也有内存,但不需要自己释放,是有池来管理的。
NSArray *weeksNames1 = [NSArray arrayWithObjects: @"星期一", @"星期二", nil];
//实例构造函数
NSArray *weeksNames2 = [[NSArray alloc] initWithObjects: @"星期一", @"星期二", nil];
NSLog(@"retain count: %i", [weeksNames1 retainCount]);

[pool release];
return 0;
}

objective-c的自动垃圾回收机制GC(Grabage Collection)很少使用,因为会过多的消耗内存,减缓内存释放的时间。autorelease并不是立刻释放,而是延时的。如果对实时性要求高,尽量不要使用autorelease。

MRC中属性的内存管理参数 retain/copy/assign

为了保证对get/set方法的读写的一些行为,提出了属性定义的参数。内存管理的这些参数,对get方法没有什么影响,主要是对set方法有影响。调用方法给成员变量赋值是不会发生内存泄漏和空指针异常的;直接赋值会出问题。记住一点:通过方法或者属性来访问成员变量。不要直接使用成员变量,直接使用属性就等于使用了它的set方法。

set方法总的原则:既要保证旧的title应该先release;又要保证新传进来的数据不会改变它的引用计数,所以新传进来的数据要retain一下。retain,该方法的作用是将内存数据的所有权赋给另一指针变量,引用计数加1,即retainCount+=1; release,该方法是释放指针变量对内存数据的所有权,引用计数减1,即retainCount-=1;

Objective-C code 1.2 属性参数代码示例

- (void)setTitle:(NSString *)newTitle
{
	title = newTitle;
}
/*这段代码事实上是有内存泄漏的,当设置一个新的title时,title=newTitle只是将指针改变了,旧的对象并没有释放。所以这样修改:*/
- (void)setTitle:(NSString *)newTitle
{
	[newTitle retain];
	[title release];
	title = [[NSString alloc] initWithString: newTitle];
	[newTitle release];
}

//copy在赋值时将新拷贝一份,拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效
- (void)setTitle:(NSString *)newTitle
{
	[newTitle copy];  //相当于拷贝出一个副本
	[title release];
	title = [[NSString alloc] initWithString: newTitle];
	[newTitle release];
}

//retain参数会在赋值时把新值保留(发送retain消息),此属性只能用于oc的对象类型,不能用于基本类型或者CF类型(Core Foundation)
- (void)setTitle:(NSString *)newTitle
{
	[newTitle retain];
	[title release];
	title = [[NSString alloc] initWithString: newTitle];
	[newTitle release];
}

//assign参数代表设置的时候直接赋值,相当于直接把引用给它了,而不是复制或者保留它。这种机制非常适合一些基本类型。比如NSInteger和CGFloat,或者就是不想直接拥有的类型比如委托。assign相当于如下的写法:
- (void)setTitle:(NSString *)newTitle
{
	title = newTitle;
}

ARC

ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入release或autorelease,就如同之前MRC时你所做的那样

ARC主要提供了4种修饰符,它们分别是:__strong,__weak,__autoreleasing,__unsafe_unretained。在ARC中与内存管理有关的标识符,可以分为变量标识符和属性标识符,对于变量默认为__strong,对于属性默认为unsafe_unretained。

对于变量标识符有:

  • __strong: 表示引用为强引用。对应在定义property时的”strong”。所有对象只有当没有任何一个强引用指向时,才会被释放。注意:如果在声明引用时不加修饰符,那么引用将默认是强引用。当需要释放强引用指向对象时,需要将强引用置为nil。
  • __weak: 表示引用为弱引用。对应在定义property时的”weak”。弱引用不会影响对象的释放。对象的释放依旧是依赖是否有强引用指向它。当对象被释放的同时,指向它的弱引用会被自动置为nil,这个技术叫做zeroing weak pointer。这样能有效的防止无效指针、和野指针的产生。基本所有的outlet都应该用weak。
  • __unsafe_unretained: 这个修饰符在定义property时对应的是”unsafe_unretained”,实际可以将它理解为MRC时代的assign:纯粹只是将引用指向对象,没有任何额外的操作,在指向的对象被释放时依然指向原来被释放的对象(所在的区域)。所以非常不安全。现在可以完全忽略掉这个修饰符了,因为它的产生只是为了兼容ios4以及更低版本的设备。
  • __autoreleasing: 表示autorelease pool中自动释放对象的引用,和MRC时代autorelease的用法相同。定义property时不能使用这个修饰符,任何一个对象的property都不应该是autorelease型的。一个常见的误解是:在ARC时代中没有autorelease,因为这样一个“自动释放”看起来有点多余,这个误解可能来源于将ARC的“自动”和autorelease的“自动”混淆。其实你只要看一下每个IOS App的main .m文件就能知道,autorelease不仅好好的存在着,而且变的更fashion了:不需要手工被创建,也不需要再显示的调用[drain]释放内存池。2
int main (int argc, const char *argv[])
{
	@autoreleasepool{
		return UIApplicationMain(argc, argv, nil, NSStringFromClass([RootiOSAppDelegate class]));
	}
}

对于变量标识符的用法:Number * __strong num = [[Number alloc] init]; 且

在ARC内存管理模式下,其属性的标识符存在以下几种:@property (nonatomic/atomic, assign/retain/strong/weak/unsafe_unretained/copy, readonly/readwrite) Number *num; //默认是unsafe_unretained

其中assign/retain/copy与MRC下property的标识符意义相同,strong类似于retain, assign类似于unsafe_unretained, strong/weak/unsafe_unretained与ARC下变量标识符意义相同,只是一个用于属性的标识,一个用于变量的标识(带两个下滑短线__)。所列出的其他的标识符与MRC下意义相同。

  • 对于assign,你可以对标量类型(如int、float、BOOL)使用这个属性。
  • 对于copy,指定应该使用对象的副本(深度复制),前一个值发送一条release消息。基本上像retain,但是没有增加引用计数,是分配一块儿新的内存来放置它。特别适用于NSString。
  • UI控件用weak;NSString字符串用copy;其他对象一般用strong;特别地,对于NSString对象,在MRC时代很多人喜欢用copy,而ARC时代一般喜欢用strong…

对于ARC的时代理解和运用,还只是停留在语法的层面,以上也只是在网上搜集整理的一些,以后真正用到了再加入理解和补充。



版权声明:自由转载-非商用-非衍生-保持署名 tunnyios 本文永久链接: http://tunnyios.github.io/personal/MRC-ARC