主线程的RunLoop是默认开启的(视图用[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]来停止它,也是做不到的), 每一次消息循环开始的时候会先创建自动释放池,这次循环结束前,会释放自动释放池,然后RunLoop等待下次事件源。 在这个过程中,由RunLoop创建的释放池类似于一个全局的释放池。但是开发者可以任何执行的地方创建释放池,也就是局部的释放池,这时的释放池类似于代码块 当释放池结束的时候会自动释放。因此一般情况下,局部的自动释放池很快就被释放了,而RunLoop释放池会等一次消息循环结束的时候释放。
当我们使用for循环创建很多个使用autorelease方式创建的NSString对象的时候,将所有的对象的释放权都交给了RunLoop 的释放池,而RunLoop的释放池会等待这个事件处理之后才会释放,因此就会使对象无法及时释放,堆积在内存造成内存泄露,可以在Debug Navigation 中观察到内存激增。为了验证确实是因为autorelease这种创建方式引起的内存泄露,我做了如下的测试:
int largeNumber = 1000000;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0 ; i < largeNumber; i++) {
// @autoreleasepool {
NSNumber *num = [NSNumber numberWithInt:i];
NSString *str = [NSString stringWithFormat:@"%d ", i];
//Use num and str...whatever...
[NSString stringWithFormat:@"%@%@", num, str];
// }
}
}
结果:
int largeNumber = 1000000;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0 ; i < largeNumber; i++) {
@autoreleasepool {
NSNumber *num = [NSNumber numberWithInt:i];
NSString *str = [NSString stringWithFormat:@"%d ", i];
//Use num and str...whatever...
[NSString stringWithFormat:@"%@%@", num, str];
}
}
}
结果:
如果将局部释放池添加到循环外:
int largeNumber = 1000000;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
@autoreleasepool {
for (int i = 0 ; i < largeNumber; i++) {
NSNumber *num = [NSNumber numberWithInt:i];
NSString *str = [NSString stringWithFormat:@"%d ", i];
//Use num and str...whatever...
[NSString stringWithFormat:@"%@%@", num, str];
}
}
}
结果:没效果
新事件 点击了两次
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"q");
for (int i = 0 ; i < largeNumber; i++) {
NSNumber *num = [NSNumber numberWithInt:i];
NSString *str = [NSString stringWithFormat:@"%d ", i];
//Use num and str...whatever...
[NSString stringWithFormat:@"%@%@", num, str];
}
}
结果: