OC内存管理(非ARC)

来源:互联网 发布:春秋与战国的区别知乎 编辑:程序博客网 时间:2024/06/05 16:26

一、基本原理

    1.内存管理的必要性:由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等。如不及时回收内存,可能造成app闪退、崩溃。
    2.管理范围:任何继承NSObject的对象,对其他的基本数据类型无效
       本质原因:因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
    3.引用计数器
     用于计算对象被使用的次数,是一个整数(每个对象都有自己的引用计数器:占4个字节)判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。    

1>当使用alloc、new或copy创建新对象时,新对象的引用计数器被设置为1.

2>当引用计数器为0时,则此对象所占用内存就会被回收。(发送release消息时,计数器减1)

3>给对象发送retainCount消息获取当前的计数器值

注:retain方法返回的是对象本身(有返回值)

    4. 对象的销毁

1>当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。

2>当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)。

3>一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)


二、野指针、僵尸对象和空指针
    1.野指针:指向僵尸对象(不可用内存)的指针(虽然对象回收,但是指针的值还在)。
       注:给野指针发送消息会报错,报错提示:EXC_BAD_ACCESS

    2.僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)


    3.空指针:没有指向任何东西的指针(存储的东西是0,null,nil),给空指针发送消息不会报错
       注:1)给空指针发送消息不会报错。2) 不能使用[p retaion]让僵尸对象起死复生。
三、内存管理原则
    1.原则
      只要还有人在使用某个对象,那么这个对象就不会被回收;
      只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;
      当你不想使用这个对象时,应该让对象的引用计数器-1;

1>谁创建,谁release

a.如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法

b.不是你创建的就不用你去负责

2>谁retain,谁release

只要你调用了retain,无论这个对象时如何生成的,你都要调用release

    2.总结

有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1.


四、内存管理代码规范
    1.只要调用了alloc,则必须要有release(或autorelease)

如果对象不是通过alloc产生,则不需要release。

    2. Set方法的代码规范

1>基本数据类型:直接复制

- (void)setAge:(int)age{    _age=age;}

2>OC对象类型

- (void)setCar:(Car *)car{    // 先判断是不是新传进来的对象    if(car != _car) {        // 对旧对象做一次release        [_car release]; //若没有旧对象,则没有影响        // 对新对象做一次retain        _car=[car retain];    }}

    3.dealloc方法的代码规范

1>一定要[super dealloc],而且要放到最后

2>对self(当前)所拥有的的其他对象做一次release操作

- (void)dealloc{    [_car release];    [super dealloc];}


五、@property参数的内存管理
    1.内存管理的相关参数

retain:生成的setter方法中会release旧值,retain新值。(适用于OC对象类型)

assign:直接赋值(默认,适用于非OC对象类型)

copy: release旧值,copy新值

    2.是否要生成set方法

readwrite:可读写,同时生成setter和getter的声明和实现(系统默认)

readonly:只读,只会生成getter的声明和实现

    3.多线程管理(多个线程同时调用某个方法)

nonatomic:代表方法不要考虑线程安全性问题,告诉系统不在set方法中生成多线程代码。(高性能,禁止多线程,推荐使用)

atomic:代表给方法进行加锁,保证线程安全(默认,低性能)

线程保护机制:防止方法在未写入完成时,被其它线程调用,造成数据错误。

    4.setter和getter方法的名称

setter:setter = 方法名,决定了set方法的名称,方法名一定有冒号:

getter:决定了get方法的名称。(一般用在BOOL类型)

        修改set和get方法的名称,主要用于布尔类型。因为返回布尔类型的方法名一般以is开头,修改名称一般用在布尔类型中的getter。

@propery(setter=setAbc,getter=isRich) BOOL rich;BOOL b=p.isRich; // 调用


六、循环引用问题以及解决
    循环问题引入:每个人有一张身份证,每张身份证对应一个人,不能使用#import的方式相互包含,这就形成了循环引用
    1.@class使用方法:

1>作用:  @class Card    仅仅告诉编译器,Card只是一个类

使用场合:用于.h声明此类,但是不会引入此类的方法和成员变量。

注意点:仅仅声明这个类,不会将此类的方法和成员变量导入,如果需要,则应该在.m文件中#import此类。

2>引用一个类的规范

  a.在.h头文件中用@class来声明类

  b.在.m源文件中用#import来包含类的所有东西

3>@class优点

  a.解决循环包含的问题。- >只在.m 源文件中进行引用(可以循环声明,A中声明B,B中声明A)

  b.提高了性能。->如果被引入类的头文件进行了修改,不需要全部进行重新编译。

4>@class 和 #import 区别

  a.#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;(使用类所有信息,包括成员变量和方法)

    @class方式只是告诉编译器在A.h文件中 ,声明某个类,不知道此类的所有具体信息。(仅仅声明有这个类)

  b.如果有上百个头文件都#import了同一个文件,那么一旦被导入文件的头文件稍有改动,后面引用到这个     

    文件的所有类的头文件都需要重新拷贝,效率较低。(A->B,B->C,C->D.....一旦A变动,则后面都需要重新编译。程序只编译.h文件)

    使用@class方式就不会出现这种问题了,只需在源文件中修改类即可,提高了效率(解决了循环包含问题)

  c.在.m实现文件中,如果需要引用到被引用类的成员变量或方法时,还需要使用#import方式引入被引用类。

    2.两端循环引用

 一端用retain,一端用assign

Card 端 : @property (nonatomic,retain)   Person *person;

Person端:@propertor (nonatomic,assign) Card *card


七、Autorelease
    1.基本用法

1>会将对象放到一个自动释放池中

2>当自动释放池被销毁时,会对池子里的所有对象做一次release

3>会返回对象本身

4>调用完autorelease方法后,对象的计数器不受影响(销毁时影响)

    2.优点

1>不需要再关心对象释放的时间

2>不需要再关心什么时候调用release

    3.使用注意

1>占用内存较大的对象,不要随便使用autorelease,应该使用release来精确控制

2>占用内存较小的对象使用autorelease,没有太大的影响

    4.错误写法

1>连续调用多次autorelease,释放池销毁时执行两次release(-1吗?)

2>Alloc之后调用了autorelease,之后又调用了release。

    5.自动释放池

1>在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。

2>当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

    6.自动释放池的创建方式

1>ios 5.0以前的创建方式

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

......

[pool  release];  // pool drain];用于mac 

2>ios5.0以后

@autoreleasepool

{   // 开始代表创建自动释放池

……

}   // 结束代表销毁自动释放池

    7.Autorelease注意

1>系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate  date];

2>开发中经常会写一些类方法来快速创建一个autorelease对象,创建对象时不要直接使用类名,而是使用self

0 0
原创粉丝点击