也许这不是一个重复的问题,因为我已经搜索并尝试了许多关于如何在ARC下发布对象的解决方案 代码很简单:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self recreateView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self.view addGestureRecognizer:tap];
}
- (void)tapped:(UITapGestureRecognizer *)g
{
[self recreateView];
}
- (void)recreateView
{
@autoreleasepool {
for (UIView *v in self.view.subviews) {
[v removeFromSuperview];
}
MyView *vv = [[MyView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:vv];
}
[self _performHeavyWork];
}
- (void)_performHeavyWork
{
int j = 0;
for (int i = 0 ; i < 100000000; ++i) {
j += random() % 7;
j = j % 18747;
}
}
@end
ViewController
只需添加一个点击手势识别器,其操作是在添加新子视图之前删除旧子视图。 MyView
是UIView
的子类,只需在解除分配时记录消息。
@implementation MyView
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
唯一的魔力是每次创建新视图时都会调用-_performHeavyWork
。当您继续快速点击屏幕时,ViewController
将忙于创建和放弃视图。然而,奇怪的是,所有丢弃的视图都不会立即被释放,但是当时你已经停止了一段时间。
这是流程的概况:
正如您所看到的,如果继续点击,内存会不断增长,并且同时存在许多MyView
个实例。如果你注释掉[self _performHeavyWork];
,一切都将恢复正常。所以我的问题是:
答案 0 :(得分:3)
我认为主要的问题是你在主线程上执行繁重的工作。如果你把硬件放在不同的线程(或GCD)上,你可能会看到你期待的东西。
以下是对正在发生的事情的一些猜测。
iOS仅在主线程上响应UI中的更改。因此,如果您正在使用主线程进行其他操作,则会将这些抽头排队等待以后进行处理。
点击屏幕,主线程开始处理繁重的工作。
您再点按一下屏幕。 iOS无法处理您的请求,因此它会对事件进行排队。
最终,您的繁重工作完成并将控制权返回给iOS。
iOS获取事件队列并在单个运行循环中处理它们,这意味着主循环自动释放池永远不会耗尽。
但手动自动释放池怎么样?好吧,所有UI相关的东西都发生在主循环和主线程上,所以removeFrmSuperview:在控制权返回操作系统之前不会发生。在此之前,视图层次结构仍然包含对视图的引用,因此内存增长。
答案 1 :(得分:0)
当你认为最后一个引用消失时,你永远不应该依赖dealloc。你很可能还没有引用它们,但最重要的是,ARC可以在后台线程上调用dealloc。
答案 2 :(得分:0)
首先,当您将视图添加到可见窗口的视图层次结构时,iOS会保留对视图的引用。因此,当您在块中创建UIView子类的新实例时,它将保留在autorelease块之外的内存中。其次,对removeFromSuperview的调用:实际上不会导致ARC在主线程完成之前释放视图,这意味着在自动释放块结束后仍然存在对视图的引用。您正在执行的工作会延迟主线程。这会延迟删除对视图的最终引用。
此外,自动释放块在删除视图时无效,因为相关视图未在自动释放块的同一实例中分配。 IOW,正在创建并添加到视图层次结构中的视图在稍后在同一块中删除时不在同一范围内。因此,在autorelease块中进行remove调用没有任何好处。