【发布时间】:2014-01-16 20:20:21
【问题描述】:
我只是想知道,为什么 ARC 编译器下没有自动释放池优化,它会在最内部的范围内保留一个对象,将其从自动释放池中删除并在对象不再使用时释放?
从另一个问题中引用一个非常不切实际的例子,
for(NSUInteger i = 0; i < 10000; i++)
{
for(NSUInteger j = 0; j < 10000; j++)
{
NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
//NSLog(@"%@", n); //Disabled this to increase memory bloat faster.
}
}
如果没有@autoreleasepool { ... } 包装,内存会不断增长。用@autoreleasepool包裹,内存仍然很低:
for(NSUInteger i = 0; i < 10000; i++)
{
for(NSUInteger j = 0; j < 10000; j++)
{
@autoreleasepool {
NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
//NSLog(@"%@", n); //Disabled this to increase memory bloat faster.
}
}
}
但是为什么编译器不能优化这样的情况,在最内层范围之外不需要对象并删除@autoreleasepool 包装的需要?是否有技术原因这不可能或尚未完成?
编辑
澄清一下,为什么编译器不能输出如下代码:
for(NSUInteger i = 0; i < 10000; i++)
{
for(NSUInteger j = 0; j < 10000; j++)
{
NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
objc_retain(n);
objc_removeFromAutoreleasePool(n);
NSLog(@"%@", n);
objc_release(n);
}
}
编辑 2
应格雷格的要求,这里是上面两个例子的反汇编结果。
没有@autoreleasepool { }:
TestOpt`-[LMViewController testAutoreleaseMem] at LMViewController.m:17:
0x2187: pushl %ebp
0x2188: movl %esp, %ebp
0x218a: pushl %ebx
0x218b: pushl %edi
0x218c: pushl %esi
0x218d: subl $0x1c, %esp
0x2190: calll 0x2195 ; -[LMViewController testAutoreleaseMem] + 14 at LMViewController.m:17
0x2195: popl %esi
0x2196: xorl %eax, %eax
0x2198: movl 0x13cb(%esi), %ebx
0x219e: movl %eax, -0x10(%ebp)
0x21a1: xorl %edi, %edi
0x21a3: movl 0x13df(%esi), %eax
0x21a9: movl %edi, 0x8(%esp)
0x21ad: movl %ebx, 0x4(%esp)
0x21b1: movl %eax, (%esp)
0x21b4: calll 0x227e ; symbol stub for: objc_msgSend
0x21b9: movl %eax, (%esp)
0x21bc: calll 0x2296 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x21c1: movl %eax, (%esp)
0x21c4: calll 0x228a ; symbol stub for: objc_release
0x21c9: incl %edi
0x21ca: cmpl $0x2710, %edi
0x21d0: jne 0x21a3 ; -[LMViewController testAutoreleaseMem] + 28 at LMViewController.m:24
0x21d2: movl -0x10(%ebp), %eax
0x21d5: incl %eax
0x21d6: cmpl $0x2710, %eax
0x21db: jne 0x219e ; -[LMViewController testAutoreleaseMem] + 23 at LMViewController.m:24
0x21dd: addl $0x1c, %esp
0x21e0: popl %esi
0x21e1: popl %edi
0x21e2: popl %ebx
0x21e3: popl %ebp
0x21e4: ret
与:
TestOpt`-[LMViewController testAutoreleaseMem] at LMViewController.m:17:
0x216f: pushl %ebp
0x2170: movl %esp, %ebp
0x2172: pushl %ebx
0x2173: pushl %edi
0x2174: pushl %esi
0x2175: subl $0x1c, %esp
0x2178: calll 0x217d ; -[LMViewController testAutoreleaseMem] + 14 at LMViewController.m:17
0x217d: popl %ecx
0x217e: movl %ecx, -0x10(%ebp)
0x2181: xorl %eax, %eax
0x2183: movl 0x13e3(%ecx), %ecx
0x2189: movl %eax, -0x14(%ebp)
0x218c: xorl %edi, %edi
0x218e: movl %ecx, %ebx
0x2190: calll 0x2278 ; symbol stub for: objc_autoreleasePoolPush
0x2195: movl %eax, %esi
0x2197: movl -0x10(%ebp), %eax
0x219a: movl 0x13f7(%eax), %eax
0x21a0: movl %edi, 0x8(%esp)
0x21a4: movl %ebx, 0x4(%esp)
0x21a8: movl %eax, (%esp)
0x21ab: calll 0x227e ; symbol stub for: objc_msgSend
0x21b0: movl %eax, (%esp)
0x21b3: calll 0x2296 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x21b8: movl %eax, (%esp)
0x21bb: calll 0x228a ; symbol stub for: objc_release
0x21c0: movl %esi, (%esp)
0x21c3: calll 0x2272 ; symbol stub for: objc_autoreleasePoolPop
0x21c8: incl %edi
0x21c9: cmpl $0x2710, %edi
0x21cf: jne 0x2190 ; -[LMViewController testAutoreleaseMem] + 33 at LMViewController.m:23
0x21d1: movl %ebx, %ecx
0x21d3: movl -0x14(%ebp), %eax
0x21d6: incl %eax
0x21d7: cmpl $0x2710, %eax
0x21dc: jne 0x2189 ; -[LMViewController testAutoreleaseMem] + 26 at LMViewController.m:24
0x21de: addl $0x1c, %esp
0x21e1: popl %esi
0x21e2: popl %edi
0x21e3: popl %ebx
0x21e4: popl %ebp
0x21e5: ret
【问题讨论】:
-
如果你使用了 alloc/initWith... 它将有机会插入一个释放... +numberWith 将始终返回一个自动释放的对象。所以它将被池保留。
-
@GradyPlayer 我知道它目前是如何工作的。我在问为什么不能从池中删除对象,以便在范围结束后释放?
-
我认为 ARC 能正常工作是一个奇迹。我们不要挑剔。
-
@HotLicks 没什么神奇的,只有精细的工程和良好的规则集。只是想知道是否在某些情况下无法进行这种优化。但 Rob 的建议很有趣。
标签: objective-c automatic-ref-counting llvm