内存管理总结-autoreleasePool

序言

无论是在MRC时期还是ARC时期,做过开发的程序员都接触过autoreleasepool。尽管接触过但本人对它还不是很了解。本文只是将自己的理解说出来。在内存管理的文章中提到了OC的内存管理是通过引用计数来完成的,也介绍了可以通过内存管理的方法(alloc/retain/new/copy等)来使引用计数加1,使用release方法来使引用计数减1。在我们创建了大量对象的时候,如果还是手动调用release方法来释放它们就显得太繁琐了。本文章将介绍内存管理的另外一种机制-autoreleasepool。

autoreleasepool概念

自动释放池是NSAutoreleasePool的实例,其中包含了收到autorelease消息的对象。当一个自动释放池自身被销毁(dealloc)时,它会给池中每一个对象发送一个release消息(如果你给一个对象多次发送autorelease消息,那么当自动释放池销毁时,这个对象也会收到同样数目的release消息)。可以看出,一个自动释放的对象,它至少能够存活到自动释放池销毁的时候。这样看来它是一种延迟释放机制,这样保证局部堆上的变量能够被外部正常使用。

这里说一下,在Xcode5以前是通过NSAutoreleasePool创建实例来实现的,代码如下:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// code
[pool drain];

但是在Xcode5以后,它的写法就简单了,代码如下:(所以本文的代码主要以这种写法来讲解)

@autoreleasepool {
    // code
}

既然autoreleasepool也是一个对象,它在内存中以什么结构进行存储呢?它存储于内存中的栈中,遵循”先进后出”原则。

下面通过代码来简单的看一下autoreleasepool是如何进行内存管理的。

新建一个HXPerson类,重写其dealloc方法,代码如下:

- (void)dealloc {
    NSLog(@"HXPerson dealloc");

    [super dealloc];

}

无autoreleasepool情况:

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

    HXPerson *person = [[HXPerson alloc] init];
    [person release];
    return 0;
}

有autoreleasepool情况:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 根据上面介绍的,我们要在初始化的时候,调用autorelease方法
        HXPerson *person = [[[HXPerson alloc] init] autorelease];
    }
    return 0;
}

可以看到,使用autoreleasepool的情况就算没有调用release方法,该person对象也被销毁了。但是在创建person对象的时候一定要调用autorelease方法。该方法主要的作用就是将person对象放在该autoreleasepool中,且person对象在该autoreleasepool没有销毁之前一直是有效的,也就是说该person对象可以被访问,直到该autoreleasepool被销毁。只要autoreleasepool被销毁,放在autoreleasepool里面的所有对象(调用过autorelease的对象)都会自动执行一次release方法来销毁对象。

autorelease作用:

  1. 对象执行autorelease方法时会将对象添加到自动释放池中
  2. 当自动释放池销毁时自动释放池中所有对象作release操作
  3. 对象执行autorelease方法后自身引用计数器不会改变,而且会返回对象本身
  4. autorelease实际上只是把对象release的调用延迟了,对于对象的autorelease系统只是把当前对象放入了当前对应的autorelease pool中,当该pool被释放时([pool drain]),该pool中的所有对象会被调用Release,从而释放使用的内存。这个可以说是autorelease的优点,因为无需我们再关注他的引用计数,直接交给系统来做!
  5. 对于操作占用内存比较大的对象的时候不要随便使用,担心对象释放的时间太迟,造成内存高峰, 但是操作占用内存比较小的对象可以使用

在创建对象的时候,调用autorelease,就能将该对象放到autoreleasepool中。利用autoreleasepool的延迟释放来管理内存。autoreleasepool这么重要,可是我们在实际开发中并没有手动创建autoreleasepool,却没有内存泄露。这是为什么呢?其实没有手动创建并不代表它不会被创建,那么它是什么时候创建的呢?

autoreleasepool创建

上篇文章中讲到runLoop的时候就提到autoreleasepool。 App启动后,系统在主线程runLoop里注册两个Observser,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其优先级最高,保证创建释放池发生在其他所有回调之前。第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠)
时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 优先级最低,保证其释放池子发生在其他所有回调之后。在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被runLoop创建好的AutoreleasePool环绕着,所以不会出现内存泄漏,开发者也不必显示创建Pool。可见开发过程中我们没有创建autoreleasepool,系统也会帮我们创建。这就解释了,为什么开发中没有创建autoreleasepool也没有内存泄露的原因了。关于runLoop

通过下面的例子,我们来看一下,runLoop创建的autoreleasepool是不是真的帮我们管理了内存。

__weak id reference = nil;
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *str = [NSString stringWithFormat:@"autoreleasePool"];
    // str是一个autorelease对象,设置一个weak的引用来观察它
    reference = str;
    NSLog(@"%@", reference); // Console: autoreleasePool
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"%@", reference); // Console: autoreleasePool
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"%@", reference); // Console: (null)
}

这个实验同时也证明了viewDidLoad和viewWillAppear是在同一个runloop调用的,而viewDidAppear是在之后的某个runloop调用的。由于这个vc在loadView之后便add到了window层级上,所以viewDidLoad和viewWillAppear是在同一个runloop调用的,因此在viewWillAppear中,这个autorelease的变量依然有值。

当然,我们也可以不用等到当前runLoop结束,选择手动干预Autorelease对象的释放时机:

- (void)viewDidLoad {
    [super viewDidLoad];
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"autoreleasePool"];
    }
    NSLog(@"%@", str); // Console: (null)
}

通过上面的例子,可以看出,没有调用release也做到了内存管理。可是大家注意到了,str对象没有调用autorelease方法啊,怎么被放到autoreleasepool进行管理的呢?其实静态方法已经在内部自动调用了autorelease方法,所有这里不需要再调用。

autoreleasepool作用

autoreleasepool实质

现在以ARC环境来分析其原理。runLoop创建的autoreleasepool实例我们就以@autoreleasepool形式呈现。新建项目之后,其中的main函数如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在这个 @autoreleasepool{}中只包含了一行代码,这行代码将所有的事件、消息全部交给了 UIApplication 来处理,但是这不是本文关注的重点。

需要注意的是:整个iOS的应用都是包含在一个自动释放池block中的。

继续我们的主题。我们知道autoreleasepool是一个自动释放池,那么它到底是一个什么样的数据结构呢?我们在命令行中使用 clang -rewrite-objc main.m 让编译器重新改写这个文件,编译完后,会在该文件目录下多一个.cpp文件。打开这个文件。滚到最底部。可以看到如下代码:(删除掉多余的代码)

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    }
    return 0;
}

在这个文件中,有一个非常奇怪的 __AtAutoreleasePool 的结构体,前面的注释写到/* @autoreleasepopl */ 。也就是说@autoreleasepool {} 被转换为:

{
    __AtAutoreleasePool __autoreleasepool;
}

那么__AtAutoreleasePool又是什么?在文件中可以找到__AtAutoreleasePool数据结构如下:

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

它是一个结构体,该结构体会在初始化时调用 objc_autoreleasePoolPush() 方法,会在析构时调用objc_autoreleasePoolPop 方法。所以我们可以进一步将main函数中的代码改写为如下:

int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();

        // do whatever you want

        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

@autoreleasepool 只是帮助我们少写了这两行代码而已,让代码看起来更美观,然后要根据上述两个方法来分析自动释放池的实现。

objc_autoreleasePoolPushobjc_autoreleasePoolPop 的实现:

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

__AtAutoreleasePool的Push和Pop方法看上去方法看上去是对 AutoreleasePoolPage 对应静态方法push
pop 的封装。

AutoreleasePoolPage

那么AutoreleasePoolPage又是一个什么东东呢?,它的定义可以在NSObject.mm文件中看到,定义如下:

class AutoreleasePoolPage {
    magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
};
  • magic 用于对当前 AutoreleasePoolPage 完整性 的校验
  • thread 保存了当前页所在的线程

每一个自动释放池都是由一系列的 AutoreleasePoolPage 组成的,并且每一个 AutoreleasePoolPage 的大小都是4096 字节(16 进制 0x1000)

#define I386_PGBYTES 4096
#define PAGE_SIZE I386_PGBYTES
  • parentchild 就是用来构造双向链表的指针。

自动释放池中的 AutoreleasePoolPage 是以 双向链表 的形式连接起来的:

假设我们的一个 AutoreleasePoolPage 被初始化在内存的 0x100816000 ~ 0x100817000 中,它在内存中的结构如下:

其中有 56 bit 用于存储 AutoreleasePoolPage 的成员变量,剩下的 0x100816038 ~ 0x100817000 都是用来存储 加入到自动释放池中的对象 。

begin() 和 end() 这两个类的实例方法帮助我们快速获取 0x100816038 ~ 0x100817000 这一范围的边界地址。

  • next 指向了下一个为空的内存地址,如果 next 指向的地址加入一个 object ,它就会如下图所示 移动到下一个为空的内存地址中 :

  • 关于 hiwatdepth 在文章中并不会进行介绍,因为它们并不影响整个自动释放池的实现,也不在关键方法的调用栈中。

POOL_SENTINEL(哨兵对象)

到了这里,你可能想要知道 POOL_SENTINEL 到底是什么,还有它为什么在栈中。首先回答第一个问题:
POOL_SENTINEL
只是 nil 的别名。 定义如下:

#define POOL_SENTINEL nil

在每个自动释放池初始化调用 objc_autoreleasePoolPush 的时候,都会把一个POOL_SENTINEL push 到自动释放池的栈顶,并且返回这个POOL_SENTINEL 哨兵对象。

int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();

        // do whatever you want

        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

上面的 atautoreleasepoolobj 就是一个 POOL_SENTINEL 。而当方法
objc_autoreleasePoolPop
调用时,就会向自动释放池中的对象发送 release 消息,直到POOL_SENTINEL

objc_autoreleasePoolPush 方法

了解了 POOL_SENTINEL ,我们来重新回顾一下 objc_autoreleasePoolPush 方法:

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

它调用 AutoreleasePoolPage 的类方法 push ,也非常简单:

static inline void *push() {
   return autoreleaseFast(POOL_SENTINEL);
}

在这里会进入一个比较关键的方法 autoreleaseFast ,并传入哨兵对象 POOL_SENTINEL

static inline id *autoreleaseFast(id obj)
{
   AutoreleasePoolPage *page = hotPage();
   if (page && !page->full()) {
       return page->add(obj);
   } else if (page) {
       return autoreleaseFullPage(obj, page);
   } else {
       return autoreleaseNoPage(obj);
   }
}

上述方法分三种情况选择不同的代码执行:

  • hotPage 并且当前 page 不满

    • 调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
  • hotPage 并且当前 page 已满
    • 调用 autoreleaseFullPage 初始化一个新的页
    • 调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
  • hotPage
    • 调用 autoreleaseNoPage 创建一个 hotPage
    • 调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中

最后的都会调用 page->add(obj) 将对象添加到自动释放池中。hotPage 可以理解为当前正在使用的AutoreleasePoolPage 。 page->add添加对象。id *add(id obj)将对象添加到自动释放池页中:

id *add(id obj) {
    id *ret = next;
    *next = obj;
    next++;
    return ret;
}

笔者对这个方法进行了处理,更方便理解。这个方法其实就是一个压栈的操作,将对象加入 AutoreleasePoolPage 然后移动栈顶的指针。autoreleaseFullPage 会在当前的hotPage 已满的时候调用:

static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
    do {
        if (page->child) page = page->child;
        else page = new AutoreleasePoolPage(page);
    } while (page->full());

    setHotPage(page);
    return page->add(obj);
}

它会从传入的 page 开始遍历整个双向链表,直到:

  1. 查找到一个未满的 AutoreleasePoolPage
  2. 使用构造器传入 parent 创建一个新的 AutoreleasePoolPage

在查找到一个可以使用的 AutoreleasePoolPage 之后,会将该页面标记成 hotPage ,然后调动上面分析过的page->add 方法添加对象。 如果当前内存中不存在hotPage ,就会调用
autoreleaseNoPage 方法初始化一个AutoreleasePoolPage

static id *autoreleaseNoPage(id obj) {
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    if (obj != POOL_SENTINEL) {
        page->add(POOL_SENTINEL);
    }

    return page->add(obj);
}

既然当前内存中不存在 AutoreleasePoolPage ,就要 从头开始构建这个自动释放池的双向链表 ,也就是说,新的AutoreleasePoolPage 是没有parent 指针的。初始化之后,将当前页标记为
hotPage ,然后会先向这个 page 中添加一个POOL_SENTINEL 对象,来确保在pop 调用的时候,不会出现异常。最后,将
obj 添加到自动释放池中。

objc_autoreleasePoolPop 方法

同样,回顾一下上面提到的 objc_autoreleasePoolPop 方法:

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

看起来传入任何一个指针都是可以的,但是在整个工程并没有发现传入其他对象的例子。不过在这个方法中 传入其它的指针也是可行的 ,会将自动释放池释放到相应的位置。我们一般都会在这个方法中传入一个哨兵对象POOL_SENTINEL ,如下图一样释放对象:

在继续分析这个方法之前做一个小测试,在 objc_autoreleasePoolPop 传入非哨兵对象,测试一下这个方法的行为。其具体测试方法查看这篇文章

让我们重新回到对 objc_autoreleasePoolPop 方法的分析,也就是 AutoreleasePoolPage::pop 方法的调用:

static inline void pop(void *token) {
    AutoreleasePoolPage *page = pageForPointer(token);
    id *stop = (id *)token;

    page->releaseUntil(stop);

    if (page->child) {
        if (page->lessThanHalfFull()) {
            page->child->kill();
        } else if (page->child->child) {
            page->child->child->kill();
        }
    }
}

在这个方法中删除了大量无关的代码,以及对格式进行了调整。

该静态方法总共做了三件事情:

  1. 使用 pageForPointer 获取当前 token 所在的 AutoreleasePoolPage
  2. 调用 releaseUntil 方法释放 栈中的 对象,直到 stop
  3. 调用 childkill 方法

我到现在也不是很清楚为什么要根据当前页的不同状态 kill 掉不同 child 的页面。

if (page->lessThanHalfFull()) {
    page->child->kill();
} else if (page->child->child) {
    page->child->child->kill();
}

pageForPointer 方法主要是通过内存地址的操作,获取当前指针所在页的首地址:

static AutoreleasePoolPage *pageForPointer(const void *p) {
    return pageForPointer((uintptr_t)p);
}

static AutoreleasePoolPage *pageForPointer(uintptr_t p) {
    AutoreleasePoolPage *result;
    uintptr_t offset = p % SIZE;

    assert(offset >= sizeof(AutoreleasePoolPage));

    result = (AutoreleasePoolPage *)(p - offset);
    result->fastcheck();

    return result;
}

将指针与页面的大小,也就是 4096 取模,得到当前指针的偏移量,因为所有的 AutoreleasePoolPage 在内存中都是对齐的:

p = 0x100816048
p % SIZE = 0x48
result = 0x100816000  

而最后调用的方法 fastCheck() 用来检查当前的 result 是不是一个 AutoreleasePoolPage 。通过检查magic_t 结构体中的某个成员是否为0xA1A1A1A1

releaseUntil 方法的实现如下:

void releaseUntil(id *stop) {
    while (this->next != stop) {
        AutoreleasePoolPage *page = hotPage();

        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }

        page->unprotect();
        id obj = *--page->next;
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();

        if (obj != POOL_SENTINEL) {
            objc_release(obj);
        }
    }

    setHotPage(this);
}

它的实现还是很容易的,用一个 while 循环持续释放 AutoreleasePoolPage 中的内容,直到next 指向了stop 。使用
memset 将内存的内容设置成 SCRIBBLE ,然后使用objc_release 释放对象。 到这里,没有分析的方法就只剩下kill 了,而它会将当前页面以及子页面全部删除:

void kill() {
    AutoreleasePoolPage *page = this;
    while (page->child) page = page->child;

    AutoreleasePoolPage *deathptr;
    do {
        deathptr = page;
        page = page->parent;
        if (page) {
            page->unprotect();
            page->child = nil;
            page->protect();
        }
        delete deathptr;
    } while (deathptr != this);
}

总结内容:

  • 自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的
  • 当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中
  • 调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息

autoreleasepool释放

现在我们知道了AutoreleasePool是在runLoop即将进入runLoop和准备进入休眠这两种状态的时候被创建和销毁的。所以AutoreleasePool的释放有如下两种情况。一种是Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。还有一种就是手动调用AutoreleasePool的释放方法(drain方法)来销毁AutoreleasePool或者@autoreleasepool{}执行完释放。比如前面的reference弱引用的例子。

__weak id reference = nil;
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *str = [NSString stringWithFormat:@"autoreleasePool"];
    // str是一个autorelease对象,设置一个weak的引用来观察它
    reference = str;
    NSLog(@"%@", reference); // Console: autoreleasePool
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"%@", reference); // Console: autoreleasePool
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"%@", reference); // Console: (null)
}

和:

- (void)viewDidLoad {
    [super viewDidLoad];
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"autoreleasePool"];
    }
    NSLog(@"%@", str); // Console: (null)
}

autoreleasepool关联知识

总结:

既然autoreleasepool跟runLoop机制有关,而runLoop又与线程有关,所有autoreleasepool也与线程有关。

既然autoreleasepool是一种延迟释放机制,当在autoreleasepool中有大量对象被创建而得不到及时释放会出现内存高峰现象。

autoreleasepool与autorelease的关系

参考文章:

http://blog.sunnyxx.com/2014/10/15/behind-autorelease/

http://www.tuicool.com/articles/ABvIjm2

时间: 2024-08-25 15:09:58

内存管理总结-autoreleasePool的相关文章

菜鸟学习Cocos2d-x 3.x——内存管理

菜鸟学习Cocos2d-x 3.x——内存管理 2014-12-10 分类:Cocos2d-x / 游戏开发 阅读(394) 评论(6) 亘古不变的东西 到现在,内存已经非常便宜,但是也不是可以无限大的让你去使用,特别是在移动端,那么点内存,那么多 APP要抢着用,搞不好,你占的内存太多了,系统直接干掉你的APP,所以说了,我们又要老生常谈了——内存管理.总结COM开发的时候,分析过COM的 内存管理模式:总结Lua的时候,也分析了Lua的内存回收机制:前几天,还专门写了C++中的智能指针在内存

Cocos2d之“引用计数”内存管理机制实现解析

一.引言 本文主要分析cocos2d游戏开发引擎的引用计数内存管理技术的实现原理.建议读者在阅读本文之前阅读笔者之前一篇介绍如何使用cocos2d内存管理技术的文章--<Cocos2d之Ref类与内存管理使用详解>. 二.相关概念 引用计数 引用计数是计算机编程语言的一种内存管理技术,是指将资源(对象.内存或者磁盘空间等)的被引用计数保存起来,当引用计数变为零时就将资源释放的过程.使用引用计数技术可以实现自动内存管理的目的. 当实例化一个类时,对象的引用计数为1,在其他对象需要持有这个对象时,

Cocos2d-x 3.x:如何进行合理的内存分配(使用AutoreleasePool 来合理的管理内存)

Cocos2d-x 3.x:如何进行合理的内存分配(使用AutoreleasePool 来合理的管理内存) 本文转载至深入理解Cocos2d-x 3.x:如何进行合理的内存分配 设想如下场景,这是一个典型的内存合理分配的场景:在一帧内,有若干个函数,每个函数都会创建一系列的精灵,每个精灵都不同,都会占用一定的内存,精灵的总数可能会有1000个,而一个函数只会创建10个精灵这样,创建的精灵只会在这个函数中使用,大致代码如下: 1 2 3 4 5 for(int i = 0; i < 10; i++

Block内存管理实例分析

在ios开发中,相信说道block大家都不陌生,内存管理问题也是开发者最头疼的问题,网上很多讲block的博客,但大都是理论性多点,今天结合一些实例来讲解下. 存储域 首先和大家聊聊block的存储域,根据block在内存中的位置,block被分为三种类型: NSGlobalBlock NSStackBlock NSMallocBlock 从字面意思上大家也可以看出来 NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中. NSStackBlock是位于

OC内存管理

OC内存管理 一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等. 管理范围:任何继承NSObject的对象,对其他的基本数据类型无效. 本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也

iOS基础 ----- 内存管理

Objective-C 的内存管理方式有引用计数机制,垃圾回收机制,自动释放池.有alloc,就有释放.iOS应?程序出现Crash(闪退),90%的原因是因为内存问 题.在?个拥有数?个甚?是上百个类的?程?,查找内存问 题极其困难,学会内存管理,能帮我们减少出错的?率.内存问题体现在两个??:内存溢出.野指针异常. 引用计数器 在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Counting)机制,程序编译时Xcode可以自动给你的代码添加内存释放代

魏兆辉的IOS基础学习笔记之十二 OC语言基础-07 Foundation内存管理

本篇博文,将给大家介绍下再Objective-C中如何使用内存管理.一个程序运行的时候,如果不及时的释放没有用的空间内存.那么,程序会越来 越臃肿,内存占用量会不断升高.我们在使用的时候,就会感觉很卡,最终使得程序运行奔溃.因此,将无效的内存及时清理释放,是非常有必要的. 一个对象在最初创建使用,到最后的回收释放,经历的是怎样一个过程呢?包括:诞生(通过alloc或new方法实现).生存(接收消息并执行操作).交友(通过复合以及向方法传递参数).最终死去(被释放掉). 一.引用计数 在对象创建的

OC -内存管理

管理内存有三种方式 1:垃圾回收,在java中常见的管理内存方法,系统自动检测对象是否被使用,是否被释放 2.MRC: 手动管理引用计数,iOS管理内存的方式,程序员通过手动方式来管理对象是否被释放 3.ARC: 自动管理引用计数:基于MRC,系统自动的管理内存 引用计数:retainCount 当我们使用一个指针指向一块内存的时候,应该对这块内存做retain操作,引用计数+1.当我们不再使用这个指针指向这块内存,应该对这块内存做release操作,引用计数-1.这样可以使引用计数值一直保持等

IOS内存管理

原文链接:http://blog.csdn.net/weiqubo/article/details/7376189 1.  内总管理原则(引用计数)    IOS的对象都继承于NSObject,   该对象有一个方法:retainCount ,内存引用计数. 引用计数在很多技术都用到: window下的COM组件,多线程的信号量,读写锁,思想都一样.       (一般情况下: 后面会讨论例外情况)    alloc      对象分配后引用计数为1    retain    对象的引用计数+1