面试题(含 runloop)

 

掘金上的面试经验收集https://juejin.im/post/5b4cd5aae51d455b5d3efa2c

 

http://www.cocoachina.com/ios/20160414/15918.html

http://www.cocoachina.com/ios/20160415/15930.html

http://www.cocoachina.com/ios/20160418/15931.html

 

http://www.jianshu.com/p/d72c4b595c7b 有些新颖的面试题

http://www.tuicool.com/articles/IzaaE3  百度可穿戴部门的面试题

http://www.jianshu.com/p/262c1f8b7461

http://draveness.me/guan-yu-xie-ios-wen-ti-de-jie-da/

 

runloop的另一个讲解http://ios.jobbole.com/85759/    http://ios.jobbole.com/92193/?utm_source=blog.jobbole.com&utm_medium=relatedPosts


 

autorelease http://www.cnblogs.com/wengzilin/p/4351187.html

                                    http://www.jianshu.com/p/5559bc15490d

 

                                    https://my.oschina.net/aofe/blog/267337

                                    http://www.tuicool.com/articles/QvEJ73q

 

runloop的一个面试题http://www.bkjia.com/IOSjc/987588.html 就是下面这部分内容

RunLoop和autorelease的一道面试题,runloop autorelease

 

 

 

 

有这么一道ios面试题 以下代码有没有什么问题?如果有?如何解决? 

for (int i = 0 ; i < largeNumber; i++) {	
	NSString *str = [NSString stringWithFormat:@"hello -%04d",i];
	str = [str stringByAppendingString:@" - world"]; 
}

局部释放池和RunLoop释放池的概念:

主线程的RunLoop是默认开启的(视图用[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]来停止它,也是做不到的), 每一次消息循环开始的时候会先创建自动释放池,这次循环结束前,会释放自动释放池,然后RunLoop等待下次事件源。 在这个过程中,由RunLoop创建的释放池类似于一个全局的释放池。但是开发者可以任何执行的地方创建释放池,也就是局部的释放池,这时的释放池类似于代码块 当释放池结束的时候会自动释放。因此一般情况下,局部的自动释放池很快就被释放了,而RunLoop释放池会等一次消息循环结束的时候释放。 

什么样的对象会交给释放池管理:

返回当前类的实例的类方法创建出来的对象,都是autorelease的,会交给所在的释放池进行管理。 例如创建一个Person类,使用[[self alloc]init]方法创建的对象的管理不会交给它所在的释放池,而是根据引用计数来控制释放的时机, 如果使用[[[self alloc]init] autorelease]创建的对象,会交给所在的释放池管理,控制其释放的时机。 

- (void)test{
	@autoreleasepool {
        Person *p = [[Person alloc]init];
        p = nil;
        NSLog(@"---");
    }
    NSLog(@"autorelease结束");
}

执行结果:

Person---dealloc
---
autorelease结束

 

- (void)test1{    
    @autoreleasepool {
        Person *p = [Person person]; // 内部是[[[self alloc]init] autorelease]
        p = nil;
        NSLog(@"---");
    }
    NSLog(@"autorelease结束");
}

执行的结果为:

---
Person---dealloc
autorelease结束

因此自动释放池被销毁或耗尽时会向池中所有使用autorelease创建的对象发送release 消息,释放所有autorelease的对象,而不是所有的对象。 

回到面试的问题:

当我们使用for循环创建很多个使用autorelease方式创建的NSString对象的时候,将所有的对象的释放权都交给了RunLoop 的释放池,而RunLoop的释放池会等待这个事件处理之后才会释放,因此就会使对象无法及时释放,堆积在内存造成内存泄露,可以在Debug Navigation 中观察到内存激增。为了验证确实是因为autorelease这种创建方式引起的内存泄露,我做了如下的测试: 

int largeNumber = 100000000;
- (void)test3{
    for (int i = 0 ; i < largeNumber; i++) {
        NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];
        str = [str stringByAppendingString:@" - world"];
    }
}
// 这样做的结果是内存几乎没有变化,验证了确实是这个原因。

但是在编写代码的时候我们仍然习惯用类的快速创建方法,而不是alloc+init。因此解决的方案就是添加局部的释放池,以及时释放内存 如果将局部释放池添加到循环外:

- (void)test4{
    @autoreleasepool {
        for (int i = 0 ; i < largeNumber; i++) {
            NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];
            str = [str stringByAppendingString:@" - world"];
        }
    }
}

这样显然是没有效果的,释放池需要等循环执行之后再释放内存,这和使用RunKLoop创建的释放池没有什么区别。 较好的方案就是每次循环的时候添加一个释放池:

- (void)test5{
    for (int i = 0 ; i < largeNumber; i++) {
        @autoreleasepool {
            NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];
            str = [str stringByAppendingString:@" - world"];
        }
    }
}

这样每一次循环的结束时都会释放一次内存,因而这个循环全部执行完成时也几乎补不消耗内存。 

总结是:

做多线程开发时,需要在线程调度方法中手动添加自动释放池,尤其是当执行循环的时候,如果循环内部有使用类的快速创建方法创建的对象, 一定要将循环体放到自动释放池中。

 

 

 

你可能感兴趣的:(知识点,面试)