-------------------------------------- android培训、java培训、ios培训期待与您交流! ---------------------------------
iOS下内存管理的基本思想就是引用计数,通过对象的引用计数来对内存对象的生命周期进行控制。具体到编程时间方面,主要有两种方式:
1:MRR(manual retain-release),人工引用计数,对象的生成、销毁、引用计数的变化都是由开发人员来完成。
2:ARC(Automatic Reference Counting),自动引用计数,只负责对象的生成,其他过程开发人员不再需要关心其销毁,使用方式类似于垃圾回收,但其实质还是引用计数。
<span style="font-size:14px;">#import "Person.h"
@implementation Person
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
//重写dealloc方法后的输出,可以实现对象的监听
NSLog(@"Person被回收了");
//这里必须调用[super dealloc]且只能放在最后
[super dealloc];
}
@end </span>
dealloc方法不要直接调用,会引发野指针错误。一旦对象被回收,它所占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)。
/*
1.方法的基本使用
1> retain :计数器+1,会返回对象本身
2> release :计数器-1,没有返回值
3> retainCount :获取当前的计数器
4> dealloc
* 当一个对象要被回收的时候,就会调用
* 一定要调用[super dealloc],这句调用要放在最后面
2.概念
1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const charchar * argv[])
{
Person *p = [[Person alloc] init];
//获取对象引用计数器的值
NSUInteger c = [p retainCount];
//[p dealloc];//dealloc方法不要直接掉用,会引发野指针错误
NSLog(@"%ld",c);//c == 1
//使对象引用计数器的值-1
[p release];
//[p release];//不能多次调用release,会引发野指针错误。
p = nil; //清空指针,使p变成空指针,避免出现野指针错误,
return 0;
}
/*运行结果
2014-04-10 13:00:05.815 7-引用计数器的操作[818:303] 1
2014-04-10 13:00:05.818 7-引用计数器的操作[818:303] Person被回收了
*/
打开你的工程,点击目录的工程文件,最顶端蓝色的,然后选择project下你的工程,还是蓝色那项,然后build Settings,然后往下拉,在Apple LLVM 5.0 - Language - Objective C 里有一个选项,Objective-C Automatic Reference Counting 选择NO,就可以了。
project ->
build Settings ->
Apple LLVM 5.0 - Language - Objective C ->
Objective-C Automatic Reference Counting
基本原则
关于MRR,我总结了一句话:是你的,就是你的;不是你的,就不是你的。
虽然看上去比较废话,但揭示了MRR内存管理里的一个核心原则,“只负责你拥有的对象的生命周期”,也就是说,如果你对一个对象有所有权,那么你就要负责其回收的工作,否则,你不需要,也不能取回收你不拥有的对象。
那些对象属于“拥有”范畴呢?
1:所有使用alloc, new, copy或mutabelCopy,以及这些关键词开头的函数返回的对象,你都是拥有所有权的,也就是要负责这些对象的内存回收工作。这是iOS开发中的一种约定,所以,当你编写自己的alloc, new或copy类型的函数时,也请遵循这样的命名规范。
2:retain返回的对象,拥有所有权。例如显示调用retain函数返回的结果,或者synthesize 的retain类型的成员变量。
3:所有使用其他函数返回的对象,没有所有权。
4:返回的对象的引用,没有所有权。
5:autorelease返回的对象没有所有权。
举例说明:
//1:使用alloc函数生成,有所有权
NSString* stringA = [[NSString alloc] init];
stringA = @"abc";
[stringA release];
//2:使用initWithString返回对象,无所有权
NSString* stringB = [NSString stringWithString: @"abc"];
//3:显示调用retain返回对象, 拥有所有权
NSString* stringC = [[NSString stringWithString: @"abc"] retain];
stringC = @"def";
[stringC release];
//4:retain类型成员变量,拥有所有权
@property (retain) NSString* stringD;
@synthesize stringD = _stringD;
- (void) dealloc
{
[_stringD release];
[super dealloc];
}
//5:通过引用返回的对象无所有权
NSString** stringERef = [self stringRef];
*stringERef = @"abc";
成员变量的内存管理
上面的例子,将成员变量声明为retain并synthesize时,编译器会帮你生成对应变量的setter与getter,其中, 生成的setter形式如下:
- (void) setStringD: (NSString*) newString
{
[newString retain];
[_stringD release];
_stringD = newString;
}
首先获得newString的所有权,然后释放掉已经拥有的_stringD(其实此对象有可能并没有被释放,取决与其release后的引用计数是否为0), 再将newString的值赋给_stringD,这样,获得了新的,释放了旧的,就完成了对象所有权的释放与获得。注意retain与release的调用顺序,避免了同一个对象赋值引起的悬空指针问题。
当成员变量声明为assign和copy时,其生成的setter形式如下:
//copy
- (void) setStringD: (NSString*) newString
{
NSString* temp = [newString copy];
[_stringD release];
_stringD = temp;
}
//assign
- (void) setStringD: (NSString*) newString
{
_stringD = newString;
}
copy类型的成员变量也是拥有所有权的,也要在dealloc函数中显式释放。而assign不是。
很多人对self.stringD = @"abc"的调用形式比较困惑; 其实,编译器会将此语句自动转换为[self.setStringD:@"abc"];
还有一点要注意,声明成员类型时只有copy,没有mutableCopy,那么,如果一个成员变量是MutableArray,且被声明成copy类型,但编译器生成的setter中调用的是copy,而不是MutableCopy,所以,向MutableArray类型的成员插入数据时就会报错,因为其保存的真正类型不是MutableArray,而是Array. 这种情况有两个解决方案:一是将改变量类型声明为retain形式; 二是手动编写setter,调用mutableCopy函数。
NSObject函数声明了dealloc函数来清理内存,所有有“retain, copy”类型成员变量的类都要实现这个函数。在其内,要调用相应对象的release函数,不要忘记在【最后】调用[super dealloc];
说到成员变量的声明周期,还要提一下IBOutlet类型的变量,默认情况下, IBOutlet对象的类型都是retain的,由于这些对象来自界面文件(xib, storyboard),所以其出事化过程无需关心,其他处理方式与普通成员变量大体相同,只有一点,需要在- (void) viewDidUnload函数中将这些变量置空, 如下所示:
- (void) viewDidUnload
{
self.outletA = nil;
self.outletB = nil;
[super viewDidUnload];
}
容器对象与内存管理
iOS中,容器对象对其内的对象拥有所有权,也就是说,当一个对象被插入到容器内时,其retainCount会加一,从容器内移除时,其retainCount会减一,当容器本身被release时,期内所有对象的retainCount都会减一。如下代码所示:
NSString* stringA = [[NSString alloc] init];//stringA的retainCount: 1
NSArray* array = [[NSArray alloc] init];
[array addObject: stringA];//stringA的retainCount:2
[stringA release];//stringA的retainCount:1
[array release];//retainCount: 0
NSNumber *n = [NSNumber numberWithInt:100];
NSString *s = [NSString stringWithFormat:@"jack"];
NSString *s2 = @"rose";
(2) 开发中经常会提供一些类方法,快速创建一个已经 autorelease 过的对象
// 创建对象时不要直接用类名,一般用self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
-------------------------------------- android培训 、 java培训 、ios培训期待与您交流! ---------------------------------