ios内存管理很简单
作者QQ:415074476
QQ群:191280586
Ios内存篇
引用计数
+alloc和-copy生成的对象retain计数值为1
-retain增加 retain计数
-release减少 retain计数
当retain计数减为0时,对象被释放.
-dealloc将被调用.(具体调用时间由系统决定)
一旦dealloc方法被调用,对象就直接释放了,原则上是不允许程序员调用的..
代码1:
implementation persion
{
-(NSString *)name{
return name;
}
-(void)setName:(NSString *)newName{
name = newName;
}
}
问题:name = newName,相当于只是给newName指向的对象增加了一个别名为name,并没有增加retain.
只有创建和复制时,对象的retain数值为1.之后也只能通过retain和release方法来改变retain计数.
代码2:
-(void)setName:(NSString *)newName{
if(name)
{
[name release];//先把之前引用的对象retain减一
}
else
{
name = [newName retain];//再增加当前指向对象的retain
}
}
代码3:
-(void)setName:(NSString *)newName{
if(name)
{
[name release];//先把之前引用的对象retain减一
}
else
{
name = [newName copy];//实际上内容还是使用的retain方法@@
}
}
代码2和代码3都能工作.他们的区别是什么呢?
简单来说:
retain只是把原有对象引用数增1.
字面上看来,Copy是复制原来的对象数据,生成了一个全新的对象.原来的对象retain不变.新对象retain为1.实际上这信赖于copy方法的具体实现,对于不可变对象来说,复制的只是一个指针而已,效果同retain.
由于NSString对象是不可变对象,代码2和代码3实际上就是一样的.
Property 本身的属性
• 只读与读写均可
@property int age; // 默认读写均可
@property (readonly) BOOL canLegallyVote;
• 内存管理策略(仅针对对象property)
@property (assign) NSString *name; // 指针赋值
@property (retain) NSString *name; // 调用retain
@property (copy) NSString *name; // 调用copy
• 不同于默认的setter/getter方法
@property (getter=getAge, setter=setAge) int age;
点语法使用注意:
@implementation Person
– (void)doSomething {
name = @”Fred”; // 直接访问实例变量!
self.name = @”Fred”; // 调用访问方法
}
例子:
@interface Test
{
NSString *name;
}
@property(retain) NSString *name;
-(id)initWithName: (NSString *)_name;
@end
@implementation Test
-(id)initWithName: (NSString *)_name
{
if (self == [super init])
{
name = name;//1错误,引用计数没有加1,有风险.
name = [_name retain];//2正确
self.name = _name;//3正确
[self SetName: _name];//4正确
}
}
-(void)resetName: (NSString *)_newName
{
name = name;//1错误,引用计数没有加1,有风险.
name = [_name retain];//2错误,之前持有的对象没有release
self.name = _name;//3正确
[self SetName: _name];//4正确
}
-(void)dealloc
{
[name release];
[super dealloc];
}
@end
总结,定义了property的属性,建议用点语法
Autorelease的使用.
在函数中返回的对象要使用autorelease
-(NSString *)testLocalVariable
{
NSString *dd = [[NSString alloc] initWithString:@”this is dd”];
NSString *str = [[[NSString alloc] initWithFormat:@”%@”,dd] autorelease];//这里要注意.一定得用autorelease
[dd release];
return str;
}
可以想像代码调用处会是这样的:
NSString *test = [self testLocalVariable];
根据前面的规则,通过alloc,copy,retain调用的对象才应该调用release来降低retain计数.
所以test对象是不应该管理的.
但函数内部又不能release,否则返回的对象就是无意义的对象,故只能打上autorelease 标记,由@autoreleasepool来管理
在Iphone项目中,大家会看到一个默认的Autorelease pool,程序开始时创建,程序退出时销毁,按照对Autorelease的理解,岂不是所有autorelease pool里的对象在程序退出时才release, 这样跟内存泄露有什么区别?
答案是,对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。
那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。例子如下:
NSString* globalObject;
– (void)applicationDidFinishLaunching:(UIApplication *)application
{
globalObject = [[NSString alloc] initWithFormat:@”Test”];
NSLog(@”Retain count after create: %d”, [globalObject retainCount]); // output 1.
[globalObject retain];
NSLog(@”Retain count after retain: %d”, [globalObject retainCount]); // output 2.
}
– (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(@”Retain count after Button click runloop finished: %d”, [globalObject retainCount]);
// 输出1. Button click loop finished, it’s autorelease pool released, globalObject get released once.
}
-(IBAction)onButtonClicked
{
[globalObject autorelease];
NSLog(@”Retain count after autorelease: %d”, [globalObject retainCount]);
// 输出2。 Autorelease被call, globalObject被加如当前的AutoreleaePool。
}