autorelease的理解

2018-09-21  本文已影响0人  Johnny_Wu

在ARC模式下,因为系统帮我们管理内存,所以很少关注到autorelease自动释放池。系统其实是通过autorelease来管理我们申请的内存(创建对象),在一次runLoop后,会进行一次autorelease的处理,释放掉申请的内存。程序运行过程中,会产生很多autorelease,系统是通过一个栈来管理它们。
虽然,很多事情都是系统帮我们做了,那有时候还是需要我们自己善用autorelease来优化代码:
1、利用@autoreleasepool优化循环。如果循环次数比较多,并且里面申请了一些比较大的内存,可以在循环内部手动加上@autoreleasepool,来及时释放内存,避免内存暴涨。
2、线程需要长期运行并且有可能产生大量autoreleased对象, 你应该使用autorelease pool blocks。
3、长期在后台中运行的任务, 方法。

一、下面,是想说说autorelease pool的其他一些东西。

一切从一个例子开始吧:

    __weak id wsPtr;
    {
        NSObject *obj = [[NSObject alloc] init];
        wsPtr = obj;
        NSLog(@"1>>>>>>%@",[wsPtr class]);
    }
    NSLog(@"2>>>>>>%@",[wsPtr class]);

结果为:

2018-09-21 14:05:00.154692+0800 SummaryTest[1333:1156186] 1>>>>>>NSObject
2018-09-21 14:05:00.154904+0800 SummaryTest[1333:1156186] 2>>>>>>(null)

这结果,大家都不会觉得奇怪,出了作用范围嘛,obj对象已经释放了,所以后面访问到为nil。
改为如下情况呢:

    __weak id wsPtr;
    {
        __autoreleasing NSObject *obj = [[NSObject alloc] init];
        wsPtr = obj;
        NSLog(@"1>>>>>>%@",[wsPtr class]);
    }
    NSLog(@"2>>>>>>%@",[wsPtr class]);

结果为:

2018-09-21 14:14:05.920146+0800 SummaryTest[1336:1157313] 1>>>>>>NSObject
2018-09-21 14:14:05.920369+0800 SummaryTest[1336:1157313] 2>>>>>>NSObject

结果是,在括号外部,生成的obj还是没有释放。这就是autoreleasePool对它的影响。虽然autorelease是系统控制,我们不好把控它的结束时间。但可以肯定,在一个函数内部,它不会释放的,所以,放到释放池的对象,在整个函数内部是有效的。那么问题来了,什么时候的对象才归autoreleasePool管理,为什么第一次申请的obj,没放到autoreleasePool中呢。
在以下情况,会注册到autoreleasePool中:
1、__autoreleasing修饰的对象,如上所述。
2、非自己生成并持有的对象。明显点的,就是通过一个函数返回的对象。使用alloc/new/copy/mutableCopy以外的方法来取得的对象。这就解释了,上面第一个例子的obj为什么没被autoreleasePool管理。

- (void)testAutorelease
{
    __weak id wsPtr;
    {
        NSObject *obj = [self gainObj];
        wsPtr = obj;
        NSLog(@"1>>>>>>%@",[wsPtr class]);
    }
    NSLog(@"2>>>>>>%@",[wsPtr class]);
}
- (NSObject *)gainObj
{
    return [[NSObject alloc] init];
}

结果是,obj注册到autoreleasepool中,括号外也可以访问对象。
3、id的指针或对象的指针会默认加上__autoreleasing修饰符。如一些函数:

- (BOOL)performOperationWithError:(NSError **)error;

二、一些特殊情况

一切都按我的推理进行着,切出现了一个奇葩的情况:

__weak id reference;
- (void)testAutorelease
{
    NSString *myStr = [NSString stringWithFormat:@"asdd"];
    reference = myStr;
    NSLog(@"1>>>>>>%@",reference);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"2>>>>%@",reference);
}

我想测试,出了函数域,什么时候就会释放,所以加了个点击事件。结果是,不管什么时候点击,都可以访问到对象。然后我就一脸蒙蔽了。我又测试了NSObject、NSArray,都不会出现上面的情况。难道是NSString特有的性质,在view期间都不释放,最后随view一起释放。

NSString *myStr = [NSString stringWithFormat:@"assdfdsfdsfddd"];

把字符串加长一点,竟然会释放了。真是奇葩。看看它们的地址吧:

- (void)testAutorelease
{
    NSString *myStr1 = [NSString stringWithFormat:@"assdfdsfdsfddd"];
    NSString *myStr2 = [NSString stringWithFormat:@"ass"];
    reference = myStr1;
    NSLog(@"str1=%p  str2=%p",myStr1,myStr2);
}

2018-09-21 15:23:07.671661+0800 SummaryTest[1367:1168589] str1=0x1700373a0  str2=0xa000000007373613

比较长的NSString存于堆区,较短的NSString感觉像是存于数据区,所以性质不一样吧,不懂。记住NSString特殊一点就可以了。

上一篇下一篇

猜你喜欢

热点阅读