美文网首页
AutoreleasePool详解以及避免内存过大

AutoreleasePool详解以及避免内存过大

作者: David_Do | 来源:发表于2018-12-27 16:51 被阅读14次

    苹果官方:
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
    参考:
    https://www.jianshu.com/p/1b66c4d47cd7
    http://blog.sunnyxx.com/2014/10/15/behind-autorelease/

    什么时候使用AutoreleasePool

    • 如果您正在编写不基于UI框架的程序,例如命令行工具。

    • 如果你编写一个创建许多临时对象的循环。

      您可以在循环内使用自动释放池块在下一次迭代之前处理这些对象。在循环中使用自动释放池块有助于减少应用程序的最大内存占用量。

    • 如果你产生一个辅助线程。

      一旦线程开始执行,您必须创建自己的自动释放池块; 否则,您的应用程序将泄漏对象。(有关详细信息,请参阅自动释放池块和线程。)

    使用本地自动释放池块来减少峰值内存占用量

    许多程序创建自动释放的临时对象。这些对象会添加到程序的内存占用空间,直到块结束。在许多情况下,允许临时对象累积直到当前事件循环迭代结束时不会导致过多的开销; 但是,在某些情况下,您可能会创建大量临时对象,这些对象会大大增加内存占用并且您希望更快地处置。在后面这些情况下,您可以创建自己的自动释放池块。在块结束时,临时对象被释放,这通常导致它们的释放,从而减少程序的内存占用。

    以下示例显示了如何在for循环中使用本地自动释放池块。

    NSArray *urls = <# An array of file URLs #>;
        for (NSURL *url in urls) { 
            @autoreleasepool {
                NSError *error;
                NSString *fileContents = [NSString stringWithContentsOfURL:url
                                                 encoding:NSUTF8StringEncoding error:&error];
                /* Process the string, creating and autoreleasing more objects. */
            }
        }
    

    该for循环一次处理一个文件。在块结束时释放在自动释放池块内fileContents发送autorelease消息的任何对象(例如)。

    在自动释放池块之后,您应该将在块中自动释放的任何对象视为“已处置”。不要向该对象发送消息或将其返回给您的方法的调用者。如果必须在自动释放池块之外使用临时对象,则可以通过向retain块内的对象发送消息然后在块之后发送它autorelease来执行此操作,如以下示例所示:

    – (id)findMatchingObject:(id)anObject { 
            id match;
            while (match == nil) {
                @autoreleasepool {
         
                    /* Do a search that creates a lot of temporary objects. */
                    match = [self expensiveSearchForObject:anObject];
         
                    if (match != nil) {
                        [match retain]; /* Keep match around. */
                    }
                }
            } 
            return [match autorelease];   /* Let match go and return it. */
        }
    

    发送retainmatch自动释放池块并autorelease在自动释放池块之后发送到它并延长其生命周期match并允许它在循环外接收消息并返回给调用者findMatchingObject:

    AutoreleasePool是什么时候销毁的?

    https://blog.csdn.net/nathan1987_/article/details/78402332
    这个问题能回答上来的话,肯定要对AutoreleasePool和RunLoop有所了解才行。

    AutoreleasePool被称为自动释放池,在释放池中的调用了autorelease方法的对象都会被压在该池的顶部(以栈的形式管理对象)。当自动释放池被销毁的时候,在该池中的对象会自动调用release方法来释放资源,销毁对象。以此来达到自动管理内存的目的。

    AutoreleasePool的释放有如下两种情况。
    一是Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。
    二是手动调用AutoreleasePool的释放方法(drain方法)来销毁AutoreleasePool

    避免内存使用峰值过高,及时释放内存的场景。

    举个例子

    NSArray *urls = <# An array of file URLs #>;
    for (NSURL *url in urls) {
        @autoreleasepool {
            NSError *error;
            NSString *fileContents = [NSString stringWithContentsOfURL:url
                                             encoding:NSUTF8StringEncoding
                                             error:&error];
        }
    }
    

    这个for循环里如果不使用@autoreleasepool,那临时变量内存可能是爆发式``的,但是使用了@autoreleasepool,在每个@autoreleasepool结束时,里面的``临时变量都会回收,内存使用更加合理。

    使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:

    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        // 这里被一个局部@autoreleasepool包围着
    }];
    

    当然,在普通for循环和for in循环中没有,所以,还是新版的block版本枚举器更加方便。for循环中遍历产生大量autorelease变量时,就需要手加局部AutoreleasePool咯。

    嵌套的autoreleasePool只有最里层的pool会引用其中的对象

    在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,

    而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。

    在嵌套的autoreleasePool中,只有最里层的pool使对象的引用计数加1。
    反过来说就是最里层的pool会阻止外层的pool对对象的引用。

    这样就可以解释为什么如果方法里面,如果有大循环的话,应该对循环加autoReleasePool,
    因为这个pool阻止了RunLoop的一次迭代中加入的pool对对象的引用。
    这样在一次循环结束后,在循环中创建的变量就会被释放。

    转自:https://blog.csdn.net/gaoyp/article/details/78482555

    相关文章

      网友评论

          本文标题:AutoreleasePool详解以及避免内存过大

          本文链接:https://www.haomeiwen.com/subject/nmcdlqtx.html