今日头条面试题

作者: AKyS佐毅 | 来源:发表于2020-01-04 14:18 被阅读0次

    1、kvo底层实现cell中一个点赞功能如果用kvo需要注意什么?(提示涉及cell复用问题)

    关于TableViewCell中的KVO的使用

    2、UIView和layer的关系,组合关系,为什么不做成继承?两种方式各有什么利弊?

    UIView和CALayer是啥关系?

    3、Swift用过没?它就使用了面向协议?

    谈谈Swift面向协议编程

    4、显式动画,隐式动画平时哪里使用多线程?要注意什么?

    iOS动画原理--隐式动画

    5、AFNetworking怎么实现线程保活?

    AFNetworking3.0后为什么不再需要常驻线程

    6、reachability如何检测到网络状态变化?

    iOS下的实际网络连接状态检测:RealReachability

    7、渲染UI为什么要在主线程?

    为什么刷新UI在主线程

    8、FB那个Async库都做了什么?消息转发,哪些步骤能被利用?

    iOS保持界面流畅的技巧和AsyncDisplay介绍

    9、算法:一个数组,有个滑动窗口,求每次窗口中的中位数。

    滑动窗口的中位数
    滑动窗口的中位数

    iOS基础

    1:讲讲你对atomic 和noatomic的理解
    2:被 weak 修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable 么?里面的结构可以画出来么?
    3:block 用什么修饰?strong 可以?
    4:block 为什么能够捕获外界变量? __block做了什么事?
    5:谈谈你对事件的传递链和响应链的理解 (这个面试题,如果求职者能够回答一些实际开发相关的处理,不只是简单的概念,予以加分)
    6:谈谈 KVC 以及 KVO 的理解?
    7:RunLoop 的作用是什么?它的内部工作机制了解么?
    8:苹果是如何实现 autoreleasepool的?
    9:谈谈你对 FRP (函数响应式) 的理解,延伸一下 RxSwift 或者 RAC!
    10:平时开发有没有玩过 Instrument ?

    Runtime

    1:什么是 isa,isa 的作用是什么?
    2:一个实例对象的isa 指向什么?类对象指向什么?元类isa 指向什么?
    3:objc 中类方法和实例方法有什么本质区别和联系?
    4:load 和 initialize 的去呗?
    5:_objc_msgForward 函数是做什么的?直接调用会发生什么问题?
    6:简述下 Objective-C 中调用方法的过程
    7:能否想向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

    iOS基础

    1.1、讲讲你对atomic 和noatomic的理解

    • 1、原子操作对线程安全并无任何安全保证。被 atomic 修饰的属性(不重载设置器和访问器)只保证了对数据读写的完整性,也就是原子性,但是与对象的线程安全无关。

    • 2、线程安全有保障、对性能有要求的情况下可使用 nonatomic替代atomic,当然也可以一直使用atomic。

    • 3、实现:
      原子性不可能由软件单独保证--必须需要硬件的支持,因此是和架构相关的。在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。

    • 4、讲讲你对atomic 和noatomic的理解

    1.2、被 weak 修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable 么?里面的结构可以画出来么

    • 被weak修饰的对象在被释放时候会置为nil,不同于assign;

    • Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

    • 1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

    • 2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

    • 3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    struct SideTable {
        // 保证原子操作的自旋锁
        spinlock_t slock;
        // 引用计数的 hash 表
        RefcountMap refcnts;
        // weak 引用全局 hash 表
        weak_table_t weak_table;
    }
    
    struct weak_table_t {
        // 保存了所有指向指定对象的 weak 指针
        weak_entry_t *weak_entries;
        // 存储空间
        size_t    num_entries;
        // 参与判断引用计数辅助量
        uintptr_t mask;
        // hash key 最大偏移值
        uintptr_t max_hash_displacement;
    };
    

    1.3、block 用什么修饰?strong 可以

    • block 本身是像对象一样可以 retain,和 release。但是,block 在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
    • 使用 retain 也可以,但是block的retain行为默认是用copy的行为实现的。因为 block 变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把 block 拷贝(copy)到堆,所以说为了 block 属性声明和实际的操作一致,最好声明为 copy。
    image.png image.png

    auto变量的捕获

    image.png

    Block的类型

    image.png image.png

    对象类型的auto变量

    image.png

    __block修饰符

    image.png

    __block的内存管理

    image.png image.png

    对象类型的auto变量、__block变量

    image.png
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        MJPerson *p = [[MJPerson alloc] init];
        
        __weak MJPerson *weakP = p;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"1-------%@", p);
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"2-------%@", weakP);
            });
        });
        
        NSLog(@"touchesBegan:withEvent:");
    }
    

    测试结果:

    touchesBegan:withEvent:
    1-------<MJPerson: 0x6000037984e0>
    MJPerson - dealloc
    2-------(null)
    

    1.5:谈谈你对事件的传递链和响应链的理解

    1、史上最详细的iOS之事件的传递和响应机制-原理篇
    2、iOS响应者链、事件的传递

    1.6、谈谈 KVC 以及 KVO 的理解?

    image.png image.png

    探究KVO的底层实现原理

    iOS窥探KVO底层实现原理篇

    1.7、RunLoop 的作用是什么?它的内部工作机制了解么?

    深入理解RunLoop

    image.png image.png

    RunLoop与线程

    image.png

    RunLoop相关的类

    image.png

    CFRunLoopModeRef

    image.png

    CFRunLoopObserverRef

    image.png

    RunLoop的运行逻辑

    image.png image.png

    1.8、苹果是如何实现 autoreleasepool的

    Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体了解么

    arc下编译器会优化成

    void *context = objc_autoreleasePoolPush();
    // {}中的代码
    objc_autoreleasePoolPop(context);
    
    • 向一个结构AutoreleasePoolPage,中写入需要自动释放的对象,类似一种标记,调用objc_autoreleasePoolPop(context)后,就会把这中间的对象release一下。
    • 这里要注意的是,方法返回值是怎么做到自动释放的?其使用Thread Local Storage(TLS)线程局部存储,每次存入线程或者从线程取出来。
    • 我们没有卸载{}中的自动释放对象,会在每个runloop结束时候去释放,相当于一个大的autoreleasepool中。
    image.png

    AutoreleasePoolPage的结构

    image.png image.png

    Runloop和Autorelease

    image.png

    1.9、谈谈你对 FRP (函数响应式) 的理解,延伸一下 RxSwift 或者 RAC!

    参考文章:RxSwift(1)— 初探 看这一篇文章也就够了!然后结合 RxSwift 映射到 RAC!函数响应式的思想是不变的!至于内部的封装有所不同,但是最终却是殊途同归!

    1.10、平时开发有没有玩过 Instrument

    iOS 使用Instruments的工具小结

    Runtime

    1.1、什么是 isa,isa 的作用是什么?

    image.png image.png

    Class的结构

    image.png

    1.2、isa、superclass总结

    image.png

    1.3、load 和 initialize 的区别?

    +load

    • 1、只要程序启动就会将所有类的代码加载到内存中(在main函数执行之前), 放到代码区(无论该类有没有被使用到都会被调用)
    • 2、+load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
    • 3、当父类和子类都实现+load方法时, 会先调用父类的+load方法, 再调用子类的+load方法
    • 4、先加载原始类,再加载分类的+load方法
    • 5、当子类未实现+load方法时,不会调用父类的+load方法
    • 6、多个类都实现+load方法,+load方法的调用顺序,与Compile Sources中出现的顺序一致

    +initialize

    • 1、当类第一次被使用的时候就会调用(创建类对象的时候)
    • 2、initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次
    • 3、initialize用于对某一个类进行一次性的初始化
    • 4、先调用父类的initialize再调用子类的initialize
    • 5、当子类未实现initialize方法时,会把父类的实现继承过来调用一遍,再次之前父类的initialize方法会被优先调用一次
    • 6、当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)
    image.png image.png

    1.4、_objc_msgForward 函数是做什么的?直接调用会发生什么问题?

    当对象没有实现某个方法 ,会调用这个函数进行方法转发。某方法对应的IMP没找到,会返回这个函数的IMP去执行)

    • 1.调用resolveInstanceMethod:方法,允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回。如果仍没实现,继续下面的动作。
    • 2.调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接转发给它。如果返回了nil,继续下面的动作。
    • 3.调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。
    • 4.调用forwardInvocation:方法,将地3步获取到的方法签名包装成Invocation传入,如何处理就在这里面了。

    如果直接调用这个方法,就算实现了想调用的方法,也不会被调用,会直接走消息转发步骤。

    1.5、简述下 Objective-C 中调用方法的过程

    image.png image.png image.png
    image.png image.png

    1.6、能否想向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

    • 1.不能向编译后得到的类增加实例变量
    • 2.能向运行时创建的类中添加实例变量

    解释:

    • 1.编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定, runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout来处理 strong weak引用.所以不能向存在的类中添加实例变量
    • 2.运行时创建的类是可以添加实例变量,调用class_addIvar函数.但是的在调用objc_allocateClassPair之后, objc_registerClassPair之前,原因同上.

    相关文章

      网友评论

        本文标题:今日头条面试题

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