美文网首页
iOS面试题总结(三)

iOS面试题总结(三)

作者: 沧州宁少 | 来源:发表于2017-12-06 10:07 被阅读0次

    iOS面试(三)

    1.MVC具有什么样的优势,各个模块之间怎么通信,比如Button后怎么通知Model

    MVC是一种设计思想,是一种架构模式,是一种把应用所有类组织起来的策略。他们把程序分成3块。

    M: 存储数据,处理程序的逻辑部分。
    C: 控制你的model如何呈现在屏幕上。他需要数据的时候会去Model获取数据。需要视图显示的时候会通知视图显示。它是model和view之间的桥梁。它使model和view解耦。

    V:视图的展示。根据model来创建视图。

    Controller to Model
    可以单向通信。控制器需要知道model中的一切。还要同model完全的通信的能力。

    Controller to View
    可以直接进行单向通信,Controller通过View来布局界面

    Model to View

    永远不要直接通信。Model是独立于UI的存在的。View 通过Controller获取Model数据。

    View to Controller

    View不能对Controller知道的太多。隐藏他们使用间接通信的方式

    • targer action
    • delegate
    • dataSource

    Model to Controller

    Model独立于UI存在的。Model无法直接访问Controller这样就绑死了。通过Notification&&KVO

    优点:

    • 低耦合性
    • 开发分工
    • 有利于组件重用
    • 可维护性

    2. UITableView 的相关优化

    从几个角度去分析

    • 基础优化(高度缓存,cell重用)

      • 正确使用TableViewCell的缓存机制
      • 提前计算机cell的高度。把cell的高度缓存起来。因为height forRow 这个方法会频繁的调用所以不适合存在大量的计算。
      • 下载图片避免阻塞主线程
      • 按需求加载。设置一个需要加载的Arr。超过加载范围的cell清除显示的内容。会使得滚动的范围内出现大量的留白。但是会仅绘制目标的cell
      • 减少subViews的数量
      • 尽量重用开销比较大的对象
      • 减少复杂的计算
      • 不要动态add或者remove子控件
    • 学会使用分析工具

      • instruments中的Core Animation instrument

      • instruments中的OpenGL ES Drive instrument

      • Xcode的debug的一些列的调试工具

    • 异步绘制

      • 开启一条异步线程绘制cell
      • 绘制Cell使用的是CALayer而不是UIView
      • UIView的绘制是建立在Core Graphic上,使用的是CPU。而CALayer的绘制是建立在Core Animation上,CPU,GPU均可。UIView的绘制是从下到上一层层的绘制。然后渲染。CALayer处理的是Texture,利用GPU的Texture Cache和独立的浮点数计算单元加速文理的处理。

    KVO,Notification,delegate各自的优缺点

    • delegate 委托
    • 通知中心 NotificationCenter
    • KVO键值观察

    delegate的优势:

    • 严格的语法,协议清晰
    • 不会遗忘。
    • 可以有返回值,delegate可以反馈信息给Controller
    • 出现问题可以很好的定位
      缺点:

    代码较多

    NotificationCenter优点:

    • 1对多
    • 使用简单

    缺点:

    • 无法对发出通知的路径进行追踪
    • 当监听者销毁的时候需要取消监听
    • 发送者无法确定监听者收到了消息

    KVO

    • 使用简单可以实现2个对象同步
    • 因为使用字符串可以嵌套观察
      确定
    • 字符串不会报警告
    • 如果一个controller观察多个属性 需要写很多的if else{}

    如何手动通知 KVO

    控制器中重写某个属性的set方法。在控制器中监听。

    Objective-C 中的copy方法

    对象的复制就是复制一个对象作为副本。它会开辟一块的内存来存储副本对象。就像文件复制一样。源对象和副本对象是两块内存。对象如果想要实现复制功能。必须遵循NSCopying协议或者NSMutableCopying协议。

    copy:产生的对象是不可变的
    mutableCopy产生的对象是可以变的。

    深拷贝和浅拷贝:

    浅拷贝指的是复制对象本身,对象的属性,包含的对象不做复制
    深拷贝既指复制对象本身,对象的熟悉也会复制一份

    Foundation中支持复制的类,默认是浅复制

    深浅拷贝和retain之间的关系

    • 对一个不可变对象指向copy操作,相当于retain
    • 当我们使用mutableCopy得到的都是一个可变对象
    • copy一个可变对象得到的对象不可变。

    runtime 中,SEL 和 IMP 的区别

    方法名 SEL -- 方法的名称

    一个IMP 指向该方法的具体实现的函数指针,说白了IMP就是方法的实现。

    autoreleasePool的使用场景和原理

    autoreleasePool 是OC的一个类,他不是天生就有的,而是我们手动创建的。当我们创建iPhone工程的时候,系统会为我们创建一个AutoreleasePool.这个pool写在main函数中。它的内部有一个可变数组 用来存储被标记autorelease的对象。

    内存管理一直是重中之重,尽管现在已经是ARC时代,但是理解ARC依然非常重要,只有了解了Autorelease的原理,我们才能算是理解了Objective-C的内存管理机制。

    autoreleased 对象什么时候释放

    autorelease本质就是延迟调用release,那autoreleased的对象到底什么时候释放

    我们都注意到了AutoreleasePool release本质是AutorelesePage::pop。那么本质执行了什么。

    • 每个线程的autoreleasePool本质就是一个指针的堆栈。
    • 每一个指针代表一个需要release对象或者POOL_SENTINEL(哨兵对象,代表AutoreleasePool的边界)
    • 一个pool token 就是这个pool对于的POOL_SENTINEL对应的内存地址。当这个pool被pop之后,所有内存地址在 pool token 之后的对象都会被 release
    • 这个堆栈 被划分成一个以page为节点的双向链表。pages会动态的增加和删除
    • Thread-local storage(线程局部存储)指向hotPage,即新增加的autorelease所在的page.

    AutoreleasePage内部结构

    • magic AutoreleasePoolPage 的结构是否完整
    • next指向新增的autorelease对象的下一个地址。初始化的时候指向begin
    • thread 当前线程
    • parent 指向父结点,第一个结点的 parent 值为 nil
    • child指向子节点,最后一个节点的child值为nil

    next = begin() 代表autoreleasePoolPage为空。当 next == end() 时,表示 AutoreleasePoolPage 已满。

    @autoreleasepool{}

    转换为C++代码

    extern "C" __declspec(dllimport)
    void * objc_autoreleasePoolPush(void);
    extern "C" __declspec(dllimport) 
    void objc_autoreleasePoolPop(void *);
    struct __AtAutoreleasePool {
          __AtAutoreleasePool()
           {atautoreleasepoolobj =   objc_autoreleasePoolPush();}
           
          ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
           void * atautoreleasepoolobj;
     };
    

    本质就是结构体构造的时候调用构造方法执行push操作。析构的时候执行pop函数

    因此autoreleasepool运行的过程可以理解为objc_autoreleasePoolPush(),[obj autorelease],objc_autoreleasePoolPop

    AutoreleasePoolPage 的 push 函数的作用和执行过程。一个Push操作其实就是创建一个新的autoreleasepool。对应的就是往AutoreleasePage的next插入一个哨兵对象。并且返回这个哨兵对象的地址做为pop时候的返回值来使用。

    push函数通过调用autoreleaseFast 函数来执行具体的插入操作。

    static inline id *autoreleaseFast(id obj)
    {
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {
        return page->add(obj);
    } else if (page) {
        return autoreleaseFullPage(obj, page);
    } else {
        return autoreleaseNoPage(obj);
    }
    }
    

    分为3种情况。

    • 存在&&不满 直接插入当前AutorelesePage的next位置
    • 满了 创建一个新的autoreleasePage
    • 不存在 新创建一个

    每调用一次 push 操作就会创建一个新的 autoreleasepool ,即往 AutoreleasePoolPage 中插入一个 POOL_SENTINEL ,并且返回插入的 POOL_SENTINEL 的内存地址

    Autorelease操作

    与push操作类型。Push操作是插入一个哨兵对象并且返回哨兵对象的地址供给pop对象使用。而autorelease插入的则是一个具体的autorelease对象。

    pop操作

    同理,前面提到的objc_autoreleasePoolPop(void*)也是调用的AutoreleasePoolPage的pop函数。Pop函数的入参就是Push对象的返回值。也就是POOL_SENTINEL的内存地址,即pool token。这个地址之后的所有的对象进行release操作。知道pool token所在的page的next指向pool token为止。

    可能会用到autoreleasePool对象的地方

    • 编写的程序不是基于UI框架
    • 创建了一个辅助线程
    • 编写的循环中创建了大量的临时变量

    block 为什么会有循环引用

    Block块会对其中的所有成员进行强引用,如果里面的成员也对它进行强引用的话,会形成一个闭合的环。形成循环引用

    NSOperation 和 GCD 的区别

    GCD是基于C的底层Api,NSOpertaion属于Objective-C类。iOS首先引入的NSOperation,iOS4之后引入的GCD和NSOpertaionQueue。相对GCD。NSOperation优势

    • NSOperation拥有多个函数可以调用
    • NSOperationQueue可以设置多个Operation的依赖关系
    • 通过KVO可以监听NSOperation的isExecuted(是否执行),isCancel(是否取消),isFinish(是否完成)
    • NSOperationQueue可以方便的管理并发。Operation之间的优先级。
      GCD主要与block结合使用。代码简洁高效。

    GCD也可以实现复杂的多线程应用,主要是建立个个线程时间的依赖关系这类的情况,但是需要自己实现相比NSOperation要复杂。
    具体使用哪个,依需求而定。从个人使用的感觉来看,比较合适的用法是:除了依赖关系尽量使用GCD,因为苹果专门为GCD做了性能上面的优化。

    GCD技术是一个轻量级的,底层实现隐藏的神奇技术。通过gcd和block轻松实现多线程编程。有时间gcd比其他的多线程编程更有效当然,有时候GCD不是最佳选择,另一个多线程编程的技术 NSOprationQueue 让我们能够将后台线程以队列的形式依序执行,并提供了更多操作的入口。这和GCD的实现有些类似。

    两者的区别

    • GCD是C语言构筑的APi.而NSOpertaionQueue是NSObject对象。GCD由Block构建成任务,这是一个轻量级的数据结构。而Opertaion作为对象为我们提供了更多的选择,
    • 在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务
    • NSOperation能够方便地设置依赖关系,我们可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行
    • KVO监听NSOperation的完成取消执行的情况。
    • 在NSOperation中,我们能够设置任务的优先级。GCD只能设计队列的优先级。
    • 我们可以写一个自定义的类继承自NSOperation 提高代码的复用度。

    如何设计图片缓存

    • 提供相应速度
    • 节省流量
    • 提高用户体验。

    提高响应速度:因为图片一旦缓存在本地之后,那么本地IO数据的读取,远比网络中得IO读取效率要高的多。所以可以提高响应速度

    节省流量: 一张图片在某些情况下,只加载一次,之后便不会重新加载,减少了网络流量。减少流量肯定是必然的。介于国内的流量费用这么贵,是肯定必要的

    提高用户体验: 本地存储。

    如何设计一个网络控件

    • 交互方式

    • 显示样式

    • 数据使用

    • 选择正确的初始化方式

      • 分别在awakeFromNib和initWithFrame分别调用setUp
    • 调整布局的时机

      • frame的话在layoutSubView/viewDidLayoutSubView时更改
      • 如果使用AutoLayout可以直接写
    • 正确的使用touches方法

      • 尽量使用手势
    • drawRect和CALayer的使用与动画

      • 绘制完成之后调用setNeedDisplay完成绘制
    • UIControl与UIButton

    • 更友好的支持xib
      IBInspectable 。IB_DESIGNABLE方便xib可视化

    • 不规范图形和事件的触摸范围

      - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

      - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

    • 合理使用KVO

      • 自定义控件内部使用KVO,监听自身各种属性的变化

    相关文章

      网友评论

          本文标题:iOS面试题总结(三)

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