2013-07-08 OC内存管理

来源:互联网 发布:简单的化工制图软件 编辑:程序博客网 时间:2024/06/06 13:03

内存管理的必要性
内存空间的有限性,手持移动设备的易发热性

所有权机制
1)只有当你对一个对象做了alloc,copy或retain等操作后,你才拥有它的所有权;
2)当你不在需要使用这个对象是,应该释放你对它的所有权;
3)你不能对你没有所有权的对象执行释放操作;

获得所有权的方法
1)alloc:为一个新对象分配内存,并将应用计数置1;
2)retain:对象的应用计数+1;
3)copy:制造元对象的副本,该副本的引用计数为1,调用拥有该副本的所有权,并在赋值前释放原油对象,然后在进行赋值;

释放所有权
1)release:
2)autorelease:

dealloc方法
1)dealloc方法会在对象引用计数为0时被系统调用;
2)dealloc不需要手动调用,二十让系统自动调用;
3)对象师傅的时候需要把自己所包含的对象变量一并释放掉;

-(void)dealloc

{

    [_name release];//因为name的属性声明是retain,所以在dealloc方法中要释放这个属性

    [super dealloc];//最后释放本类对象

}

4)在dealloc方法中对变量的释放顺序应与初始化的顺序相反,最后调用[super dealloc];

内存管理  
示例一
使用alloc创建对象,则需要在使用完后进行释放
Person *person1 = [[Person alloc]initWithName:@”张三”];
 
NSLog(@”name is %@”,person1.name);//假设从这往后,我们一直都不使用person1了,应该把对象给释放了。
 
[person1 release]; 
示例二
 
Person *person2 = [Person alloc]initWithName:@”李四”];
 
NSString *name = person2.name;NSLog(@”%@”,name);//假设从这以后,我们也不使用person2了。[person2 release];
 
我们不应该释放name,因为name是我们间接获得的,所以没有它的所有权,也不应该对它进行释放。 
 
自动释放池
autorelease pool 在新建一个项目的时候,Xcode会自动帮我们生成。 


便利构造器的管理
有时我们会通过便利构造器来获得一个新的对象,由便利构造器产生的对象不应当由使用者销毁,而是由便利构造器本身完成。 
便利构造器通过在实现中调用autorelease来实现上述功能。 

错误示例一
 

+(id) personWithName:(NSString *)aName
 
{
 
  Person *person = [[Person alloc];
   
initWithName:aName];
   
return person;

 }

它是错误的,因为它返回了新创建的对象以后,类就失去了释放这个对象的机会。

错误示例二
 

+(id) personWithName:(NSString *)aName

{
    Person *person = [[Person alloc];
     
initWithName:aName];
     
[person release];
     
 return person;
 
}
它也是错误的,因为在返回对象的时候,对象 已经被销毁,从而不能使用了。

正确示例

+(id)personWithName:(NSString *)aName

{

    Person *person = [[Person alloc]init];

    [person autorelease];

    return person;

}
它把对象的销毁延迟进行,从而使用者有时间去使用它,而又不必对对象的释放负责。 

#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[])

{ 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

    NSString *string = [[NSString stringWithFormat:@"hello word!"]init];

    NSLog(@"%@",string);

    [pool release];

    return 0;

}
用便利构造器创建一个字符串对象string,则在使用完毕后不用去释放string。除了自动释放池后,string被自动自动销毁。 

设置器的内存管理  
接口文件 (.h)

 
@interface MyClass : NSObject

{

    NSString *_name;

}

@property(nonatomic,retain)NSString *name;

@end

设置器的实现
在设置器中,保持对新传入对象的所有权,同时放弃旧对象的所有权

-(void)setName:(NSString *)aname

{

    if (_name !=  aname)

    {

        [_name release];

        _name = [aname retain];//或者copy    _name = [aname copy]

    }

}

-(NSString *)name

{

    return _name;
}


代码案例

声明文件中(.h)


//

//  Person.h

//  内存管理

//

//  Created by 0101 on 13-7-9.

//  Copyright (c) 2013 PH. All rights reserved.

//


#import <Foundation/Foundation.h>


@interface Person : NSObject

{

    NSString *_name;

}


-(void)setName:(NSString *)newName;

-(id)name;


@end


实现文件中(.m)

//

//  Person.m

//  内存管理

//

//  Created by 0101 on 13-7-9.

//  Copyright (c) 2013 PH. All rights reserved.

//


#import "Person.h"


@implementation Person


-(void)setName:(NSString *)newName

{

    if(_name != newName)//如果当前传入的对象不是新的对象

    {

        [_name release];//那么就将放弃旧对象

        _name = [newName retain];//保持对新对象的所有权

    }

   

}


-(id)name

{

    return _name;

}

@end


main文件中

//

//  main.m

//  内存管理

//

//  Created by 0101 on 13-7-9.

//  Copyright (c) 2013 PH. All rights reserved.

//


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[])

{

    

    /*

    Person *person1 = [[Person alloc]init];

    NSLog(@"retainCount=%ld",[person1 retainCount]);//创建一个新的对象person1,并且alloc 引用计数retainCount=1,获得所有权

    

    NSString *newName1 = [[NSString alloc]initWithFormat:@"名字1"];

    NSLog(@"retainCount=%ld",[newName1 retainCount]);//创建一个新的对象newName,此时他还没有指向person1,因此retainCount还是等1

     

    



    [person1 setName:newName1];//newName初始化的名字1”传给了setName中的参数name,相当于retain了一次,此时retainCount=2

     NSLog(@"retainCount=%ld",[newName1 retainCount]);

    [newName1 release];

    NSLog(@"retainCount=%ld",[newName1 retainCount]);//newName1 release了一次,retainCount=1


    NSLog(@"*******************************");

    

    

    NSString *newName2 = [[NSString alloc]initWithFormat:@"名字2"];//又创建了一个新对象newName2并且alloc

    NSLog(@"retainCount=%ld",[newName2 retainCount]);

    

   [person1 setName:newName2];

    NSLog(@"retainCount=%ld",[newName2 retainCount]);

    [newName2 release];

    NSLog(@"retainCount=%ld",[newName2 retainCount]);

    

    NSLog(@"person`s name = %@",[person1 name]);

    [person1 release];//同时系统调用dealloc方法将@“名字2”也释放了,@“名字2”的引用计数为0

    

    NSLog(@"*******************************");

    

    

    

    NSLog(@"retainCount=%ld",[newName2 retainCount]);

//  NSLog(@"retainCount=%ld",[newName1 retainCount]);


     */

    

    Person *person1 = [[Person alloc]init];//person1 retainCount = 1

    NSString *newName1 = [[NSString alloc]initWithFormat:@"名字1"];//newName1 retainCount = 1

    

    [person1 setName:newName1];//newName retainCount = 2

    [newName1 release];//newName1 retainCount = 1

    

    [person1 setName:[person1 name]];//Person.m的设置器中[_name release] name已经被释放掉了,_name = [newName retain];newName指向了一块释放掉的内存,导致程序崩溃

    NSLog(@"person name = %@",[person1 name]);

    

    return 0;

}





访问器的内存管理
在访问器中不需要retain或release
 示例

用访问器获得的对象,使用完毕后不需要释
放。 

-(NSString *)name
 
{ 
    return name;
 
} 

-(void) printName
 
{ 
    NSString *name = person.name;
    
NSLog(@”%@”,name);

} 



内存管理的常见错误
1)未使用设置器
2)内存泄漏
3)释放了没有所有权的对象

1)使用alloc为对象分配内存,使用-dealloc释放对象所占用的内存,dealloc是个方法
2)使用alloc,new,或者copy构造对象时,对象的retainCount为1;
3)调用对象的retain方法可以增加1 retainCount;
4)调用对象的release可以减少1 retianCount;
5)只有 retain count等于0,系统才会调用dealloc真正销毁这个对象。当对象的retianCount为0时,dealloc会自动调用,释放对象内存(也就是说只有retainCount等于0,系统才会);否则分配给对象的内存将一直占用
6)所有对象可以使用热天C偶耐他属性查看当前的计数值;

引用计数是针对内存的

会影响计数器的操作
1)alloc创建一个对象,并将其引用计数设为1,并拥有对象所有权
2)copy制造一个副本,并将副本的引用计数设为1,并拥有副本所有权
3)retain时对象引用技术加1,并拥有对象所有权
4)release使对象引用技术减1,并放弃对象所有权
5)autorelease向autoreleasepool注册

栈区是不需要进行所谓的内存释放的  实例变量是存放在堆栈中的 

 

属性内存管理
如果属性是对象类型,关键字用retain 

声明成retain属性默认实现方式

-(void)setName:(NSString *)name

{

    if (_name != name)

    {

        [_name release];

        _name = [name retain];

    }

}

-(NSString *)name

{

    return _name;

} 
 


例如:
//在MyClass.h中声明了两个属性

#import <Foundation/Foundation.h>

#import "Student.h"

/*

 班级实体类

 */

@interface MyClass : NSObject

{

    NSString *_name;

    Student *_stu1;//Student类型的学生类

}

@property(nonatomic,retain)NSString *name;

@property(nonatomic,retain)Student *stu1;//因为*stu1属性是对象类型的,所以修饰符用retain

@end


//MyClass.m

#import "MyClass.h"


@implementation MyClass

@synthesize name=_name,stu1=_stu1;

-(void)dealloc

{

    [_stu1 release];

    [_name release];

    [super release];

}

@end


//AppDelegate.m

 Student *s = [[Student alloc]init]; //alloc一次s的retainCount=1,并获得控制权限

//属性的内存管理

 MyClass *cls = [[MyClass alloc]init];

  cls.stu1 = s;//将对象s赋给*cls的属性stu1,此时stu1的retainCount=2,并获得控制权限

  [s release]; //释放对象s,此时s的retainCount=1;

内存释放原则一:
1)对象分配内存,并应在使用后,调用release释放内存;
2)凡是出现retain,copy,alloc的地方,都应出现release与之匹配;
3)如果声明属性是对象类型(var),属性关键字用retain,且在此类delloc方法中把_var实例变量release一次;



autorelease的使用之处
1)在便利构造器中使用,也就是
使用便利构造器获得对象,都(应)是autorelease的
2)内存无法确定释放时间时,可以使用autorelease相最近的池注册
3)有池决定释放所有权时间
 


原则
1.黄金法则:当有权利使用alloc copy retain,new,就有义务release
2.内存管理对基本数据类型无效,有效于其他数据类型
3.用4个字节来存放引用的次数
4.oc内存管理为了解决c/c++内存溢出的问题
5.alloc初始化计数器+1  retain计数器+1  release计数器-1  当计数器为0对象生命周期结束  (遛狗原理) 


自动释放池
当创建的对象未来某个时候销毁时,可以使用对象的autorelease 

内存检测的两个方法
1)选中文件-->Build Setting-->All-->输入static-->选择Run static Analyzer-->选中YES
2)product-->profile-->Leaks-->
小结
 




初始化方法写完后,立马写dealloc方法