iOS面试题总结(三)

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面试题总结(三))