iOS

2020年春季iOS面试总结

2020-08-04  本文已影响0人  iOS开发面试题技术合集

原文地址

1、内存管理

iOS中的内管理主要是通过引用计数来管理内存的。分为自动管理(ARC)和手动管理(MRC),以及autorelease。管理的的对象主要是值类型(int、float、struct等基本数据类型)和引用类型(即继承自NSObject类的所有的OC对象)。

那么MRC、ARC和autorelease的区别有哪些呢?MRC为手动引用计数,ARC为自动引用计数,autorelease则是添加到自动释放池中。

1. 1. ARC和MRC的区别:ARC相对于MRC,不需要手动书写retain/release/autorelease,而是在编译期和运行期这两部分帮助开发者管理内存。

在编译器的时候,ARC调用C接口实现的retain/release/autorelease,在运行期的时候使用runtime配合来实现内存管理。

1. 2. autorelease分为两种情况:手动干预释放时机、系统自动释放。

手动干预释放机制:指定autoreleasepool,就是所谓的作用域大括号结束释放;

系统自动释放:不手动指定autoreleasepool。

autorelease对象除了作用域后,会被添加到最近一次创建的自动释放池中,并会在当前的runloop迭代结束时释放。

ps:runloop从程序启动到加载完成是一个完整的运行循环,然后会停下来,等待用户交互,用户的每一次交互都会启动一次运行循环,这时候会创建自动释放池,来处理用户所有的点击事件、触摸事件,在一次完整的运行循环结束之前,会销毁自动释放池,达到销毁对象的目的。

1. 3. retain、release、autorelease

1.3. 1. retain是调用了sidetable_retain方法,retain通过Sidetable这个数据结构来存储引用计数,Sidetable存储了一个自旋锁,一个引用计数,这个引用计数的以对象的地址作为key,引用计数作为value,到这里,引用计数的底层已经清楚了。

1.3. 2. release的通过查找对象,对引用计数减1,如果引用计数小于阈值,则调用SEL_dealloc

1.3. 3. autorelease方法的作用是把对象放到autorelease pool中,到pool drain的时候,回释放池中的对象。放到自动释放池中的对象在超出作用域后会立即释放。事实上在iOS 程序启动之后,主线程会启动一个Runloop,这个Runloop在每一次循环是被自动释放池包裹的,在合适的时候对池子进行清空。autorelease方法会把对象存储到AutoreleasePoolPage的链表里。等到auto release pool被释放的时候,把链表内存储的对象删除。所以,AutoreleasePoolPage就是自动释放池的内部实现。

1. 4. 与内存有关的修饰符

1. 5. 在ARC的项目中,对MRC的文件可以添加编译选项-fno-objc-arc的标识;在MRC的项目中,对ARC的文件可以添加编译选项 -fobjc-arc的标识。把MRC文件转为ARC,实际上是去掉文件中的retain、release。

2、KVC

KVC也就是key-value-coding,即键值编码,通常是用来给某一个对象的属性进行赋值。既然可以通过kvc赋值,同样的也可以通过它进行取值。

2.1、最常见的两种用法就是:a、对私有变量进行赋值;b、字典转模型。**

2.2、需要注意:a、字典转模型的时候,字典中的某一个key一定要在模型中有对应的属性;b、如果一个模型中包含了另外的模型对象,是不能直接转化成功的;c、通过kvc转化模型中的模型,也是不能直接转化成功的。

3、KVO

KVO,即key-value-observing,利用一个key来找到某个属性并监听其值得改变。其实这也是一种典型的观察者模式。

3.1、简单的说,kvo的用法非常简单:a、添加观察者;b、在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:(通过查阅文档可以知道,绝大多数对象都有这个方法,因为这个方法属于NSObject);c、移除观察者。

3.2、KVO的底层实现

当一个类的属性被观察的时候,系统会通过runtime动态的创建一个该类的派生类,并且会在这个类中重写基类被观察的属性的setter方法,而且系统将这个类的isa指针指向了派生类,从而实现了给监听的属性赋值时调用的是派生类的setter方法。重写的setter方法会在调用原setter方法前后,通知观察对象值得改变。

3.3、总结一下:1.KVO KVC 没联系;2.KVO 是监听属性值的改变;3.KVO 底层实现原理是系统给当前类创建子类 , 在子类 setter 方法调用父类的 setter 方法。

4、RunTime

Objective-C是基于C语言加入了面向对象特性消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发。

在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方法的代码,而是向receiver发送一条message消息,这条消息可能由receiver来处理,也可能由转发给其他对象来处理,也有可能假装没有接收到这条消息而没有处理。其实[receiver message]被编译器转化为:idobjc_msgSend (idself, SEL op, ... );可以从两个数据结构id和SEL来逐步分析和理解Runtime有哪些重要的数据结构。

主要作用:a、获取一个类的全部成员变量就算是私有成员变量也可以获取的到;b、我们可以获取到一个类的全部属性名;c、获取一个类的全部方法;d、获取一个类中遵守的全部协议;e、归档/解档;f、交换两个方法的实现,拦截系统自带的方法调用功能。

5、RunLoop

RunLoop 就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用 RunLoop 的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。 runloop 的设计是为了减少 cpu 无谓的空转。

5.1、runloop保证线程的长久存活

    使用多线程时,一个子线程当它的任务执行完毕之后都会销毁,所以每次执行异步任务都会频繁去创建和销毁线程,这样无疑是耗费资源的。这种情况下我们可以利用runloop来保证线程在执行完任务后不背销毁而进入“休眠”状态,等待下一个任务的执行再被唤醒。

5.2、保证NSTimer正常运转。

5.3、滚动视图流畅性优化

    在我们的开发过程中经常遇到列表型上面有图片的,一般下载图片用异步,setimage则使用同步。为imageView设置image,是在UITrackingRunLoopMode中也可以进行的,如果图片很大,图片解压缩和渲染肯定会很耗时,那么卡顿就是必然的。我们可以再setImage的时候手动设置runloop的mode来解决。

5.4、监测iOS卡顿

在写代码之前我们首先要了解几个原理,dispatch_semaphore_t这个类,dispatch_semaphore_t 是一个信号量机制,信号量到达、或者 超时会继续向下进行,否则等待,如果超时则返回的结果必定不为0,信号量到达结果为0。在就是根据CFRunLoopActivity的几个状态以及Runloop执行的顺序过程、原理我们可以通过监听mainRunloop的状态和信号量阻塞线程的特点来检测卡顿了。

6、NSThread、GCD、NSOperation多线程

6.1、NSThread是封装程度最小最轻量级的,使用更灵活,但要手动管理线程的生命周期、线程同步和线程加锁等,开销较大;

6.2、GCD基于C语言封装的,遵循FIFO;

6.3、NSOperation基于GCD封装的,比GCD可控性更强;可以加入操作依赖(addDependency)、设置操作队列最大可并发执行的操作个数(setMaxConcurrentOperationCount)、取消操作(cancel)等,需要使用两个它的实体子类:NSBlockOperation和NSInvocationOperation,或者继承NSOperation自定义子类;NSBlockOperation和NSInvocationOperation用法的主要区别是:前者执行指定的方法,后者执行代码块,相对来说后者更加灵活易用。NSOperation操作配置完成后便可调用start函数在当前线程执行,如果要异步执行避免阻塞当前线程则可以加入NSOperationQueue中异步执行。

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

7.iOS设计模式

7.1、适配器模式;

7.2、策略模式;

7.3、观察者模式;

7.4、原型/外观模式;

7.4.3、原型模式:

当一个系统应该独立于它的产品创建,构成和表示时。

当要实例化的类是在运行时刻指定时,例如,通过动态装载。

为了避免创建一个与产品类层次平行的工厂类层次时。

当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

7.4.4、外观模式:

客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。

定义系统的入口。

7.5.5、:原型/外观模式的优缺点?

必须实现 Cloneable 接口。

逃避构造函数的约束。

7.5、工厂模式;

7.6、桥接模式;

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

实现系统可能有多个角度分类,每一种角度都可能变化。

把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。

7.7、代理模式;

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

想在访问一个类时做一些控制。

实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

7.8、单例模式;

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

当您想控制实例数目,节省系统资源的时候。

7.9、备忘录模式;

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。

很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。

7.10、生成器模式;

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

一些基本部件不会变,而其组合经常变化的时候。

7.11、命令模式;

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

8、简述weak的实现原理

8.1.1、weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil;

8.1.2、weak是有Runtime维护的weak表;

8.1.3、weak释放为nil过程

weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程:

8.2.1、调用objc_release

8.2.2、因为对象的引用计数为0,所以执行dealloc

8.2.3、在dealloc中,调用了_objc_rootDealloc函数

8.2.4、在_objc_rootDealloc中,调用了object_dispose函数

8.2.5、调用objc_destructInstance

8.2.6、最后调用objc_clear_deallocating。

对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组。

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

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

9、音视频相关

采集视频,音频–》使用iOS原生框架 AVFoundation.framework

视频滤镜处理–》使用iOS原生框架 CoreImage.framework;使用第三方框架 GPUImage.framework

9.1、CoreImage 与 GPUImage 框架比较:

在实际项目开发中,开发者更加倾向使用于GPUImage框架.

首先它在使用性能上与iOS提供的原生框架,并没有差别;其次它的使用便利性高于iOS原生框架,最后也是最重要的GPUImage框架是开源的.而大家如果想要学习GPUImage框架,建议学习OpenGL ES,其实GPUImage的封装和思维都是基于OpenGL ES.

9.2、视频\音频编码压缩

9.3、推流

9.4、拉流

9.5、音视频解码

9.6、播放

10、对称加密和非对称加密

10.1、对称密钥加密是指加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方;

为什么叫对称加密?一方通过密钥将信息加密后,把密文传给另一方,另一方通过这个相同的密钥将密文解密,转换成可以理解的明文。他们之间的关系如下:明文<->密钥<->密文

10.2、非对称加密是指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。

非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

10.3、由于非对称加密的方式不需要发送用来解密的私钥,所以可以保证安全性;但是和对称加密比起来,它非常的慢,所以我们还是要用对称加密来传送消息,但对称加密所使用的密钥我们可以通过非对称加密的方式发送出去。但是此时交换的两个公钥不一定正确。

11、证书机制

11.1、作为服务端的小红,首先把自己的公钥发给证书颁发机构,向证书颁发机构申请证书。

11.2、证书颁发机构自己也有一对公钥私钥。机构利用自己的私钥来加密Key1,并且通过服务端网址等信息生成一个证书签名,证书签名同样经过机构的私钥加密。证书制作完成后,机构把证书发送给了服务端小红。

11.3、当小灰向小红请求通信的时候,小红不再直接返回自己的公钥,而是把自己申请的证书返回给小灰。

11.4、小灰收到证书以后,要做的第一件事情是验证证书的真伪。需要说明的是,各大浏览器和操作系统已经维护了所有权威证书机构的名称和公钥。所以小灰只需要知道是哪个机构颁布的证书,就可以从本地找到对应的机构公钥,解密出证书签名。

12、Http和Https的区别

Http协议运行在TCP之上,明文传输,客户端与服务器端都无法验证对方的身份;Https是身披SSL(Secure Socket Layer)外壳的Http,运行于SSL上,SSL运行于TCP之上,是添加了加密和认证机制的HTTP。二者之间存在如下不同:

13、iOS中的图形绘制

13.1、iOS支持OpenGL ES和Quartz/UIKit/CoreAnimation绘制接口。UIKit绘制必须在主线程中完成。

13.2、Quartz支持基于路径的绘制,反走样,填充,图像,上色,坐标变换,pdf绘制显示解析等功能。

13.3、UIKit支持线条绘制、图像和颜色操作。

13.4、Core Animation支持动画绘制。

13.5、View的使用DrawRect绘制,以下行为会触发:

13.6、UIKit左上角为原点,右下角为终点。CoreAnimation坐下角为原点,右上角为终点。使用CGContextRotateCTM、 CGContextScaleCTM、CGContextTranslateCTM来变换矩阵,或者直接使用CGAffineTransform设置变换 矩阵。

13.7、CGContext绘制上下文,对于Bitmap和PDF,可以创建不同的context类型。

13.8、使用UIGraphicsGetCurrentContext来获取当前的CGContext。

13.9、UIGraphicsBeginImageContextWithOptions和UIGraphicsEndImageContext用来包含图像绘制的代码。

 UIGraphicsBeginPDFContextToFile(ToData)和UIGraphicsEndPDFContext用来包含PDF绘制的代码。

13.10、Path绘制,即向量绘制。推荐使用UIBezierPath,其次是CGPath。

13.11、翻转屏幕变换:

CGContextTranslateCTM(graphicsContext, 0.0, drawingRect.size.height);

CGContextScaleCTM(graphicsContext, 1.0, -1.0);

13.12、Point通常等于Pixel,但是可以指定一个Point对应多个Pixel。

13.13、使用UIColor坐颜色空间变换。

13.14、绘制性能:

14、iOS中的动画

Core Animation 流水线的详细过程如下:

首先,由 app 处理事件(Handle Events),如:用户的点击操作,在此过程中 app 可能需要更新视图树,相应地,图层树也会被更新。

其次,app 通过 CPU 完成对显示内容的计算,如:视图的创建、布局计算、图片解码、文本绘制等。在完成对显示内容的计算之后,app 对图层进行打包,并在下一次 RunLoop 时将其发送至 Render Server,即完成了一次 Commit Transaction 操作。

Render Server 主要执行 Open GL、Core Graphics 相关程序,并调用 GPU

GPU 则在物理层上完成了对图像的渲染。

最终,GPU 通过 Frame Buffer、视频控制器等相关部件,将图像显示在屏幕上。

参考链接:https://www.cnblogs.com/feng9exe/p/10912767.html

15、iOS中的直播开发

iOS客户端要实现直播功能,需要经过流程为:

  采集-处理-编码-封包-推流-播放

15.1、采集:

视频通过摄像头进行采集,需要用到iOS底层库AVCaptureSession,音频通过麦克风进行采集,需要用到底层库AudioMedia

15.2、处理:

对视频加美颜、水印,主要使用openGL来实现,GPUImage是一个基于openGL一个强大的图像/视频处理框架,封装好了各种滤镜同时也可以编写自定义的滤镜,其本身内置了多达120多种常见的滤镜效果。

15.3、编码:

不管是视频,还是音频,最好使用硬编,可以减少手机的发热量,效率也比较高。视频编码可以使用VideoToolbox,苹果自带的视频硬解码和硬编码API,但是在iOS8之后才开放。音频采用AudioToolbox ,苹果自带的音频硬解码和硬编码API。

15.4、封包和推流:

使用CDN即分发网络,将视频和音频推送到观众端,可以看到直播内容。虽然分发网络和iOS客户端没有关系,但是作为直播app,还是有优化空间的,比如动态码率优化,码率很高的话,需要的流量会很多,要求网速快,当网速很慢的情况下,需要对画面的质量或者说分辨率进行取舍,让码率降下来,以适应网络环境差的情况。

15.5、播放:

播放器可以做一些优化,当网络卡的情况下,主播这边还是在不断地推流,播放器将推流的信息缓存下来,进行播放,卡得越频繁,造成得延迟就越大,所以要做累计延迟优化,也就是在缓存很多信息时,可以选择性地将中间一些东西丢掉,因为对于直播而言,实时性是很重要得。

资料推荐

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

上一篇 下一篇

猜你喜欢

热点阅读