OC语言--内存管理

来源:互联网 发布:gif制作软件中文版 编辑:程序博客网 时间:2024/05/22 19:26

一、内存管理

什么是内存管理?为什么要管理内存?
解释:手机的内存空间是有限的,我们知道如果手机开的应用程序很多,则手机反应会很慢,其原因就是内存占用空间太大,导致系统反应慢。所以在开发中我们要管理内存,将一些不用的变量,或者对象销毁,释放其占用的内存,让其他程序使用。
在OC中我们只需要管理OC对象,OC对象时存放在堆中的,一般系统不会自动回收,所以需要我们开发人员销毁不用的对象。而一般的基本数据类型(int/float/double/char/enum/strut)是存放在栈中的,这些都是系统自动回收的,我们不需要管理。

二、引用计数器

1、引用计数器:每一个OC对象都有自己的引用计数器(占用4个字节),用来记录对象被引用的次数。

2、引用计数器的管理:
1.    当使用alloc/copy/new创建一个新对象的时候,默认引用计数器为1;
2.    对象调用retain方法,计数器+1;
       对象调用release方法,计数器-1;
       对象调用retainCount方法,返回计数器当期数值;
3.当对象的引用计数器数值为0,则系统会将该对象的内存回收
3、对象的销毁:
1.引用计数器为0,系统将回收该对象的内存;
2.当对象被销毁时,系统会自动给对象发送一条dealloc消息(遗言);
3.一般我们会重写dealloc,在重写时一定要调用[super dealloc],且放在最后面;
4、引用计数器方法的基本使用:
1.retain :计数器+1,返回对象本身
2.release:计数器-1,没有返回值;
3.retainCount: 返回计数器当期数值;
4.dealloc:    对象销毁时调用
示例:
Person *p = [[Person alloc] init];    //使用alloc创建,计数器为1[p retain];        // 调用retain,计数器+1,计数器:2[p release];    // 调用release,计数器-1,计数器:1[p release];    // 调用release,计数器-1,计数器:0,对象p销毁,系统调用p对象的dealloc方法,此时p为野指针//p.age = 10;   // p已经被销毁,所以不能被赋值,如果写这句话,则手机会出现闪退现象,运行代码会出现                //message sent to dealloc instance 给已经释放的对象发送一条-setAge消息,系统找不到该方法,这是野指针错误

三、set方法的内存管理:                

一般我们会将计数器的加减放在set方法中,因为set方法为赋值语句,当如果想给一个人的car变量赋值一辆车,则car对象就的计数器就应该加1,因为
有一个人正在使用car,所以car不能随随便便的销毁,所以我们需要修改set方法。
set方法一般为:
- (void)setCar:(Car *)car{    _car = [car retain];}
给当前人的_car变量赋值,但是如果新车换旧车,则这种设计就不合理,例如给p.car=newcar重新赋值,人还是只有一辆车,但是旧车的计数器并未-1,所以set
方法需要重新设计。
set方法的代码规范:
1.基本数据类型:直接复制
- (void)setAge:(int)age{    _age = age;}
2.OC对象类型
- (void)setCar:(Car *)car{    if( car != _car )        //当旧车换新车    {        [_car release];        //将当前车的计数器-1        _car = [car retain];    //当新车赋值给当前_car变量,旧车换新车    }}
dealloc方法的代码规范:
1.[super release]一定要放在最后;
2.对self拥有的其他对象做一次release操作
示例:
- (void)dealloc{    [_car release];    [super dealloc];}

四、总结:

内存管理的原则:1>谁创建,谁release;2>谁retain,谁release

五、补充概念:

1.僵尸对象:所占内存已经被回收的对象,僵尸对象不能再使用。
2.野指针:指向僵尸对象的指针。
3.空指针:没有指向任何东西的指针(值为nil/NULL/0),给空指针发送消息不报错。
4.防止野指针的方法:p=nil,[p release];
5.野指针错误:EXC_BAD_ACCESS:访问一块坏内存(已经被回收的内存)

六、@property参数

@property参数
1.内存管理相关的参数(与set方法相关)
    1>retain : release旧值,retain新值(适用于OC对象类型)
    2>assign : 直接赋值(默认,适用于非OC对象类型)
    3>copy : release旧值,copy新值
 2.是否要生成set方法
    1>readwrite : 同时生成setter和getter的声明、实现(默认)
    2>readonly  : 只会生成getter的声明、实现
 3.多线程管理
    1>nonatomic : 性能高 (一般就用这个)
    2>atomic    : 性能低(默认)
 4.setter和getter方法的名称
    1>setter : 决定了set方法的名称,一定要有个冒号 :
    2>getter : 决定了get方法的名称(一般用在BOOL类型),例如@property(getter = isRich) BOOL rich;
    
@property的一般写法:
@property(nonatomic, retain) NSString *name;@property(nonatomic, assign) NSString *name;

七、循环引用

1.@class仅仅是告诉编译器,某个名称是一个类
@class Car;仅仅是告诉编译器Car是一个类
2.开发中引用一个类的规范:
    1>在.h文件中用@class来声明类;
    2>在.m文件中用#import来包含类中所有的东西;
3.两端循环解决方案:
    1>一端用retain
    2>一端用assign

  八、autorelease

autorelease(since ios5.0)
1.autorelease基本用法
    1>autorelease会将对象放到一个自动释放池中
    2>当自动释放池被销毁时,会对池子里的所有对象做一次release操作
    3>autorelease方法会返回对象本身
    4>调用完autorelease方法后,对象的计数器不变
    5>autorelease可以有多个      
 @autoreleasePool        {            Person *p = [[[Person alloc] init] autorelease];// autorelease只是延迟release释放时间,但不会执行release            p.age = 10;                @autoreleasePool    //@autoreleasePool可以有无限多个                {                    Person *p = [[[Person alloc] init] autorelease];                    p.age = 20;                }        }
2.autorelease的好处
    1>不用再关心对象释放的时间
    2>不用再关心什么时候调用release
3.autorelease的使用注意
    1>占用内存较大的对象不要随便使用autorelease(因为不能控制对象释放的时间)
    2>占用内存较小的对象使用autorelease,没有太大影响
4.错误写法
    1>alloc之后调用了autorelease,又调用release
    
@autoreleasepool     {        Person *p = [[[Person alloc] init] autorelease]; // 计数器:1        [p release];    // 计数器:0,此时对象已经销毁     }    // 再对对象做一次release,但是对象已经不存在了,野指针错误     
    2>连续调用多次autorelease
   
@autoreleasepool     {        Person *p = [[[[Person alloc] init] autorelease] autorelease];//野指针错误     } 
5.自动释放池
     1> 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
     2> 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6.自动释放池的创建方式
     1> iOS 5.0前
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     //在pool对象销毁前的操作
     [pool release];
     //这种方法只做了解,现在已经不用了
    
     2> iOS 5.0 开始
     @autoreleasepool
     {
        //这种方式简单,现在都应用此方法
     }
autorelease的应用:
//1.+ (id)Person    //方法名称与类名称相同{    //return [[[Person alloc] init] autorelease];    // 创建一个可以自动释放的对象    return [[[self alloc] init] autorelease];        //将Person改为self这样此方法可以应用多种类而不仅仅是Person类}//2.+ (id)personWithAge:(int)age                    //可以传递参数{    //Person *p = [[[Person alloc] init] autorelease];    Person *p = [self person];    p.age = age;    return p;}int main(){    @autorelease    {        Person *p = [Person person];    //创建person对象        //Student *s = [Student person];    //如果Student继承Person,则此person方法也可以调用        Person *p2 = [Person personWithAge:20];    }    }
注意:
*系统自带的方法里面没有包含alloc、copy、new,说明返回的对象都是autorelease的;
*OC中不允许有两个类名称相同,例如连个Person,这是不对的。NSString中的NS是前缀;
*开发中经常提供一些类方法,用来快速创建一个已经autorelease的方法。

九、ARC

ARC:自动引用计数。这是编译器的特性,不是自动回收。

ARC的判断准则:

只要没有强指针指向对象,就会释放。

强指针:默认情况下,所有的指针都是强指针__strong;
弱指针:__weak,当对象只有弱指针指向时,系统会回收对象的内存空间。

ARC特点:

1>不允许调用release/retain/retainCount

2>允许重写dealloc,但不允许调用[super dealloc]

3>@property参数

*strong:相当于retain(适用于OC对象类型),成员变量为强指针

*weak:相当于assign,成员变量为弱指针

*assign:使用与非OC对象类型

4>以前的retain改为strong

@property(nonatomic, strong) Car *car;

循环引用(weak指针的应用):
@property(nonatomic, strong) Dog *dog;    //Person.m@property(nonatomic, weak) Person *person;    //Dog.m
两端引用时的解决方案:
*ARC:一端为strong,一端为weak
*非ARC: 一端为retain,一端为assign
0 0
原创粉丝点击