IOSiOS学习笔记ios开发整理

洋气的 autorelease

2018-04-21  本文已影响258人  CoderHG

0、简单的说一句

autorelease 已经在 iOS 界叱咤风云这么多年,现在网上也有很多类似的文章,今天也来造个轮子。关于 autorelease 往往会出现这三个问题:

当然,很多的时候提问者是直接问第3个问题,只要第3问回答正确了,都没有问题了。如果是你,你会怎么回答。关于这个问题,我会先给出常规的错误答案,然后逐一解释。

建议:本文主要是看过程、不要追求最终的答案。想看最后答案,直接看第三部分。

一、错误回答

你是否会这样的回答?!当运行跳出大括号之后,会给当前自动释放池中发送过 autorelease 消息的对象都发送一条 release 消息。貌似很多的小伙伴都会这么回答,遗憾的是 这个回答是错误的,接下来会给出错误的理由。十分的题、也就对了1分,在职场上回答不到8分的回答都是0分。

为什么是错误的回答呢?因为与 大括号 没有 多大 的关系。具体解释,请看下文。

在开始解释之前,先定义一个Class,命名为:HGObject,定义如下:

#import <Foundation/Foundation.h>

@interface HGObject : NSObject

/** 名称 */
@property (nonatomic, copy) NSString* name;

@end
====调皮的分割线====
#import "HGObject.h"

@implementation HGObject

- (void)dealloc {
    NSLog(@" %@ 被释放", _name);
    [super dealloc];
}

@end

总的来说,就是定义了一个属性 name,然后重写了一下 -dealloc 方法。从上面的带来来看,我们接下来的演示中是在 MRC 环境下进行的。

二、与大括号的关系

2.1 不会被释放的情况

不会被释放

你看了之后,你会说:这肯定不会被释放的,因为没有再释放池中。 对、很有道理,确实也是这样,autorelease 必须要在池子中才会有效。那么我就再迁移到池子中,看一下效果。

不会被释放

结果还是一样的:不会被释放。看到这里的你是不是很激动,不信你就试试,如果释放了记得告诉我。关于这种情况,也有这么解释的:因为这里的 autoreleasepool 没有被释放,所以不会被释放,这种解释 100% 是错误的解释。但是如果上面的两个地方将 autorelease 换成 release ,那么都是可以释放的。

两个地方都是可以释放的

到这里,是不是就说明了与大括号无关了?!
来简单的分析一下,在上面的两种使用 autorelease 之后不会被释放的情况。在上面的两个地方,是一个非常不应该的特例,因为我们一般不会在这个地方写代码。但是有一点很情况的是:在上面的两个地方都没有被进入 UIApplicationMain,在 UIApplicationMain 有一个特别重要的机制,叫:运行循环,美其名曰 NSRunLoop。如果说能想到这一点,那么就算是入门了。

看到上面的介绍,你会不会又会很激动的说:对啊,我说的就是在运行循环中的与大括号有关的,在大括号结束之后就释放了。 还想说的是,这也是错误的。是与大括号有关系,但是并不是 一层的关系。

2.2 与大括号的那一层关系

先看一下这个代码:


image.png

关于上面的代码,当我点击屏幕的时候,会打印
两个 log 日志信息, 是先打印 testAutorelease 执行完毕 呢,还是先打印 obj 对象 被释放。思考一下、把你的答案放在心中,稍后公布答案。







0
9
8
7
6
5
4
3
2
1


正确的顺序

看到这个结果,不知道是否有小伙伴开始怀疑人生。关于这个问题,我们先来打两个断点:


两个特别的断点

其实我们通过上面的结论,当断点跳过 32 行的断点之后,obj 是没有被销毁的,那么运行到 26 行的时候是否被销毁了呢?我们试一下:

26 行没有被销毁

对、运行到 26 行依然没有被销毁,那么问题来了:到底是在什么时候销毁的呢?你可能会说:这不废话么?那肯定是 26 行之后就销毁了。现象是没有错的,的确是在 26 行之后就销毁了。但是在揭穿真面目之前,还想再做一个实验,将代码换成这样的:

image.png

通过上面的介绍,这里的打印 log 的顺序是什么呢?这个得要很认真的思考了。







0
9
8
7
6
5
4
3
2
1

你的答案对么?

其实转了一圈,恐怕都晕了吧。至此 autorelease 与大括号的关系,大家都有一个明确的理解了。以后别人问你的话,你还会怎么回答呢?

三、最后一公里

在揭穿之前,希望大家静一静,我们先来介绍一个小指令,关于 lldb 的。很多时候我们总想看一个事件的调用栈,我们可以使用这样的代码:

// 单元调用栈
NSLog(@"%@", [NSThread callStackSymbols]);

也可以直接在 lldb 中这样使用:

po [NSThread callStackSymbols]

以上都是可以的,但是还有一个更简单的, 直接在 lldb 处输入 bt, 然后回车即可。

我为什么要介绍这个呢?是因为很多的时候 Xcode 的这里是显示不全的:


现在的 Xocde 很多有用的都被省略了

现在的 Xocde 很多有用的都被省略了,想看只能通过命令了。赚了一圈,也大了不少的断点,就是没有在 HGObject 中的 -dealloc 中打过断点,要想知道 autorelease 的对象是在什么时候被释放的,直接在这个地方打个断点看看不就可以了么?是啊、我的错,把这里给忘记了。[勇于承认错误,是我一直以来的光荣传统美德]。

那么我们就上面的那个状态,执行以下 bt 指令,信息如下:

重在此图

这张图片的信息,粗略的介绍一下。通过这张图片能看到一个陌生既熟悉的关键字 AutoreleasePoolPage,这又是什么?借用在一个微信群中某大神的一个解释,他的原话:autoreleasepool不是一个大栈,是分一个一个固定大小的page,双向链表连起来的。这里面所指的 page 应该就是这个 AutoreleasePoolPage。具体这个 pop 是在什么时候被调用的,这与 NSRunloop 有关。

四、推荐

推荐一下我的其它文章:

谢谢!

上一篇下一篇

猜你喜欢

热点阅读