⚠️2019年iOS面试反思总结⚠️--⚠️不断更新当中ing⚠️

2019年的夏天,成都的天气格外的闷热。html

由于公司的缘由,致使我不得不考虑去面试一家稳定成熟的大公司,本身从一开始也一直梦想着进入一家能够获得广阔平台的公司发展,有朝一日能够当上技术负责人,但是现实老是那么残酷,4年了,4年了,每次面试感受本身仍是只是会回答:我知道,可是没有深刻研究过。这个我了解过,可是尚未处理过。而后就是一个劲的出汗来排解心里的压力。3次换工做,面试过30-40次,每次都只是为了面试而面试(背题),不少东西都记住不。哎~ 我太难了~ 太难了~前端

无论面试是否经过,仍是来总结一下面试常常遇到的问题吧。ios

Object-C系列面试题总结

基础题:

1.Objective-C的类能够多重继承么?能够实现多个接口么?Category是什么?重写一个类的方式用继承好仍是分类好?为何不要在category中重写一个类原有的方法?

答:
Objective-c的类不能够有多继承,OC里面都是单继承,多继承能够用protocol委托代理来模拟实现
能够实现多个接口,能够经过实现多个接口完成OC的多重继承

Category是类别;

⚠️重写一个类的方式用继承好仍是分类好:
重写一个类的方式用继承仍是分类.取决于具体状况.假如目标类有许多的子类.咱们须要拓展这个类又不但愿影响到原有的代码.继承后比较好.
若是仅仅是拓展方法.分类更好.(不须要涉及到原先的代码)

分类中方法的优先级比原来类中的方法高,也就是说,在分类中重写了原来类中的方法,那么分类中的方法会覆盖原来类中的方法

⚠️为何不要在category中重写一个类原有的方法:
一、category没有办法去代替子类,它不能像子类同样经过super去调用父类的方法实现。若是category中重写覆盖了当前类中的某个方法,那么这个当前类中的原始方法实现,将永远不会被执行,这在某些方法里是致命的。(ps:这里提一下,+(void)load方法是一个特例,它会在当前类执行完以后再在category中执行。)
二、同时,一个category也不能可靠的覆盖另外一个category中相同的类的相同的方法。例如UIViewController+A与UIViewController+B,都重写了viewDidLoad,咱们就没法控制谁覆盖了谁。
三、经过观察头文件咱们能够发现,Cocoa框架中的许多类都是经过category来实现功能的,可能不经意间你就覆盖了这些方法中的其一,有时候就会产生一些没法排查的异常缘由。
四、category的诞生只是为了让开发者更加方便的去拓展一个类,它的初衷并非让你去改变一个类。
结论:
要重写方法,固然咱们首推经过子类重写父类的方法,在一些不方便重写的状况下,咱们也能够在category中用runtime进行method swizzling(方法的偷梁换柱)来实现。

若是须要面试资料的请进群领取,iOS开发交流学习群

2.请说明并比较如下关键词:strong, weak, assign, copy。

答:
* strong表示指向并拥有该对象。其修饰的对象引用计数会增长1。该对象只要引用计数不为0则不会被销毁。固然强行将其设为nil能够销毁它。
* weak表示指向但不拥有该对象。其修饰的对象引用计数不会增长。无需手动设置,该对象会自行在内存中销毁。
* assign主要用于修饰基本数据类型,如NSInteger和CGFloat,这些数值主要存在于栈上。
* weak 通常用来修饰对象,assign通常用来修饰基本数据类型。缘由是assign修饰的对象被释放后,指针的地址依然存在,形成野指针,在堆上容易形成崩溃。而栈上的内存系统会自动处理,不会形成野指针。
* copy与strong相似。不一样之处是strong的复制是多个指针指向同一个地址,而copy的复制每次会在内存中拷贝一份对象,指针指向不一样地址。copy通常用在修饰有可变对应类型的不可变对象上,如NSString, NSArray, NSDictionary。
* Objective-C 中,基本数据类型的默认关键字是atomic, readwrite, assign;普通属性的默认关键字是atomic, readwrite, strong。

3.用@property声明的 NSString / NSArray / NSDictionary 常用 copy 关键字,为何?若是改用strong关键字,可能形成什么问题?

答:
用 @property 声明 NSString、NSArray、NSDictionary 常用 copy 关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。
1\. 由于父类指针能够指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本。
2\. 若是咱们使用是 strong ,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性。
//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。

4.浅拷贝和深拷贝的区别?

答:
浅拷贝:只复制指向对象的指针,而不复制引用对象自己。
深拷贝:复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。

iOS深浅拷贝c++

5.Objective-C 如何对内存管理的,说说你的见解和解决方法?

答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
3). 内存释放池Release Pool:把须要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中全部的内存空间也被自动释放掉。内存池的释放操做分为自动和手动。自动释放受runloop机制影响。

6.继承、分类和类扩展

答:
1\. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
2\. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展能够扩展属性、成员变量和方法。
3\. 继承能够增长,修改或者删除方法,而且能够增长属性。

iOS中Category和Extension 原理详解git

IOS 分类(category)、扩展(Extension)和继承(inheritance)的区别?程序员

iOS 成员变量 实例变量 属性变量web

7.咱们说的OC是动态运行时语言是什么意思?

答:
主要是将数据类型的肯定由编译时,推迟到了运行时。简单来讲, 运行时机制使咱们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

8.什么是 KVO 和 KVC?谈谈 KVC 以及 KVO 的理解?

答:
KVC(key-value-coding)键值编码,是一种间接访问实例变量的方法。提供一种机制来间接访问对象的属性。
一、给私有变量赋值。
二、给控件的内部属性赋值(如自定义UITextFiled的clearButton,或placeholder的颜色,通常可利用runtime获取控件的内部属性名,Ivar *ivar = class_getInstanceVariable获取实例成员变量)。
[textField setValue:[UIColor redColor] forKeyPath:@"placeholderLabel.textColor"];
三、结合Runtime,model和字典的转换(setValuesForKeysWithDictionary,class_copyIvarList获取指定类的Ivar成员列表)

KVO是一种基于KVC实现的观察者模式。当指定的被观察的对象的属性更改了,KVO会以自动或手动方式通知观察者。
事例:监听 ScrollView 的 contentOffSet属性
[scrollview addObserver:self forKeyPath:@"contentOffset"  options:NSKeyValueObservingOptionNew context:nil];

KVO图面试

9.block的注意点

答:
1\. 在block内部使用外部指针且会形成循环引用状况下,须要用__weak修饰外部指针:
    __weak typeof(self) weakSelf = self; 
2\. 在block内部若是调用了延时函数还使用弱指针会取不到该指针,由于已经被销毁了,须要在block内部再将弱指针从新强引用一下。
    __strong typeof(self) strongSelf = weakSelf;
3\. 若是须要在block内部改变外部栈区变量的话,须要在用__block修饰外部变量。

10.堆、栈和队列

答:
1.从管理方式来说
对于栈来说,是由编译器自动管理,无需咱们手工控制;
对于堆来讲,释放工做由程序员控制,容易产生内存泄露(memory leak)
2.从申请大小大小方面讲
栈空间比较小
堆控件比较大
3.从数据存储方面来说
栈空间中通常存储基本类型,对象的地址
堆空间通常存放对象自己,block的copy等
答:
# 堆
堆是一种通过排序的树形数据结构,每一个节点都有一个值,一般咱们所说的堆的数据结构是指二叉树。因此堆在数据结构中一般能够被看作是一棵树的数组对象。并且堆须要知足一下两个性质:

1)堆中某个节点的值老是不大于或不小于其父节点的值;

2)堆老是一棵彻底二叉树。

堆分为两种状况,有最大堆和最小堆。将根节点最大的堆叫作最大堆或大根堆,根节点最小的堆叫作最小堆或小根堆,在一个摆放好元素的最小堆中,父结点中的元素必定比子结点的元素要小,但对于左右结点的大小则没有规定谁大谁小。

堆经常使用来实现优先队列,堆的存取是随意的,这就如同咱们在图书馆的书架上取书,虽然书的摆放是有顺序的,可是咱们想取任意一本时没必要像栈同样,先取出前面全部的书,书架这种机制不一样于箱子,咱们能够直接取出咱们想要的书。

# 栈
栈是限定仅在表尾进行插入和删除操做的线性表。咱们把容许插入和删除的一端称为栈顶,另外一端称为栈底,不含任何数据元素的栈称为空栈。栈的特殊之处在于它限制了这个线性表的插入和删除位置,它始终只在栈顶进行。

栈是一种具备后进先出的数据结构,又称为后进先出的线性表,简称 LIFO(Last In First Out)结构。也就是说后存放的先取,先存放的后取,这就相似于咱们要在取放在箱子底部的东西(放进去比较早的物体),咱们首先要移开压在它上面的物体(放进去比较晚的物体)。

堆栈中定义了一些操做。两个最重要的是PUSH和POP。PUSH操做在堆栈的顶部加入一个元素。POP操做相反,在堆栈顶部移去一个元素,并将堆栈的大小减一。

栈的应用—递归

# 队列
队列是只容许在一端进行插入操做、而在另外一端进行删除操做的线性表。容许插入的一端称为队尾,容许删除的一端称为队头。它是一种特殊的线性表,特殊之处在于它只容许在表的前端进行删除操做,而在表的后端进行插入操做,和栈同样,队列是一种操做受限制的线性表。

队列是一种先进先出的数据结构,又称为先进先出的线性表,简称 FIFO(First In First Out)结构。也就是说先放的先取,后放的后取,就如同行李过安检的时候,先放进去的行李在另外一端老是先出来,后放入的行李会在最后面出来。

11.什么是多态?什么是分类?什么是协议?

答:
多态在面向对象语言中指同一个接口有多种不一样的实现方式,在OC中,多态则是不一样对象对同一消息的不一样响应方式;子类经过重写父类的方法来改变同一方法的实现.体现多态性
通俗来说: 多态就父类类型的指针指向子类的对象,在函数(方法)调用的时候能够调用到正确版本的函数(方法)。
多态就是某一类事物的多种形态.继承是多态的前提;
答:
分类: 在不修改原有类代码的状况下,能够给类添加方法
Categroy 给类扩展方法,或者关联属性, Categroy底层结构也是一个结构体:内部存储这结构体的名字,那个类的分类,以及对象和类方法列表,协议,属性信息
经过Runtime加载某个类的全部Category数据
把全部Category的方法、属性、协议数据,合并到一个大数组中后面参与编译的Category数据,会在数组的前面
将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
答:
协议:协议是一套标准,这个标准中声明了不少方法,可是不关心具体这些方法是怎么实现的,具体实现是由遵循这个协议的类去完成的。
在OC中,一个类能够实现多个协议,经过协议能够弥补单继承的缺陷可是协议跟继承不同,协议只是一个方法列表,方法的实现得靠遵循这个协议的类去实现。

12.UIView 和 CALayer 是什么关系?

答:
UIView 继承 UIResponder,而 UIResponder 是响应者对象,能够对iOS 中的事件响应及传递,CALayer 没有继承自 UIResponder,因此 CALayer 不具有响应处理事件的能力。CALayer 是 QuartzCore 中的类,是一个比较底层的用来绘制内容的类,用来绘制UI

UIView 对 CALayer 封装属性,对 UIView 设置 frame、center、bounds 等位置信息时,其实都是UIView 对 CALayer 进一层封装,使得咱们能够很方便地设置控件的位置;例如圆角、阴影等属性, UIView 就没有进一步封装,因此咱们仍是须要去设置 Layer 的属性来实现功能。

UIView 是 CALayer 的代理,UIView 持有一个 CALayer 的属性,而且是该属性的代理,用来提供一些 CALayer 行的数据,例如动画和绘制。

13.说一下 JS 和 OC 互相调用的几种方式?

答:
js调用oc的三种方式:

方式一:经过替换js中的function(方法)
方式二:经过注入对象,直接调用对象方法
方式三:利用网页重定向,截取字符串.

oc调用js代码两种方式

1.经过webVIew调用 webView stringByEvaluatingJavaScriptFromString: 调用
2.经过JSContext调用[context evaluateScript:];

14.如何理解HTTP?/Http 和 Https 的区别?Https为何更加安全?

答:
HTTP本质上是一种协议,全称是Hypertext Transfer Protocol,即超文本传输协议。HTTP是一个基于TCP/IP通讯协议来传递数据, 该协议用于规定客户端与服务端之间的传输规则,所传输的内容不局限于文本(其实能够传输任意类型的数据)。

一次HTTP能够看作是一个事务,其工做过程分为4步:

1.客户端与服务器创建链接
2.创建链接后,客户端给服务端发送请求
3.服务器收到消息后,给与响应操做
4.客户端收到消息后,展现到屏幕上,断开链接.
答:
区别

1.HTTPS 须要向机构申请 CA 证书,极少免费。

2.HTTP 属于明文传输,HTTPS基于 SSL 进行加密传输。

3.HTTP 端口号为 80,HTTPS 端口号为 443 。

4.HTTPS 是加密传输,有身份验证的环节,更加安全。

安全

SSL(安全套接层) TLS(传输层安全)

以上二者在传输层之上,对网络链接进行加密处理,保障数据的完整性,更加的安全。

15.编程中的六大设计原则?

答:
1.单一职责原则

通俗地讲就是一个类只作一件事

CALayer:动画和视图的显示。

UIView:只负责事件传递、事件响应。

2.开闭原则

对修改关闭,对扩展开放。 要考虑到后续的扩展性,而不是在原有的基础上来回修改

3.接口隔离原则

使用多个专门的协议、而不是一个庞大臃肿的协议,如 UITableviewDelegate + UITableViewDataSource

4.依赖倒置原则

抽象不该该依赖于具体实现、具体实现能够依赖于抽象。 调用接口感受不到内部是如何操做的

5.里氏替换原则

父类能够被子类无缝替换,且原有的功能不受任何影响 如:KVO

6.迪米特法则

一个对象应当对其余对象尽量少的了解,实现高聚合、低耦合

16.Objective-C与Swift的异同?

答:
1.一、swift和OC的共同点:

 - OC出现过的绝大多数概念,好比引用计数、ARC(自动引用计数)、属性、协议、接口、初始化、扩展类、命名参数、匿名函数等,在Swift中继续有效(可能最多换个术语)。

 - Swift和Objective-C共用一套运行时环境,Swift的类型能够桥接到Objective-C(下面我简称OC),反之亦然

1.二、swift的优势:

 - swift注重安全,OC注重灵活

 - swift注重面向协议编程、函数式编程、面向对象编程,OC注重面向对象编程

 - swift注重值类型,OC注重指针和引用

 - swift是静态类型语言,OC是动态类型语言

 - swift容易阅读,文件结构和大部分语法简易化,只有.swift文件,结尾不须要分号

 - swift中的可选类型,是用于全部数据类型,而不只仅局限于类。相比于OC中的nil更加安全和简明

 - swift中的泛型类型更加方便和通用,而非OC中只能为集合类型添加泛型

 - swift中各类方便快捷的高阶函数(函数式编程) (Swift的标准数组支持三个高阶函数:map,filter和reduce,以及map的扩展flatMap)

 - swift新增了两种权限,细化权限。open > public > internal(默认) > fileprivate > private

 - swift中独有的元组类型(tuples),把多个值组合成复合值。元组内的值能够是任何类型,并不要求是相同类型的。

17.沙盒目录结构是怎样的?各自用于那些场景?

答:
Application:存放程序源文件,上架前通过数字签名,上架后不可修改
Documents:经常使用目录,iCloud备份目录,存放数据
Library
Caches:存放体积大又不须要备份的数据
Preference:设置目录,iCloud会备份设置信息
tmp:存放临时文件,不会被备份,并且这个文件下的数据有可能随时被清除的可能

17.iOS中数据持久化方案有哪些?

答:
NSUserDefault 简单数据快速读写
Property list (属性列表)文件存储
Archiver (归档)
SQLite 本地数据库
CoreData(是iOS5以后才出现的一个框架,本质上是对SQLite的一个封装,它提供了对象-关系映射(ORM)的功能,即可以将OC对象转化成数据,保存在SQLite数据库文件中,也可以将保存在数据库中的数据还原成OC对象,经过CoreData管理应用程序的数据模型)

18.单个viewController的生命周期?

答:
- initWithCoder:(NSCoder *)aDecoder:(若是使用storyboard或者xib)
- loadView:加载view
- viewDidLoad:view加载完毕
- viewWillAppear:控制器的view将要显示
- viewWillLayoutSubviews:控制器的view将要布局子控件
- viewDidLayoutSubviews:控制器的view布局子控件完成  
- viewDidAppear:控制器的view彻底显示
- viewWillDisappear:控制器的view即将消失的时候
- viewDidDisappear:控制器的view彻底消失的时候
- dealloc 控制器销毁

19.cocoa 和 cocoa touch是什么?/cocoa touch底层技术架构?

答:
Cocoa包含Foundation和AppKit框架,可用于开发Mac OS X系统的应用程序。
Cocoa Touch包含Foundation和UIKit框架,可用于开发iPhone OS系统的应用程序。
Cocoa是 Mac OS X 的开发环境,Cocoa Touch是 iPhone OS的开发环境。
答:
cocoa touch底层技术架构 主要分为4层:

可触摸层 Cocoa Touch : UI组件,触摸事件和事件驱动,系统接口
媒体层 Media: 音视频播放,动画,2D和3D图形
Core Server: 核心服务层,底层特性,文件,网络,位置服务区等
Core OS: 内存管理,底层网络,硬盘管理

20. 如何选择delegate、notification、KVO?

答:
三种模式都是一个对象传递事件给另一个对象,而且不要他们有耦合。

delegate. 一对一
notification 一对多,多对多
KVO 一对一
三者各有本身的特色:

delegate 语法简洁,方便阅读,易于调试
notification 灵活多变,能够跨越多个类之间进行使用
KVO 实现属性监听,实现model和view同步
能够根据实际开发遇到的场景来使用不一样的方式

21.平时开发有没有玩过 Instrument ?

答:
Instruments里面工具不少,经常使用的有:
(1).Time Profiler:性能分析,用来检测应用CPU的使用状况.能够看到应用程序中各个方法正在消耗CPU时间。
(2).Zoombies:检查是否访问了僵尸对象,可是这个工具只能从上往下检查,不智能
(3).Allocations:用来检查内存,写算法的那批人也用这个来检查
(4).Leaks:检查内存,看是否有内存泄漏
(5).Core Animation:评估图形性能,这个选项检查了图片是否被缩放,以及像素是否对齐。被放缩的图片会被标记为黄色,像素不对齐则会标注为紫色。黄色、紫色越多,性能越差。

经常使用的排序算法

答:
选择排序、冒泡排序、插入排序三种排序算法能够总结为以下:

都将数组分为已排序部分和未排序部分。

选择排序将已排序部分定义在左端,而后选择未排序部分的最小元素和未排序部分的第一个元素交换。

冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。

插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。

/** 
 *    【选择排序】:最值出如今起始端
 *    
 *    第1趟:在n个数中找到最小(大)数与第一个数交换位置
 *    第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
 *    重复这样的操做...依次与第三个、第四个...数交换位置
 *    第n-1趟,最终可实现数据的升序(降序)排列。
 *
 */
void selectSort(int *arr, int length) {
    for (int i = 0; i < length - 1; i++) { //趟数
        for (int j = i + 1; j < length; j++) { //比较次数
            if (arr[i] > arr[j]) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

/** 
 *    【冒泡排序】:相邻元素两两比较,比较完一趟,最值出如今末尾
 *    第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n个元素位置
 *    第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n-1个元素位置
 *     ……   ……
 *    第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第2个元素位置    
 */
void bublleSort(int *arr, int length) {
    for(int i = 0; i < length - 1; i++) { //趟数
        for(int j = 0; j < length - i - 1; j++) { //比较次数
            if(arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        } 
    }
}

/**
 *    折半查找:优化查找时间(不用遍历所有数据)
 *
 *    折半查找的原理:
 *   1> 数组必须是有序的
 *   2> 必须已知min和max(知道范围)
 *   3> 动态计算mid的值,取出mid对应的值进行比较
 *   4> 若是mid对应的值大于要查找的值,那么max要变小为mid-1
 *   5> 若是mid对应的值小于要查找的值,那么min要变大为mid+1
 *
 */ 
// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 
int findKey(int *arr, int length, int key) {
    int min = 0, max = length - 1, mid;
    while (min <= max) {
        mid = (min + max) / 2; //计算中间值
        if (key > arr[mid]) {
            min = mid + 1;
        } else if (key < arr[mid]) {
            max = mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}

SDWebImage加载图片过程

答:
0、首先显示占位图

一、在webimagecache中寻找图片对应的缓存,它是以url为数据索引先在内存中查找是否有缓存;

二、若是没有缓存,就经过md5处理过的key来在磁盘中查找对应的数据,若是找到就会把磁盘中的数据加到内存中,并显示出来;

三、若是内存和磁盘中都没有找到,就会向远程服务器发送请求,开始下载图片;

四、下载完的图片加入缓存中,并写入到磁盘中;

五、整个获取图片的过程是在子线程中进行,在主线程中显示。

AFNetworking 底层原理分析

答:
AFNetworking是封装的NSURLSession的网络请求,由五个模块组成:分别由NSURLSession,Security,Reachability,Serialization,UIKit五部分组成

NSURLSession:网络通讯模块(核心模块) 对应 AFNetworking中的 AFURLSessionManager和对HTTP协议进行特化处理的AFHTTPSessionManager,AFHTTPSessionManager是继承于AFURLSessionmanager的

Security:网络通信安全策略模块  对应 AFSecurityPolicy

Reachability:网络状态监听模块 对应AFNetworkReachabilityManager

Seriaalization:网络通讯信息序列化、反序列化模块 对应 AFURLResponseSerialization

UIKit:对于iOS UIKit的扩展库

进阶题:

1.KVC的底层实现?

答:
当一个对象调用setValue方法时,方法内部会作如下操做:
1). 检查是否存在相应的key的set方法,若是存在,就调用set方法。
2). 若是set方法不存在,就会查找与key相同名称而且带下划线的成员变量,若是有,则直接给成员变量属性赋值。
3). 若是没有找到_key,就会查找相同名称的属性key,若是有就直接赋值。
4). 若是尚未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
这些方法的默认实现都是抛出异常,咱们能够根据须要重写它们。

2.KVO的底层实现?

答:

KVO-键值观察机制,原理以下:

1.当给A类添加KVO的时候,runtime动态的生成了一个子类NSKVONotifying_A,让A类的isa指针指向NSKVONotifying_A类,重写class方法,隐藏对象真实类信息

2.重写监听属性的setter方法,在setter方法内部调用了Foundation 的 _NSSetObjectValueAndNotify 函数

3._NSSetObjectValueAndNotify函数内部

a) 首先会调用 willChangeValueForKey

b) 而后给属性赋值

c) 最后调用 didChangeValueForKey

d) 最后调用 observer 的 observeValueForKeyPath 去告诉监听器属性值发生了改变 .

4.重写了dealloc作一些 KVO 内存释放

3.说一下工做中你怎么作性能优化的

答:通常都是说关于tableView的优化处理,

形成tableView卡顿的缘由
1.没有使用cell的重用标识符,致使一直建立新的cell
2.cell的从新布局
3.没有提早计算并缓存cell的属性及内容
4.cell中控件的数量过多
5.使用了ClearColor,无背景色,透明度为0
6.更新只使用tableView.reloadData()(若是只是更新某组的话,使用reloadSection进行局部更新)
7.加载网络数据,下载图片,没有使用异步加载,并缓存
8.使用addView 给cell动态添加view
9.没有按需加载cell(cell滚动很快时,只加载范围内的cell)
10.实现无用的代理方法(tableView只遵照两个协议)
11.没有作缓存行高(estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这二者同时存在才会出现“窜动”的bug。
建议是:只要是固定行高就写预估行高来减小行高调用次数提高性能。若是是动态行高就不要写预估方法了,用一个行高的缓存字典来减小代码的调用次数便可)
12.作了多余的绘制工做(在实现drawRect:的时候,它的rect参数就是须要绘制的区域,这个区域以外的不须要进行绘制)
13.没有预渲染图像。(当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,而后再绘制到屏幕)

提高tableView的流畅度
*本质上是下降 CPU、GPU 的工做,从这两个大的方面去提高性能。
  1.CPU:对象的建立和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制
  2.GPU:纹理的渲染

卡顿优化在 CPU 层面
1.尽可能用轻量级的对象,好比用不到事件处理的地方,能够考虑使用 CALayer 取代 UIView
2.不要频繁地调用 UIView 的相关属性,好比 frame、bounds、transform 等属性,尽可能减小没必要要的修改
3.尽可能提早计算好布局,在有须要时一次性调整对应的属性,不要屡次修改属性
4.Autolayout 会比直接设置 frame 消耗更多的 CPU 资源
5.图片的 size 最好恰好跟 UIImageView 的 size 保持一致
6.控制一下线程的最大并发数量
7.尽可能把耗时的操做放到子线程
8.文本处理(尺寸计算、绘制)
9.图片处理(解码、绘制)

卡顿优化在 GPU层面
1.尽可能避免短期内大量图片的显示,尽量将多张图片合成一张进行显示
2.GPU能处理的最大纹理尺寸是 4096x4096,一旦超过这个尺寸,就会占用 CPU 资源进行处理,因此纹理尽可能不要超过这个尺寸
3.尽可能减小视图数量和层次
4.减小透明的视图(alpha<1),不透明的就设置 opaque 为 YES
5.尽可能避免出现离屏渲染

iOS 保持界面流畅的技巧算法

5.Runtime实现的机制是什么?能作什么事情呢?

答:
runtime简称运行时。OC是运行时机制,也就是在运行时才作一些处理。例如:C语言在编译的时候就知道要调用哪一个方法函数,而OC在编译的时候并不知道要调用哪一个方法函数,只有在运行的时候才知道调用的方法函数名称,来找到对应的方法函数进行调用。

1.发送消息
【场景:方法调用】

2.交换方法实现(交换系统的方法)
【场景:当第三方框架或者系统原生方法功能不能知足咱们的时候,咱们能够在保持系统原有方法功能的基础上,添加额外的功能。】

3.动态添加方法
【场景:若是一个类方法很是多,加载类到内存的时候也比较耗费资源,须要给每一个方法生成映射表,能够使用动态给某个类,添加方法解决。】

4.利用关联对象(AssociatedObject)给分类添加属性
【
 场景:分类是不能自定义属性和变量的,这时候能够使用runtime动态添加属性方法;
 原理:给一个类声明属性,其实本质就是给这个类添加关联,并非直接把这个值的内存空间添加到类存空间。
 】

5.遍历类的全部成员变量
【
  1.NSCoding自动归档解档
  场景:若是一个模型有许多个属性,实现自定义模型数据持久化时,须要对每一个属性都实现一遍encodeObject 和 decodeObjectForKey方法,比较麻烦。咱们能够使用Runtime来解决。
  原理:用runtime提供的函数遍历Model自身全部属性,并对属性进行encode和decode操做。

  2.字典转模型
  原理:利用Runtime,遍历模型中全部属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。

  3.修改textfield的占位文字颜色
】

6.利用消息转发机制解决方法找不到的异常问题

教你深入理解Runtime机制数据库

Runtime在工做中的运用

Runtime运行机制

6.iOS图片设置圆角性能问题

答:
1.直接使用setCornerRadius
【这样设置会触发离屏渲染,比较消耗性能。好比当一个页面上有十几头像这样设置了圆角会明显感受到卡顿。
 注意:png图片UIImageView处理圆角是不会产生离屏渲染的。(ios9.0以后不会离屏渲染,ios9.0以前仍是会离屏渲染)
 】

 2.setCornerRadius设置圆角以后,shouldRasterize=YES光栅化
 【avatarImageView.layer.shouldRasterize = YES;
   avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale;  //UIImageView不加这句会产生一点模糊

  shouldRasterize=YES设置光栅化,能够使离屏渲染的结果缓存到内存中存为位图,
使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。

  可是若是layer及sublayers经常改变的话,它就会一直不停的渲染及删除缓存从新
建立缓存,因此这种状况下建议不要使用光栅化,这样也是比较损耗性能的。
 】

 3.直接覆盖一张中间为圆形透明的图片(推荐使用)

 4.UIImage drawInRect绘制圆角
【这种方式GPU损耗低内存占用大,并且UIButton上不知道怎么绘制,能够用
UIimageView添加个点击手势当作UIButton使用。】

5.SDWebImage处理图片时Core Graphics绘制圆角(暂时感受是最优方法)

iOS图片设置圆角性能问题

7.什么是 RunLoop?

答:
从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各类任务。
一个线程对应一个RunLoop,基本做用就是保持程序的持续运行,处理app中的各类事件。经过runloop,有事运行,没事就休息,能够节省cpu资源,提升程序性能。
主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个以下的main()函数
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
答:
RunLoop,是多线程的法宝,即一个线程一次只能执行一个任务,执行完任务后就会退出线程。主线程执行完即时任务时会继续等待接收事件而不退出。非主线程一般来讲就是为了执行某一任务的,执行完毕就须要归还资源,所以默认是不运行RunLoop的;

每个线程都有其对应的RunLoop,只是默认只有主线程的RunLoop是启动的,其它子线程的RunLoop默认是不启动的,若要启动则须要手动启动;

在一个单独的线程中,若是须要在处理完某个任务后不退出,继续等待接收事件,则须要启用RunLoop;

NSRunLoop提供了一个添加NSTimer的方法,能够指定Mode,若是要让任何状况下都回调,则须要设置Mode为Common模式;

实质上,对于子线程的runloop默认是不存在的,由于苹果采用了懒加载的方式。若是咱们没有手动调用[NSRunLoop currentRunLoop]的话,就不会去查询是否存在当前线程的RunLoop,也就不会去加载,更不会建立。

深刻理解RunLoop

8.以scheduledTimerWithTimeInterval的方式触发的timer,在滑动页面上的列表时,timer会暂停,为何?该如何解决?

答:
缘由在于滑动时当前线程的runloop切换了mode用于列表滑动,致使timer暂停。
runloop中的mode主要用来指定事件在runloop中的优先级,有如下几种:
* Default(NSDefaultRunLoopMode):默认,通常状况下使用;
* Connection(NSConnectionReplyMode):通常系统用来处理NSConnection相关事件,开发者通常用不到;
* Modal(NSModalPanelRunLoopMode):处理modal panels事件;
* Event Tracking(NSEventTrackingRunLoopMode):用于处理拖拽和用户交互的模式。
* Common(NSRunloopCommonModes):模式合集。默认包括Default,Modal,Event Tracking三大模式,能够处理几乎全部事件。
回到题中的情境。滑动列表时,runloop的mode由原来的Default模式切换到了Event Tracking模式,timer原来好好的运行在Default模式中,被关闭后天然就中止工做了。
解决方法其一是将timer加入到NSRunloopCommonModes中。其二是将timer放到另外一个线程中,而后开启另外一个线程的runloop,这样能够保证与主线程互不干扰,而如今主线程正在处理页面滑动。
方法1
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
方法2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop] run];
});

9.进程与线程

答:
进程:

1.进程是一个具备必定独立功能的程序关于某次数据集合的一次运行活动,它是操做系统分配资源的基本单元.

2.进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,咱们能够理解为手机上的一个app.

3.每一个进程之间是独立的,每一个进程均运行在其专用且受保护的内存空间内,拥有独立运行所需的所有资源

线程

1.程序执行流的最小单元,线程是进程中的一个实体.

2.一个进程要想执行任务,必须至少有一条线程.应用程序启动的时候,系统会默认开启一条线程,也就是主线程

进程和线程的关系

1.线程是进程的执行单元,进程的全部任务都在线程中执行

2.线程是 CPU 分配资源和调度的最小单位

3.一个程序能够对应多个进程(多进程),一个进程中可有多个线程,但至少要有一条线程

4.同一个进程内的线程共享进程资源

10.iOS中实现多线程的几种方案,各自有什么特色?讲一下具体使用场景/在项目何时选择使用 GCD,何时选 择 NSOperation?

答:
NSThread 面向对象的,须要程序员手动建立线程,但不须要手动销毁。子线程间通讯很难。

GCD c语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。

NSOperation 基于gcd封装,更加面向对象,比gcd多了一些功能。

【场景:1.多个网络请求完成后执行下一步 2.多个网络请求顺序执行后执行下一步 3.异步操做两组数据时, 执行完第一组以后, 才能执行第二组】
答:
项目中使用 NSOperation 的优势是 NSOperation 是对线程的高度抽象,在项目中使 用它,会使项目的程序结构更好,子类化 NSOperation 的设计思路,是具备面向对 象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中 使用。

项目中使用 GCD 的优势是 GCD 自己很是简单、易用,对于不复杂的多线程操 做,会节省代码量,而 Block 参数的使用,会是代码更为易读,建议在简单项目中 使用。

11.什么是GCD?GCD 的队列类型?

答:
GCD(Grand Central Dispatch), 又叫作大中央调度, 它对线程操做进行了封装,加入了不少新的特性,内部进行了效率优化,提供了简洁的C语言接口, 使用更加高效,也是苹果推荐的使用方式.

GCD的队列能够分为2大类型

1.并发队列(Concurrent Dispatch Queue)
可让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效

2.串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务),按照FIFO顺序执行.

12.什么是同步和异步任务派发(synchronous和asynchronous)?

答:
GCD多线程常常会使用 dispatch_sync和dispatch_async函数向指定队列添加任务,分别是同步和异步

同步:指阻塞当前线程,既要等待添加的耗时任务块Block完成后,函数才能返回,后面的代码才能继续执行
异步:指将任务添加到队列后,函数当即返回,后面的代码不用等待添加的任务完成后便可执行,异步提交没法肯定任务执行顺序

13.dispatch_barrier_(a)sync使用?

答:
栅栏函数

一个dispatch barrier 容许在一个并发队列中建立一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待全部在barrier以前提交的blocks执行结束。 这时,barrier block本身开始执行。 以后, 队列继续正常的执行操做。

14.对称加密和非对称加密的区别?

答:
一、对称加密又称公开密钥加密,加密和解密都会用到同一个密钥,若是密钥被攻击者得到,此时加密就失去了意义。常见的对称加密算法有DES、3DES、AES、Blowfish、IDEA、RC五、RC6。

二、非对称加密又称共享密钥加密,使用一对非对称的密钥,一把叫作私有密钥,另外一把叫作公有密钥;公钥加密只能用私钥来解密,私钥加密只能用公钥来解密。常见的公钥加密算法有:RSA、ElGamal、背包算法、Rabin(RSA的特例)、迪菲-赫尔曼密钥交换协议中的公钥加密算法、椭圆曲线加密算法)。

15.组件化有什么好处?

答:
业务分层、解耦,使代码变得可维护;

有效的拆分、组织日益庞大的工程代码,使工程目录变得可维护;

便于各业务功能拆分、抽离,实现真正的功能复用;

业务隔离,跨团队开发代码控制和版本风险控制的实现;

模块化对代码的封装性、合理性都有必定的要求,提高开发同窗的设计能力;

在维护好各级组件的状况下,随意组合知足不一样客户需求;(只须要将以前的多个业务组件模块在新的主App中进行组装便可快速迭代出下一个全新App)

16.你是如何组件化解耦的?

答:
分层

基础功能组件:按功能分库,不涉及产品业务需求,跟库Library相似,经过良好的接口拱上层业务组件调用;不写入产品定制逻辑,经过扩展接口完成定制;

基础UI组件:各个业务模块依赖使用,但须要保持好定制扩展的设计

业务组件:业务功能间相对独立,相互间没有Model共享的依赖;业务之间的页面调用只能经过UIBus进行跳转;业务之间的逻辑Action调用只能经过服务提供;

中间件:target-action,url-block,protocol-class

iOS组件化方案的几种实现

17.APP启动时间应从哪些方面优化?

答:
App启动时间能够经过xcode提供的工具来度量,在Xcode的Product->Scheme-->Edit Scheme->Run->Auguments中,将环境变量DYLD_PRINT_STATISTICS设为YES,优化需如下方面入手

dylib loading time

核心思想是减小dylibs的引用

合并现有的dylibs(最好是6个之内)

使用静态库

rebase/binding time

核心思想是减小DATA块内的指针

减小Object C元数据量,减小Objc类数量,减小实例变量和函数(与面向对象设计思想冲突)

减小c++虚函数

多使用Swift结构体(推荐使用swift)

ObjC setup time

核心思想同上,这部份内容基本上在上一阶段优化事后就不会太过耗时

initializer time

使用initialize替代load方法

减小使用c/c++的attribute((constructor));推荐使用dispatch_once() pthread_once() std:once()等方法

推荐使用swift

不要在初始化中调用dlopen()方法,由于加载过程是单线程,无锁,若是调用dlopen则会变成多线程,会开启锁的消耗,同时有可能死锁

不要在初始化中建立线程

iOS App 启动过程(一):基础概念

iOS App 启动过程(二):从 exec() 到 main()

iOS App 启动过程(三):main() 及生命周期

Swift系列面试题总结

基础题:

1.类(class) 和 结构体(struct) 有什么区别? 类(class) 和 结构体(struct) 比较,优缺点?

答:
struct是值类型,class是引用类型。

值类型的变量直接包含它们的数据,对于值类型都有它们本身的数据副本,所以对一个变量操做不可能影响另外一个变量。
引用类型的变量存储对他们的数据引用,所以后者称为对象,所以对一个变量操做可能影响另外一个变量所引用的对象。

两者的本质区别:struct是深拷贝,拷贝的是内容;class是浅拷贝,拷贝的是指针。

property的初始化不一样:class 在初始化时不能直接把 property 放在 默认的constructor 的参数里,而是须要本身建立一个带参数的constructor;而struct能够,把属性放在默认的constructor 的参数里。
变量赋值方式不一样:struct是值拷贝;class是引用拷贝。
immutable变量:swift的可变内容和不可变内容用var和let来甄别,若是初始为let的变量再去修改会发生编译错误。struct遵循这一特性;class不存在这样的问题。
mutating function: struct 和 class 的差別是 struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。
继承: struct不能够继承,class能够继承。
struct比class更轻量:struct分配在栈中,class分配在堆中。
答:
class 有如下功能,struct 是没有的:

class能够继承,子类能够使用父类的特性和方法
类型转换能够在运行时检查和解释一个实例对象
class能够用 deinit来释放资源
一个类能够被屡次引用
struct 优点:

结构较小,适用于复制操做,相比较一个class 实例被屡次引用,struct 更安全
无需担忧内存泄露问题

2.Swift 是面向对象仍是函数式的编程语言?

答:
Swift 既是面向对象的,又是函数式的编程语言。
说 Swift 是面向对象的语言,是由于 Swift 支持类的封装、继承、和多态,从这点上来看与 Java 这类纯面向对象的语言几乎毫无差异。
说 Swift 是函数式编程语言,是由于 Swift 支持 map, reduce, filter, flatmap 这类去除中间状态、数学函数式的方法,更增强调运算结果而不是中间过程。

3.什么是泛型,swift哪些地方使用了泛型?

答:
泛型(generic)能够使咱们在程序代码中定义一些可变的部分,在运行的时候指定。使用泛型能够最大限度地重用代码、保护类型的安全以及提升性能。
泛型能够将类型参数化,提升代码复用率,减小代码量。

例如 optional 中的 map、flatMap 、?? (泛型加逃逸闭包的方式,作三目运算)

Swift 泛型

4.swift 语法糖 ? !的本质(实现原理)

答:
?为optional的语法糖
optional<T> 是一个包含了nil 和普通类型的枚举,确保使用者在变量为nil的状况下处理

!为optional 强制解包的语法糖

5.什么是可选型(Optional),Optional(可选型) 是用什么实现的

答:
1.在 Swift 中,可选型是为了表达一个变量为空的状况,当一个变量为空,他的值就是 nil
在类型名称后面加个问号? 来定义一个可选型
值类型或者引用类型均可以是可选型变量

2.Optional 是一个泛型枚举
大体定义以下:

enum Optional<Wrapped> {
  case none
  case some(Wrapped)
}

除了使用 let someValue: Int? = nil 以外, 还能够使用let optional1: Optional<Int> = nil 来定义

6.什么是高阶函数

答:
一个函数若是能够以某一个函数做为参数, 或者是返回值, 那么这个函数就称之为高阶函数, 如 map, reduce, filter

7.如何解决引用循环

答:
转换为值类型, 只有类会存在引用循环, 因此若是能不用类, 是能够解引用循环的,
delegate 使用 weak 属性.
闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned, 修饰

8.定义静态方法时关键字 static 和 class 有什么区别

答:
static 定义的方法不能够被子类继承, class 则能够

class AnotherClass {
    static func staticMethod(){}
    class func classMethod(){}
}
class ChildOfAnotherClass: AnotherClass {
    override class func classMethod(){}
    //override static func staticMethod(){}// error
}

9.请说明并比较如下关键词:Open, Public, Internal, File-private, Private

答:
Swift 有五个级别的访问控制权限,从高到底依次为好比 Open, Public, Internal, File-private, Private。

他们遵循的基本原则是:高级别的变量不容许被定义为低级别变量的成员变量。好比一个 private 的 class 中不能含有 public 的 String。反之,低级别的变量却能够定义在高级别的变量中。好比 public 的 class 中能够含有 private 的 Int。

Open 具有最高的访问权限。其修饰的类和方法能够在任意 Module 中被访问和重写;它是 Swift 3 中新添加的访问权限。
Public 的权限仅次于 Open。与 Open 惟一的区别在于它修饰的对象能够在任意 Module 中被访问,但不能重写。
Internal 是默认的权限。它表示只能在当前定义的 Module 中访问和重写,它能够被一个 Module 中的多个文件访问,但不能够被其余的 Module 中被访问。
File-private 也是 Swift 3 新添加的权限。其被修饰的对象只能在当前文件中被使用。例如它能够被一个文件中的 class,extension,struct 共同使用。
Private 是最低的访问权限。它的对象只能在定义的做用域内使用。离开了这个做用域,即便是同一个文件中的其余做用域,也没法访问。

10.swift中,关键字 guard 和 defer 的用法 guard也是基于一个表达式的布尔值去判断一段代码是否该被执行。与if语句不一样的是,guard只有在条件不知足的时候才会执行这段代码。

答:
guard let name = self.text else {  return }
defer的用法是,这条语句并不会立刻执行,而是被推入栈中,直到函数结束时才再次被调用。

defer {
   //函数结束才调用
}

11.关键字:Strong,Weak,Unowned 区别?

答:
Swift 的内存管理机制同OC一致,都是ARC管理机制; Strong,和 Weak用法同OC同样

Unowned(无主引用), 不会产生强引用,实例销毁后仍然存储着实例的内存地址(相似于OC中的unsafe_unretained), 试图在实例销毁后访问无主引用,会产生运行时错误(野指针)

12.如何理解copy-on-write?

答:
值类型(好比:struct),在复制时,复制对象与原对象实际上在内存中指向同一个对象,当且仅当修改复制的对象时,才会在内存中建立一个新的对象,

为了提高性能,Struct, String、Array、Dictionary、Set采起了Copy On Write的技术
好比仅当有“写”操做时,才会真正执行拷贝操做
对于标准库值类型的赋值操做,Swift 能确保最佳性能,全部不必为了保证最佳性能来避免赋值

13.什么是属性观察?

答:
属性观察是指在当前类型内对特性属性进行监测,并做出响应,属性观察是 swift 中的特性,具备2种, willset 和 didset

var title: String {
    willSet {
        print("willSet", newValue)

    }
    didSet {
        print("didSet", oldValue, title)
    }
}

willSet会传递新值,默认叫newValue
didSet会传递旧值,默认叫oldValue
在初始化器中设置属性值不会触发willSet和didSet

14.swift 为何将 String,Array,Dictionary设计为值类型?

答:
值类型和引用类型相比,最大优点能够高效的使用内存,值类型在栈上操做,引用类型在堆上操做,栈上操做仅仅是单个指针的移动,而堆上操做牵涉到合并,位移,重连接,Swift 这样设计减小了堆上内存分配和回收次数,使用 copy-on-write将值传递与复制开销降到最低

15..如何将Swift 中的协议(protocol)中的部分方法设计为可选(optional)?

答:
1.在协议和方法前面添加 @objc,而后在方法前面添加 optional关键字,改方式其实是将协议转为了OC的方式
@objc protocol someProtocol {
  @objc  optional func test()
}

2.使用扩展(extension),来规定可选方法,在 swift 中,协议扩展能够定义部分方法的默认实现

protocol someProtocol {
    func test()
}

extension someProtocol{
    func test() {
        print("test")
    }
}

16.比较Swift 和OC中的初始化方法 (init) 有什么不一样?

答:
swift 的初始化方法,更加严格和准确, swift初始化方法须要保证全部的非optional的成员变量都完成初始化, 同时 swfit 新增了convenience和 required两个修饰初始化器的关键字

convenience只提供一种方便的初始化器,必须经过一个指定初始化器来完成初始化
required是强制子类重写父类中所修饰的初始化方法

17.比较 Swift和OC中的 protocol 有什么不一样?

答:
Swift 和OC中的 protocol相同点在于: 二者均可以被用做代理;
不一样点: Swift中的 protocol还能够对接口进行抽象,能够实现面向协议,从而大大提升编程效率,Swift中的protocol能够用于值类型,结构体,枚举;

18.swift 和OC 中的自省 有什么区别?

答:
自省在OC中就是判断某一对象是否属于某一个类的操做,有如下2中方式

[obj iskinOfClass:[SomeClass class]]
[obj isMemberOfClass:[SomeClass class]]

在 Swift 中因为不少 class 并不是继承自 NSObject, 故而 Swift 使用 is 来判断是否属于某一类型, is 不只能够做用于class, 仍是做用于enum和struct

19.什么是函数重载? swift 支不支持函数重载?

答:
函数重载是指: 函数名称相同,函数的参数个数不一样, 或者参数类型不一样,或参数标签不一样, 返回值类型与函数重载无关

swift 支持函数重载

20.swift 中的枚举,关联值 和 原始值的区分?

答:
1.关联值--有时会将枚举的成员值跟其余类型的变量关联存储在一块儿,会很是有用
// 关联值
enum Date {
  case digit(year: Int, month: Int, day: Int)
  case string(String)
}

2.原始值--枚举成员能够使用相同类型的默认值预先关联,这个默认值叫作:原始值
// 原始值
enum Grade: String {
  case perfect = "A"
  case great = "B"
  case good = "C"
  case bad = "D"
}

21.swift 中的闭包结构是什么样子的?什么是尾随闭包?什么是逃逸闭包?什么是自动闭包?

答:
1.
{
    (参数列表) -> 返回值类型 in 函数体代码
}
答:
2.将一个很长的闭包表达式做为函数的最后一个实参
  使用尾随闭包能够加强函数的可读性
  尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式

// fn 就是一个尾随闭包参数
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}

// 调用
exec(v1: 10, v2: 20) {
    $0 + $1
}
答:
3.当闭包做为一个实际参数传递给一个函数或者变量的时候,咱们就说这个闭包逃逸了,能够在形式参数前写 @escaping 来明确闭包是容许逃逸的。

非逃逸闭包、逃逸闭包,通常都是当作参数传递给函数
非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数做用域内
逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的做用域,须要经过@escaping声明

// 定义一个数组用于存储闭包类型
var completionHandlers: [() -> Void] = []

//  在方法中将闭包当作实际参数,存储到外部变量中
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
答:
4.自动闭包是一种自动建立的用来把做为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,而且当它被调用时,它会返回内部打包的表达式的值。这个语法的好处在于经过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。

func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)

为了不与指望冲突,使用了@autoclosure的地方最好明确注释清楚:这个值会被推迟执行
@autoclosure 会自动将 20 封装成闭包 { 20 }
@autoclosure 只支持 () -> T 格式的参数
@autoclosure 并不是只支持最后1个参数
有@autoclosure、无@autoclosure,构成了函数重载
若是你想要自动闭包容许逃逸,就同时使用 @autoclosure 和 @escaping 标志。

22. swift中, 存储属性和计算属性的区别?

答:
Swift中跟实例对象相关的属性能够分为2大类

存储属性(Stored Property)

相似于成员变量这个概念
存储在实例对象的内存中
结构体、类能够定义存储属性
枚举不能够定义存储属性
计算属性(Computed Property)

本质就是方法(函数)
不占用实例对象的内存
枚举、结构体、类均可以定义计算属性

struct Circle {
    // 存储属性
    var radius: Double
    // 计算属性
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            return radius * 2
        }
    }
}

23.什么是延迟存储属性(Lazy Stored Property)?

答:
使用lazy能够定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化(相似OC中的懒加载)

lazy属性必须是var,不能是let
 let必须在实例对象的初始化方法完成以前就拥有值
若是多条线程同时第一次访问lazy属性
 没法保证属性只被初始化1次

 class PhotoView {
    // 延迟存储属性
    lazy var image: Image = {
        let url = "https://...x.png"        
        let data = Data(url: url)
        return Image(data: data)
    }() 
}

24.swift 中如何使用单例模式?

答:
能够经过类型属性+let+private 来写单例; 代码以下以下:

 public class FileManager {
    public static let shared = {
        // ....
        // ....
        return FileManager()
}()
    private init() { }
}

25.swift 中的下标是什么?

答:
使用subscript能够给任意类型(枚举、结构体、类)增长下标功能,有些地方也翻译为:下标脚本
subscript的语法相似于实例方法、计算属性,本质就是方法(函数)

使用以下:
class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue }
        }
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}

var p = Point()
// 下标赋值
p[0] = 11.1
p[1] = 22.2
// 下标访问
print(p.x) // 11.1
print(p.y) // 22.2

26.简要说明Swift中的初始化器?

答:
类、结构体、枚举均可以定义初始化器
类有2种初始化器: 指定初始化器(designated initializer)、便捷初始化器(convenience initializer)

// 指定初始化器 
init(parameters) {
    statements 
}
// 便捷初始化器
convenience init(parameters) {
    statements 
} 

规则:

每一个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
默认初始化器老是类的指定初始化器
类偏向于少许指定初始化器,一个类一般只有一个指定初始化器
初始化器的相互调用规则

指定初始化器必须从它的直系父类调用指定初始化器
便捷初始化器必须从相同的类里调用另外一个初始化器
便捷初始化器最终必须调用一个指定初始化器

27.什么可选链?

答:
可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil 。若是可选项包含值,属性、方法或者下标的调用成功;若是可选项是 nil ,属性、方法或者下标的调用会返回 nil 。多个查询能够连接在一块儿,若是链中任何一个节点是 nil ,那么整个链就会得体地失败。

多个?能够连接在一块儿
若是链中任何一个节点是nil,那么整个链就会调用失败

28.什么是运算符重载(Operator Overload)?

答:
类、结构体、枚举能够为现有的运算符提供自定义的实现,这个操做叫作:运算符重载

struct Point {
    var x: Int
    var y: Int

    // 重载运算符
    static func + (p1: Point, p2: Point) -> Point   {
        return Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
}

var p1 = Point(x: 10, y: 10)
var p2 = Point(x: 20, y: 20)
var p3 = p1 + p2

参考资料

iOS学习笔记

iOS面试题整理

Swift 教程

iOS | 面试知识整理

推荐

若是须要面试资料的请进群领取,iOS开发交流学习群

收录:原文地址

相关文章
相关标签/搜索