iOS与OS X内存管理

声明:

1、如果文章内容涉及到其他已经发表了,但文章中又未提及转载事项,请及时与本人联系。

2、本文为个人理解,如果部分知识点与真实情况有出入,请忽略本文。

3、如本文解释有任何问题导致您的损失,概不负责。

4、本文仅作为个人学习笔记及技术交流之用,大神可以忽略或提意见,但是……不要(或者轻点)喷。。。

 

正文:

1 相关知识点

1.1 内存管理:软件运行的时候,对计算机内存资源的分配和使用进行管理的策略。

 

1.2 引用计数:一种内存管理策略。(以下解释来自百度百科)

当一个新的引用指向对象时,引用计数器就递增;

当去掉一个引用时,引用计数就递减;

当引用计数到零时,该对象就将释放占有的资源。

 

1.3 iOS与OS X的自动释放池:把所有生成的对象放到一个“池子”里,当超过“池子”的作用域,就会自动帮你释放对象,不用你手动release。

 

1.4 iOS与OS X的自动引用计数:“俗称”ARC(Automatic Reference Counting)。

如名,就是iOS和OS X系统内自行处理内存管理,不用你专门敲代码去给对象的引用计数+1、-1。

从iOS 5和OS X Lion开始引入了自动引用计数,所以除非你需要适配在那之前的版本,不然你完全可以打开ARC(想知道怎么开?请自行百度)。

 

1.5 内存泄漏:应该让出的内存资源,却一直占着不放。

 

2 引用计数

2.1 生成对象(+1);

2.2 持有对象(+1);

2.3 释放对象(-1);

2.4 废弃对象(=0)。


3 iOS与OS X的引用计数

3.1 生成对象(alloc/new/copy/mutableCopy)

alloc方法:生成对象,并用某个变量去持有对象;

new方法:也是生成对象,并用某个变量去持有对象([NSObject new]与[[NSObject alloc] init]完全一致);

copy方法:生成对象,并用某个变量去持有对象的副本;

mutableCopy方法:同copy,区别在于生成的对象,copy生成不可变更的对象,mutableCopy则生成可变。

使用以上方法名称开头,并且以驼峰拼写法命名的方法,也意味着自己生成并持有对象。

例如:

allocFirstObject

newSecondObject

copyThirdThat

mutableCopyFourthObject等。

 

3.2 持有对象(retain)

除去上述的四种方法之外取得的对象,可以用retain方法持有该对象。

例如:

id testArr = [NSMutableArray array];
[testArr retain];


3.3 释放对象(release)

自己持有的对象,一旦不再需要,持有者有义务释放该对象。释放使用release方法。

例如:

id testObj = [[NSObject alloc] init];
[testObj release];


3.4 废弃对象(dealloc)

当引用计数为0时,则调用dealloc方法废弃对象。

如果需要显示调用dealloc方法,即在废弃对象后进行必要的操作,则需要调用[super dealloc]。

例如:

- ( void ) dealloc
{
       [superdealloc];
}


3.5 释放之后再次释放

在持有对象之后,需要释放对象,但不能再次释放已经释放的对象。下例会Crash。

例如:

id testObj = [[NSObject alloc] init];
[testObj release];
[testObj release];

 

3.6 释放自己不持有的对象

非自己生成的对象,需要用retain持有,如果没有用retain持有,就释放也会Crash。

例如:

id testObj = [NSMutableArray array];
[testObj release];

 

4 iOS与OS X的自动释放池autorelease

4.1 具体过程:

4.1.1 生成自动释放池;

4.1.2 调用autorelease方法,将对象放进自动释放池中;

4.1.3 废弃自动释放池,所有在池中的对象自动调用release方法;

例如:

@autoreleasepool {
Id testObj =[[NSObject alloc] init];
[testObj autorelease];
}
//也可以通过生成NSAutoreleasePool对象的方式,来生成自动释放池
//通过生成NSAutoreleasePool对象的方式,需要手动废弃自动释放池(使用drain方法)。


4.2 注意事项

4.2.1 当大量读取大容量文件时,要在适当的地方生成、持有或废弃自动释放池。如果不及时释放自动释放池所占用的内存,会导致内存占用过大而Crash。

4.2.2 Cocoa框架中,部分方法返回已调用autorelease方法的对象。具体信息请查阅官方文档。

  

5 iOS与OS X的自动引用计数(ARC)

所有ARC的操作需要在Xcode4.2之后进行,并且打开ARC功能(Xcode4.2之后默认打开)。

 

5.1 变量修饰符

5.1.1 __strong

“强引用”。

生成并持有对象,非生成也可以通过赋值持有对象。

超出作用域时,自动释放。

id类型和对象类型默认添加该修饰符。

例如:

{
       id__strong testObj = [[NSObject alloc] init];
       //;在ARC中,以上写法与id testObj = [[NSObject alloc]init]完全相同
}
//超出范围,自动释放


 5.1.2 __weak

“弱引用”。

弱引用不能持有对象。

不能直接生成,需要间接赋值。

弱变量持有对象时,若对象被废弃,则此变量自动失效或为nil。

防止循环引用,而导致内存泄漏。

例如:

@interface Test : NSObject
{
       id__weak testObj;
}
- ( void ) setObject: (id __strong)obj;
@end
@implementation Test
- ( id )init
{
       self= [super init];
}
- ( void ) setObject: (id __strong)obj
{
       testObj= obj;
}
以下为引用:
{
       idtest0 = [[Test alloc] init];
       idtest1 = [[Test alloc] init];
       [test0setObject:test1];
       [test1setObject:test0];
       //由于testObj为__weak类型,不持有对象,此处不会发生循环引用
}
//如果testObj为__strong类型,则持有对象
//导致testObj一直持有对象,导致对象无法释放==内存泄漏

 

5.1.3 __unsafe_unretained

该修饰符修饰的变量,不属于自动引用计数的范围。

不能直接生成,需要间接赋值。

如果赋值的对象被废弃或不存在,则程序有可能(有可能,即个别情况)崩溃。

例如:

id __unsafe_unretained testObj1 = nil;
{
       idtestObj0 = [[NSObject alloc] init];
       testObj1= testObj0;
       NSLog(@”%@”,testObj1);
}
NSLog(@”%@”,testObj1);
//其中testObj0的对象引用已被废弃,则访问testObj1有可能出现程序崩溃

5.1.4 __autoreleasing

如名,ARC的自动释放池(以下简称“池”)。

在iOS的main函数中就已经生成了池,并且包含程序的所有内容。

不能用生成NSAutoreleasePool的方式,来生成自动释放池,编译器会报错。

正常情况下,如果你用@autoreleasepool生成池,必须显式将对象注册到池中。但也存在个别情况:

(1)非自己生成并持有的对象,会自动注册到池中。

例如:

@autoreleasepool {
       idtestObj = [NSMutableArray array];
       //生成的对象已自动注册到池中
}
 

(2)访问附有__weak修饰符的变量时,会将即将引用的对象注册到池中。

例如:

@autoreleasepool {
       idtestObj0 = [[NSArray alloc] init];
//此处若需将testObj0注册到池中,须显式使用__autoreleasepool修饰符声明
 
       id__weak testObj1 = testObj0;
       //在赋值的同时,将testObj0引用的对象注册到池中
}
 

(3)id的指针、对象的指针变量会自动附上__autoreleasepool修饰符。

例如:

@autoreleasepool {
       idtestObj0 = [[NSObject alloc] init]; // testObj0自动附上__strong修饰符
       id*testObj1 = &testObj0; //*testObj1自动附上__autoreleasepool修饰符
       //此处会报错,因为指针赋值时,修饰符必须一致
}
 

Tips:无论ARC是否开启,_objc_autoreleasePoolPrint();函数都可以打印出已经注册到当前池中所有对象。

 

5.2 iOS与OS X中ARC开启的状态下,注意事项

 

5.2.1 不能使用alloc/retain/release/retainCount/autorelease

估计大家都知道这点,在ARC开启状态下,以上这些方法都是不能使用的。

 

5.2.2 须遵守的命名规则

在ARC未开启时候的命名规则,参考3.1最后一条所述。

而在ARC开启的时候,除去以上规则同样有效外,还要考虑init关键字:

(1)以init开头(并且用驼峰拼写发)命名的方法,必须是一个实例方法,并且需要返回对象。

(2)以init开头(并且用驼峰拼写发)命名的方法,返回的对象并不自动注册到自动释放池中。

 

5.2.3 不要显示调用dealloc

(1)对象被废弃时,不管ARC是否开启,都会调用对象的dealloc方法。

(2)参考(1)所述,可以在类中实现dealloc方法(实现dealloc方法在ARC是否开启之间的区别,请参考3.4)。

例如:

- ( void ) dealloc
{
       //无需[super dealloc]
}


 (3)如上例中所述,在ARC开启的状态下,不要显式调用dealloc。

 

5.2.4 不要使用NSAutoreleasePool

在ARC开启的状态下,使用NSAutoreleasePool类生成自动释放池,编译器会报错。

 

5.2.5 不能使用区域(NSZone)

无论ARC是否开启,最好都不要用(其实我还没去了解这个“区域”是个什么东西o(╯□╰)o)。

 

5.2.6 对象型变量不能成为struct的成员

例如:

struct TestStruct {
       NSArray* array;
       //报错,不能用对象型变量
}
 

5.2.7 __bridge转换

(1)不能显示转换id和void *(C/C++语言中的空指针类型),需要用__bridge关键字进行转换。

例如:

id testObj = [[NSObject alloc] init];
void *testVoid = (__bridge void *)testObj;
id testObj1 = (__bridge id)testVoid;
 

(2)__bridge_retained转换,可使要转换赋值的变量,也持有所赋值的对象。

(3)__bridge_transfer转换,被转换的变量所持有的对象,在转换完成后释放。

 

Tips:Foundation框架与Core Foundation框架的对象互相转换,需要用__bridge关键字进行转换。

 

5.3 iOS与OS X的属性(property)

属性声明的属性与所有权修饰符的对应关系:

属性声明的属性

所有权修饰符

assign

__unsafe_unretained修饰符

copy

__strong修饰符(但是赋值的是被复制的对象)

retain

__strong修饰符

strong

__strong修饰符

unsafe_unretained

__unsafe_unretained修饰符

weak

__weak修饰符

 

5.4 容器

 

5.4.1静态容器(NSArray、NSDictionary、NSSet)

(1)除去__unsafe_unretained修饰符以外,__strong、__weak、__autoreleasing修饰符都能保证其指定变量初始化为nil。

(2)数组超出其作用域,则变量失效、对象释放(蛮押韵的)。

 

5.4.2 动态容器(NSMutableArray、NSMutableDictionary、NSMutableSet)

(1)Objective-C的动态容器使用的基本规则,基本同静态一致。

(2)iOS中C语言的动态容器:

A.声明动态容器,需要用指针。

例如:

id __strong *array = nil;
 

B. 附有__strong修饰符的id型变量初始化为nil,附有__strong修饰符的id型指针变量初始化不一定为nil。

 

C.calloc函数,给附有__strong修饰符的id型指针变量分配内存。

例如:

array = (id __strong *)calloc(entries,sizeof(id));


 D.操作__strong修饰符的变量,需要自己释放所有元素。释放所有元素后,利用free(array)的方法废弃内存块。

例如:

for (NSUInteger i = 0; i < entries; ++i){
       array[i]= nil;
}
free(array);
 

E.以下函数会造成内存泄漏的危险,请慎用:

用malloc函数分配内存(之后,可用memset函数将内存置零);

用memcpy函数拷贝数组元素;

用realloc函数重新分配内存块。

 

F.__weak修饰符与__strong修饰符使用方式相同;但__autoreleasing修饰符存在不可控因素,慎用;__unsafe__unretained修饰符在编译器的内存管理对象之外,只能作为指针类型使用。


参考:

《Objective-C高级编程 iOS与OS X多线程和内存管理》【日】Kazuki Sakamoto  Tomohiko Furumoto 著  黎华 译

你可能感兴趣的:(小白iOS笔记)