美文网首页
iOS面试宝典《二》--经典案例

iOS面试宝典《二》--经典案例

作者: Yochi | 来源:发表于2018-02-28 16:26 被阅读62次

    经典案例

    ===CGD考察===
    下面代码的输出顺序

    - (void)gcdTest {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"4");
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"5");
        });
        [self performSelector:@selector(test2)];
        [self performSelector:@selector(test3) withObject:nil afterDelay:0];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"6");
        });
        [self test1];
    }
    
    - (void)test1 {
        NSLog(@"1");
    }
    - (void)test2 {
        NSLog(@"2");
    }
    - (void)test3 {
        NSLog(@"3");
    }
    
      /*
         苹果文档
         Enqueue a block for execution at the specified time.
         This function waits until the specified time and then asynchronously adds block to the specified queue.
        
         dispatch_after 第二个参数为0,以此可以理解为在当前时刻往主队列添加一个block, 文档中 asynchronously 可以看出是在当前时刻
         添加到主队列的尾部
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"4");
        });
       
        /*
        这里主要考的是 dispatch_async 这个函数的特点,往指定的队列添加一个block立马返回
           
        以下代码在主线程上执行
         dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"A");
         });
         NSLog(@"B");
          结果为 BA
        
         */
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"5");
        });
       
        /*
         performSelector:
         先看苹果文档的解析:
         Sends a specified message to the receiver and returns the result of the message.
        
         那么这句话相当于
         [self test2]
         */
        [self performSelector:@selector(test2)];
       
        /*
         performSelector:withObject:afterDelay:
         Invokes a method of the receiver on the current thread using the default mode after a delay.
         This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
        
         这些解析中最关键的一个单词是 NSDefaultRunLoopMode
         NSDefaultRunLoopMode 是当前线程上正在执行的任务优先,
         那么如果当前线程上有其他任务要执行,test3 先让步,然后在轮到它执行
        
         如果往当前队列中 dispatch_async 一下,通过测试,他的优先级跟 performSelector:withObject:afterDelay: 是同等的,
         换句话说就是谁先谁先执行
         */
        [self performSelector:@selector(test3) withObject:nil afterDelay:0];
       
        /*
        这里主要开启了一个异步线程,再加上 使用了 dispatch_async 函数 因此
        NSLog(@"6"); 在此打印的比较随机
         */
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"6");
        });
       
        [self test1];
       
        /*
         综上所述:
         执行的顺序为
         test2
         test1
         NSLog(@"4");
         NSLog(@"5");
         test3
        
         NSLog(@"6"); 的出现随机
         */
    
    

    下面代码会造成什么问题,解释原因

    - (void)viewDidLoad{
        [super viewDidLoad];
        NSLog(@"=================4");
        dispatch_sync(dispatch_get_main_queue(),
        ^{ NSLog(@"=================5"); });
        NSLog(@"=================6");
    }
    
    分析上面代码:
    
    viewDidLoad 在主线程中, 及在dispatch_get_main_queue() 中,
    执行到sync 时 向dispatch_get_main_queue()插入 同步 threed。
    sync 会等到 后面block 执行完成才返回, sync 又再 dispatch_get_main_queue() 队列中,
    它是串行队列,sync 是后加入的,
    前一个是主线程,所以 sync 想执行 block 必须等待主线程执行完成,
    主线程等待 sync 返回,去执行后续内容。
    照成死锁,sync 等待mainThread 执行完成, mianThread 等待sync 函数返回。
    下面例子:
    
    - (void)viewDidLoad{
        [super viewDidLoad];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"=================1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"=================2"); });
            NSLog(@"=================3"); });
    }
    程序会完成执行,为什么不会出现死锁。
    
    首先: async 在主线程中 创建了一个异步线程 加入 全局并发队列,
    async 不会等待block 执行完成,立即返回,
    
    1,async 立即返回, viewDidLoad 执行完毕,及主线程执行完毕。
    
    2,同时,全局并发队列立即执行异步 block , 打印 1, 
    当执行到 sync 它会等待 block 执行完成才返回, 
    及等待dispatch_get_main_queue() 队列中的 mianThread 执行完成, 然后才开始调用block 。
    

    ===Runloop===
    runloop是什么?在项目中有具体的应用吗?好像没什么可以用到的地方啊?

    Runloop,运行循环。当我们应用启动之后,就会在主线程开启一个运行循环,来监听我们的触摸事件和消息,等。注意,这个时候,子线程中是默认不开启运行循环的,另外,也不建议开启。
    
    在项目中应用的话,
    1. RunLoop保证子线程的长时间存活,而不是执行完任务后就立刻销毁的应用场景。这个是系统自己在底层自己处理得。
    2. 滑动与图片刷新: 当tableview的cell上有需要从网络获取的图片的时候,滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,但是会造成卡顿。可以让设置图片的任务在CFRunLoopDefaultMode下进行,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下进行,不去设置图片,而是当停止的时候,再去设置图片。
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      // 只在NSDefaultRunLoopMode下执行(刷新图片)
      [self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];   
    }
     
    3. 保持子线程一直处理事件: 为了保证线程长期运转,可以在子线程中加入RunLoop,并且给Runloop设置item,防止Runloop自动退出。
    
    
    + (void)networkRequestThreadEntryPoint:(id)__unused object {
        @autoreleasepool {
            [[NSThread currentThread] setName:@"AFNetworking"];
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
            [runLoop run];
        }
    }
    
    + (NSThread *)networkRequestThread {
        static NSThread *_networkRequestThread = nil;
        static dispatch_once_t oncePredicate;
        dispatch_once(&oncePredicate, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
            [_networkRequestThread start];
        });
        return _networkRequestThread;
    }
    - (void)start {
        [self.lock lock];
        if ([self isCancelled]) {
            [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        } else if ([self isReady]) {
            self.state = AFOperationExecutingState;
            [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        }
        [self.lock unlock];
    }
    

    ===RunTime===
    你会遇到的Runtime面试题

    Objective-C 是面相运行时的语言(runtime oriented language),就是说它会尽可能的把编译和链接时要执行的逻辑延迟到运行时。这就给了你很大的灵活性,你可以按需要把消息重定向给合适的对象,你甚 至可以交换方法的实现,等等。

    RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

    以下面的代码为例:

    [obj makeText];

    其中obj是一个对象,makeText是一个函数名称。对于这样一个简单的调用。在编译时RunTime会将上述代码转化成

    objc_msgSend(obj,@selector(makeText));

    首先,编译器将代码[obj makeText];转化为objc_msgSend(obj, @selector (makeText));,在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

    Objective-C Runtime 是什么?

    Objective-C 的 Runtime 是一个运行时库(Runtime Library),它是一个主要使用 C 和汇编写的库,为 C 添加了面相对象的能力并创造了 Objective-C。这就是说它在类信息(Class information) 中被加载,完成所有的方法分发,方法转发,等等。Objective-C runtime 创建了所有需要的结构体,让 Objective-C 的面相对象编程变为可能。

    Method Swizzling 原理

    在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。

    我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,

    我们可以利用 class_replaceMethod 来修改类,

    我们可以利用 method_setImplementation 来直接设置某个方法的IMP,……

    归根结底,都是偷换了selector的IMP。

    基础核心

    1、Objective-C对象模型及应用
    2、 谈Objective-C block的实现


    冷门问题 没答案自己解决

    1、请解释下method swizzling,并说出你一般什么时候会用到它?
    2、当一个空指针(nil pointer)调用了一个方法会发生什么?
    3、为什么retainCount绝对不能用在发布的代码中?请给出两个相对独立的解释。
    3、请说明一下你查找或者解决内存泄露的处理过程。这个可以深入了解面试者对内存管理方面的知识,instruments的运用及其调试的处理过程。
    4、解释下自动回收池(autorelease pool)在程序运行时是如何运作的。
    5、当处理属性申明的时候,原子(atomic)跟 非原子(non-atomic)属性有什么区别?
    6、在C语言中,你如何能用尽可能短的时间来倒转一个字符串?
    7、遍历一个NSArray和一个NSSet,哪一个更快?
    8、解释代码签名(code signing)是如何运作的。
    9、Objective-C中的posing指的是什么?
    10、copy跟retain有什么区别?
    11、frames跟bounds有哪些区别?
    12、执行如下的代码会发生什么情况?
    Ball *ball = [[[[Ball alloc] init] autorelease] autorelease];


    趣味问答

    最近有没有开发什么好玩的东西?
    你最引以为豪的作品是什么?
    谈一谈你常用的开发工具都有哪些优势?
    你最敬佩的独立Mac或者iOS应用开发者是谁?
    最喜欢什么项目?哪种类型的?
    你觉得Xcode有哪些需要改进的地方?
    iOS上你最喜欢哪些API?
    是否有最中意的错误报告?
    你最爱以哪种方式来检验一项新技术是否好用?

    相关文章

      网友评论

          本文标题:iOS面试宝典《二》--经典案例

          本文链接:https://www.haomeiwen.com/subject/ogkpxftx.html