美文网首页
iOS相关知识(一)-- 大杂烩

iOS相关知识(一)-- 大杂烩

作者: 奋斗的小马达 | 来源:发表于2021-03-03 14:41 被阅读0次

一、谈谈Runtime

1、OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行

2、OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数

3、平时编写的OC代码,底层都是转换成了Runtime API进行调用

利用如time我们可以做很多事情 如

1、利用关联对象(AssociatedObject)给分类添加属性

2、遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)

3、交换方法实现(交换系统的方法)

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

OC中方法的调用都是转化为objc_msgSend函数的调用

objc_msgSend函数可以分为三个阶段

第一个阶段是:消息发送阶段

首先判断消息的接受者是否存在 如果不存在则直接返回如果存在则 从自己类的方法缓存列表中查找该方法,如果存在则执行 如果不存在则去自己类的方法列表中查找,如果存在则缓存到自己类的方法缓存列表中 然后在执行,如果不存在则去父类的方法缓存列表中查找,如果存在则缓存到自己类的方法缓存列表中 然后执行,如果不存在 则去父父类的方法列表中查找,如果存在 则缓存到自己类的方法缓存列表中, 如果不存在则进入第二不方法动态解析阶段

第二阶段是:方法动态解析阶段

首先判断方法是否动态解析过,如果之前已经有过动态解析 则直接进入第一步消息发送阶段

如果没有过动态解析则调用+resolveInstanceMethod:或则+resoveClassMethod:方法来动态解析 然后标记为已经动态解析 最后重新走“消息发送”的流程

在此阶段 我们可以添加一个新的方法来代替原来的方法 如果在此阶段不做任何处理 则直接进入第三步 消息转发阶段

第三阶段是:消息转发阶段

1、首先调用forwardingTargetForSelector:方法 在此方法中:如果返回值不为空 则直接给返回值转发消息

如果返回值为空否则进入第2步 调用方法签名函数

2、调用methodSignatureForSelector:方法(方法签名函数)在此方法中:

如果返回值为空 则直接调用 调用doesNotRecognizeSelector:方法 然后程序报错:unrecognized selector sent to instance 经典错误

如果返回值不为空 则直接进入第3步 调用 forwardInvocation方法

3、forwardInvocation:方法

开发人员可以在此方法中处理调用的方法

二、谈谈Runloop

顾名思义就是运行循环,在程序运行过程中循环做一些事情

1、保持程序的持续运行

2、处理App中的各种事件(比如触摸事件、定时器事件等)

3、节省CPU资源,提高程序性能:该做事时做事,该休息时休息

runloop与线程之间的关系

1、每条线程都有唯一的一个与之对应的RunLoop对象

2、RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value

3、线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建([NSRunLoop currentRunLoop])

4、RunLoop会在线程结束时销毁

5、主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

获取runloop对象的方法

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象

[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象

CFRunLoopGetMain(); // 获得主线程的RunLoop对象

三、你做了什么工作使崩溃率下降的 (使用什么工具定位崩溃,崩溃的补救措施)

答:线上项目中集成第三方bug收集工具 bugly 然后配置好符号表(dSYM),app每次崩溃bugly后台都会有相应的崩溃信息,而且能够具体的某一行,根据崩溃信息查漏补缺。

开发过程中根据Xcode设置全局断点定位崩溃某一行。

测试过程中可以根据bugly后台查看崩溃信息,也可以使用Xcode查看手机里面的log信息来分析crash信息。

四、使用什么方式使子线程永驻

开启子线程的runloop

五、https 中间人攻击(是怎样攻击的)

HTTPS 中间人攻击。也就是通讯双方中插入一个中间人,通讯双方的对方已经变成中间人了,而不是原本的对方。

HTTPS 协议之所以是安全的是因为 HTTPS 协议会对传输的数据进行加密,而加密过程是使用了非对称加密实现。但其实,HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段。

HTTPS的整体过程分为证书验证和数据传输阶段

① 证书验证阶段

  1. 客户端发起 HTTPS 请求

  2. 服务端返回 HTTPS 证书

  3. 客户端验证证书是否合法,如果不合法则提示告警

② 数据传输阶段

  1. 当证书验证合法后,客户端在本地生成随机数

  2. 通过公钥加密随机数,并把加密后的随机数传输到服务端

  3. 服务端通过私钥对随机数进行解密

  4. 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输

为什么数据传输是用对称加密?

首先,非对称加密的加解密效率是非常低的,而 http 的应用场景中通常端与端之间存在大量的交互,非对称加密的效率是无法接受的;另外,在 HTTPS 的场景中只有服务端保存了私钥,一对公私钥只能实现单向的加解密,所以 HTTPS 中内容传输加密采取的是对称加密,而不是非对称加密。

为什么需要 CA 认证机构颁发证书?

HTTP 协议被认为不安全是因为传输过程容易被监听者勾线监听、伪造服务器,而 HTTPS 协议主要解决的便是网络传输的安全性问题。首先我们假设不存在认证机构,任何人都可以制作证书,这带来的安全风险便是经典的“中间人攻击”问题。“中间人攻击”的具体过程如下:

过程原理:

1、客户端请求被劫持(如DNS劫持等),所有请求均发送到中间人的服务器

2、中间人服务器返回中间人自己的证书

3、客户端创建随机数,通过中间人证书的公钥对随机数加密后传送给中间人,然后凭随机数构造对称加密对传输内容进行加密传输

4、中间人因为拥有客户端的随机数,可以通过对称加密算法进行内容解密

5、中间人以客户端的请求内容再向正规网站发起请求

6、因为中间人与服务器的通信过程是合法的,正规网站通过建立的安全通道返回加密后的数据

7、中间人凭借与正规网站建立的对称加密算法对内容进行解密

8、中间人通过与客户端建立的对称加密算法对正规内容返回的数据进行加密传输

9、客户端通过与中间人建立的对称加密算法对返回结果数据进行解密

由于缺少对证书的验证,所以客户端虽然发起的是 HTTPS 请求,但客户端完全不知道自己的网络已被拦截,传输内容被中间人全部窃取。

Q: HTTPS 为什么安全?

A: 因为 HTTPS 保证了传输安全,防止传输过程被监听、防止数据被窃取,可以确认网站的真实性。

Q: HTTPS 的传输过程是怎样的?

A: 客户端发起 HTTPS 请求,服务端返回证书,客户端对证书进行验证,验证通过后本地生成用于改造对称加密算法的随机数,通过证书中的公钥对随机数进行加密传输到服务端,服务端接收后通过私钥解密得到随机数,之后的数据交互通过对称加密算法进行加解密。

Q: 为什么需要证书?

A: 防止”中间人“攻击,同时可以为网站提供身份证明。

Q: 使用 HTTPS 会被抓包吗?

A: 会被抓包,HTTPS 只防止用户在不知情的情况下通信被监听,如果用户主动授信,是可以构建“中间人”网络,代理软件可以对传输内容进行解密。

参考地址:<u>https://juejin.cn/post/6844904065227292685</u>

六、KVC 原理

1、KVC的全称是Key-Value Coding、俗称“键值编码”、可以通过一个key来访问某一个属性。

2、常见的API有四种

setValue: forKey

setValue: forKeyPath

valueForKey:

valueForKeyPath

forKey 和 forKeyPath 主要区别是路径区别

七、KVO实现原理

KVO的原理:

1、当实例对象 进行KVO观察时候,会利用RuntimeAPI动态生成一个子类,然后将对象的isa指向新生成的子类

2、KVO本质上是监听属性的setter方法,只要被观察对象有成员变量和对应的set方法,调用Foundation的_NSSetValueAndNotify函数这个函数内部会执行 willChangeVlaueForKey函数、父类的setter方法 和didChangeVlaueForKey函数

3、子类会重写父类的set、class、dealloc方法

4、当观察对象移除所有的监听后,会将观察对象的isa指向原来的类

5、当观察对象的监听全部移除后,动态生成的类不会注销,而是留在下次观察时候再使用,避免反复创建中间子类

八、为什么ui在主线程刷新

UI刷新在主线程主要有两个原因

第一:安全

因为UIKit框架不是线程安全的,当多个线程同时操作UI的时候,抢夺资源,导致崩溃,UI异常等问题。

第二:用户体验

iOS中只有主线程才能立即刷新UI。在子线程中是不能够更新UI,我们看到的子线程能够更新UI的原因是,等到子线程执行完毕,自动进入了主线程去执行子线程中更新UI的代码。由于子线程执行时间非常短暂,让我们误以为子线程可以更新UI。如果子线程一直在运行,则无法更新UI,因为没有办法进入主线程。

九、block有几种,底层实现,以及如何持有外部变量的

block是封装了函数调用以及函数调用环境的OC对象

block 有三种类型分别如下

· __NSGlobalBlock

· __NSStackBlock

· __NSMallocBlock

只要没有访问 auto的变量 都是 NSGlobalBlock

访问了auto变量 但是没有调用 copy 都是 NSStackBlock

即访问了auto变量又调用了copy 则是 NSMallocBlock

注意:NSGlobalBlock即使调用了copy 它还是 NSGlobalBlock

         __NSStackBlock__调用了 copy 它就变成了  __NSMallocBlock__

         __NSMallocBlock__ 调用了copy 只是使引用计数增加 其类型还是 __NSMallocBlock__

十、load和initlize的区别

调用时机不同

load方法在runtime类、分类加载的时候调用且只调用一次 如果出现继承时

在调用子类的load方法时首先调用父类的load方法 其他子类的load方法则按照编译顺序来决定

initialize方法是在类第一次接收到消息时调用,

出现继承时父类的initialize方法可能会调用多次(父类的initialize调用多次并不代表父类会初始化多次,父类只会初始化一次)

调用方式不同

load方式是通过指针函数直接调用

initialize是通过objc_msgSend来调用

十一、性能优化

App的性能优化主要从两个方面

CPU 和 GPU

CPU 和 GPU 在屏幕成像过程中起着至关重要的作用

CPU 主要负责 对象的创建和销毁、 对象属性的调整、布局计算、文本计算和排版、图片的格式转换和解码、图像的绘制工作

GPU 主要负责纹理渲染

性能优化主要思路就是 尽可能减少 CPU和GPU的资源消耗

在CUP方面

1、尽量使用轻量级的对象、比如在使用不到事件处理的地方 可以考虑使用CALayer来取代UIView

2、不要频繁调用UIView的属性,比如 frame、bounds、transform 等属性,尽量减少不必要的修改

3、尽量提前计算好布局、在需要的时候一次性调整对应的属性、不要多次修改属性

4、AutoLayout会比直接设置frame消耗更多的CPU资源

5、图片的size最好和UIImageView的size的保持一致

6、控制一下子线程的最大并非数量

7、耗时的操作尽量放在子线程中处理

 比如,文本处理(尺寸计算、绘制)

             图片处理(解码、绘制)

GPU方面

1、尽量避免段时间内大量图片的显示,尽可能将多张图片合成一张显示

2、GPU最大处理纹理是4096 x 4096 一旦超过这个尺寸就会占用CPU资源来处理

3、尽量减少试图的数量和层数

4、尽量减少离屏渲染

十二、推送相关知识点

推送原理:

1、App向iOS设备发送一个注册通知,用户需要同意系统发送推送。

2、iOS设备向苹果远程推送服务器发送App的Bundle Id和设备的UDID

3、苹果推送服务器根据设备的UDID和App的Bundle Id生成deviceToken再发回给App

4、App再将deviceToken发送给远程推送服务器(自己的服务器), 由服务器保存在数据库中。

5、当自己的服务器想发送推送时, 会把用户的devicetoken和消息内容一起发送给苹果远程推送服务器

6、苹果远程推送服务器在自身已注冊Push服务的iOS设备列表中。查找有对应标识的iOS设备,并将消息发送到iOS设备

7、iOS设备接收到苹果远程服务器push过来的消息之后,找到相应的应用程序。并依照设定弹出Push通知

十三、MVC、MVVM、MVP相关知识点

MVC

MVC也就是Model View Controller(模型 视图 控制器)

MVC的特点就是view上显示什么内容取决于model 。view和model之间的通信是通过Controler来完成 Controler负责初始化 Model且对model进行读写调用然后并将 Model的值 传递给 View 去解析展示。

MVC有优点也有缺点

优点是

将数据与视图分离开来,有一定可阅读性。降低了代码的耦合性有利于代码的维护。

缺点

1、自身设计的缺点

在 MVC 模式中 view 将用户交互通知给控制器。view 的控制器通过更新 Model 来反应状态的改变。Model(通常使用 Key-Value-Observation)通知控制器来更新他们负责的 view。大多数 iOS 应用程序的代码使用这种方式来组织。

2、愈发笨重的 Controller

大量的代码被堆放在controller中使Controller变得越来越臃肿,臃肿的Controller 不仅要管理自身的属性状态还要遵循许多协议,导致协议的响应代码和 controller 的逻辑代码混淆在一起很难管理和测试。

3、太过于轻量级的 Model

ARC 普及以后我们在 Model 层的实现文件中基本上看不到代码,同时与控制器的代码越来厚重形成强烈的反差。

4、遗失的网络逻辑

开发过程中,我们的大部分数据来源于网络请求,如果网络请求放在model/view中,由于网络调用大部分使用异步,可能会出现网络请求比持有它的 Model /view生命周期更长,这样会导致网络请求被提前释放。放到controller中又使控制器越来越臃肿。

MVVM

MVVM 即 Model View(可能是View 也可能是ViewController) ViewModel(模型 视图 视图模型)

MVVM衍生于MVC,是对 MVC 的一种演进,它促进了 UI 代码与业务逻辑的分离。它正式规范了视图和控制器紧耦合的性质,并引入新的组件。

在MVVM 中,view 和 view controller正式联系在一起,我们把它们视为一个组件

view 和 view controller 都不能直接引用model,而是引用视图模型(viewModel)

viewModel 是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他代码的地方

使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性

MVP

MVP模式是MVC模式的一个演化版本 MVP全称Model-View-Presenter

Model:与MVC中的model没有太大的区别。主要提供数据的存储功能,一般都是用来封装网络获取的json数据的集合。Presenter通过调用Model进行对象交互。

View:这里的View与MVC中的V又有一些小差别,这个View可以是viewcontroller、view等控件。Presenter通过向View传model数据进行交互。

Presenter:作为model和view的中间人,从model层获取数据之后传给view,使得View和model没有耦合。

总得来说MVP的好处就是解除view与model的耦合,使得view或model有更强的复用性。

十四、App体积优化

1、删除项目中未使用的图片

2、删除项目中未使用的类

十五、启动时间优化

App的启动分为冷启动和热启动

冷启动是从零开始启动app

热启动是app已经在内存中,在后台运行,然后再次点击app图标启动app

一般我们优化启动时间 所指的就是优化app 的冷启动时间。

App 冷启动一般分为三个阶段

第一个阶段是 dyld Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)

启动APP时,dyld所做的事情有

1、装载APP的可执行文件,同时会递归加载所有依赖的动态库

2、当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理

第二个阶段:runtime

1、调用map_images进行可执行文件内容的解析和处理

2、调用所有Class和Category的+load方法

3、进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)

4、调用C++静态初始化器和attribute((constructor))修饰的函数

第三个阶段 main

也就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法 到根视图控制器页面显示的这一段

按照不同的阶段

第一阶段:dyld

1、减少动态库、合并一些动态库(定期清理不必要的动态库)

2、减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)

3、减少C++虚函数数量

第二阶段:runtime

1、用+initialize方法和dispatch_once取代所有的attribute((constructor))、C++静态构造器、ObjC的+load

第三阶段:main

在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中

按需加载

十六、多线程

iOS 多线程种类有4种

1、pthread 基于C语言可以跨平台 手动管理线程的生命周期 由于使用比较复杂几乎很少使用

2、NSThread 简单易用 可以直接操作线程对象,是基于OC语言的 手动管理线程的生命周期 偶尔使用

3、GCD 基于C语言,能够充分利用设备的多核,自动管理线程的生命周期 经常使用

4、NSOperation 基于OC是对GCD的封装 自动关线程的生命周期 经常使用

我在开发中经常使用的是GCD 因为

1、他可以多核的并行运算

2、会自动利用更多的CPU内核

3、hi自动管理线程的生命周期

4、使用简单

CGD有两个核心的概念

1、任务

2、队列

任务:分为同步和异步

同步和异步的区别在于能不能开辟新的线程

同步:不具备开辟线程的能力 任务只在当前线程执行

异步:具备开辟线程的能力,可以在新的线程中执行任务

队列:分为串行队列和并发队列

串行队列和并发队列主要的区别是能否同时执行多个任务

串行队列:让任务一个接着一个的执行

并发队列:任务可以同时执行,并发队列只有在异步中才有效

NSoperation和NSOPreationQueue:是对GCD更高一层的封装,但是比 GCD 更简单易用、代码可读性也更高,他也是自动管理线程的生命周期。

NSoperation和NSOPreationQueue 可以设定任务执行的优先级、可以随时取消一个操作的执行、也可以添加多个操作的依赖关系。

十七、线程锁

线程锁出现的原因就是解决多线程的安全隐患

当一块资源被多个线程同时访问的时候很容易引发数据错乱

解决这一问题的方案就是对线程加锁

iOS的锁有很多种

OSSpinLock //自旋锁 (iOS 10以后被os_unfair_lock替换)

os_unfair_lock //

pthread_mutex //互斥锁

dispatch_semaphore //信号量

dispatch_queue(DISPATCH_QUEUE_SERIAL)

NSLock

NSRecursiveLock

NSCondition

NSConditionLock

@synchronized

1.3 什么是 懒加载?

懒加载 也称为延迟加载, 核心思想是把对象的实例化尽量延迟,直到真正用到的时候才将其实例化,

这样做的好处是可以减轻大量对象在实例化时对资源的消耗,而不是在程序初始化的时候 就预先将对象实例化,

懒加载可以将对象的实例化代码从初始化方法中独立出来. 提高代码可读性,

1.5 什么是谓词?

简单的说 谓词就是一个过滤器, 符合条件的留下, 不符合条件的删除.

谓词本身就代表了一个逻辑条件, 计算谓词之后 返回的结果永远为 BOOL 类型的值.

而谓词最常用的功能就是对集合进行过滤,当程序使用谓词对集合元素进行过滤时, 程序会自动遍历其元素, 并根据集合元素来计算谓词的值,

当这个集合中的元素 计算谓词并返回YES时, 这个元素才会被保留下来. 并重新组合成一个集合返回 .

1.7 OC 的数组或字典中, 添加nil 对象会有什么问题?

数组或字典如果通过 addObject 函数添加 nil 会崩溃.

但初始化时 通过 initWithObjects方法里面的nil 会被编译器过滤去掉不会有影响,

另外如果使用语法糖初始化数组或字典也不可以有nil , 此时 nil 不会被过滤 也会崩溃.

1.10 AppDelegate 中主要的几个回调方法?

  • application (应用程序.) 应用启动并进入初始化时会调用该方法并发生通知, 这个阶段会实例化 根视图控制器,.

  • applicationDidBecomeActice ( 变得活跃 ) 应用进入前台并处于活动状态 调用该方法并发生通知, 这个阶段可以恢复 UI 的状态,

  • applicationWillResignActive (退出活动) 应用从活动状态 进入非活动状态时 调用该方法并发生通知, 这个阶段可以保存 UI 的状态,

  • applicationDidEnterBackground ( 进入后台 ) 应用进入前台, 但是还没有处于活动状态时 调用该方法并发生通知, 这个阶段 可以恢复用户数据,

  • applicationWillTerminate (应用将被终止 ) 应用被终止时调用该方法并发生通知, 但内存清除时 除外, 这个阶段释放一些资源, 也可以保存用户数据.

七、控制器的生命周期

创建

1、alloc 创建对象 分配空间

2、init 初始化对象 初始化数据、

3、loadview 从nib载入视图

4、viewDidLoad 载入完成 可以自定义数据以及动态加载其他数据

5、viewWillAppear 视图即将展示

6、viewDidAppear 视图加载完成

消失

1、viewWillDisappear 视图即将消失

2、viewDidDisAppear 视图已经消失

3、delloc 视图已被销毁

4.5 frame 和 bounds 有什么不同?

Frame 指的是 该view在父view坐标系统中的位置和大小, (参照点是父view的坐标系统)

Bounds 指的是 该view在本身系统中位置和大小, (参照点是本身坐标系统)

4.6 声明可变数组用copy 修饰会存在什么问题?

添加, 删除, 修改 数组内的元素的时候, 程序会因找不到对应的方法而崩溃,

因为 copy 就是将可变数组复制成一个 不可变的数组对象.

2.6 关于 Objective-C 语言的动态性主要体现在哪?

Objective-C 语言的动态性主要体现为3个方面:

  • 动态类型: 运行时确定对象的类型

  • 动态绑定: 运行时确定对象的调用方法

  • 动态加载: 运行时加载需要的资源或者可执行的代码

扩展

NSString *string = [[ NSData alloc ] init ];

问题: string 在编译时和运行时分别是什么类型的对象??

答: 在编译时是 NSString 类型

    在运行时是 NSData 类型 

2.9. id , instancetype 是什么? 区别是什么?

  • id : 万能指针, 能作为参数 和 方法的返回类型

  • instancetype : 只能作为方法的返回类型, 并且返回的类型是当前定义类的 类类型.

3.1 什么叫多态?

  • 不同对象以自己的方式响应相同的消息的能力叫做多态.

  • 程序中的多态: 父类指针指向子类对象

  • 在程序中的表现为: 父类指针指向不同子类对象的时候, 通过父类指针调用被重写的方法的时候,. 会执行该指针指向的那个对象的方法,

  • 原理: 动态绑定: 动态类型能使程序直到执行时才确定对象的真是类型, 动态类型绑定能使程序直到程序执行时 才确定要对那个对象调用的方法.

  • 多态的条件包括为: 有继承关系, 子类重写父类的方法, 父类指针指向子类对象.

3.3 什么是编译时 与 运行时?

  • 编译时: 即编译器对语言的编译阶段, 编译时只是对语言进行最基本的检查报错, 包括词法分析, 语法分析等等,

        将程序代码翻译成计算机能够识别的语言, 编译通过并不意味着程序就可以成功运行, 
    
  • 运行时: 即程序通过了编译之后, 将编译好的代码装载到内存中运行起来的阶段, 这个时候会具体对类型进行检查,

        而不仅仅是对代码的简单扫描分析. 此时出错程序会崩溃. 
    

3.6. isEqual 和 isEqualToString 有什么区别?

  • isEqual 是比较两个NSObject 的方法. 而 isEqualToString 是比较两个 NSString 的方法.

    明显 isEqualToString只是专门用来比较字符串的. 是isEqual的衍生方法.

  • isEqual 先是比较两个指针地址, 如果地址相同直接返回YES,

    如果地址不同, 再看两个指针指向的对象是否为空以及对象类型是否相同,

    如果有一个为空, 或者两者不是同类对象, 那么直接返回 NO.

    如果都不为空且属于同类对象 而且对象的属性也相等, 那么返回YES.

3.7. isa 指针指向什么? 有什么作用?

  • Objective-C 实例对象的isa指针是指向它的类对象的.

OC 中有3个层次的对象: 实例对象, 类对象, 和 元类.

Class 即自定义的类, 是实例对象的类对象, 而类对象又是其对应元类的实例对象.

isa 指针的作用是 通过它可以找到对应类对象 或 元类中的方法,

例如, 实例对象可以在 其类对象中 找到它的实例方法,

 Class 对象可以从元类中找到它的类方法. 

3.9 . 类别 (category) 和扩展 (extension) 的区别?

类别:

1.类别不能为已存在的类添加实例变量。

2.类别中添加的方法优先级高,会覆盖原类中的方法

3.原类中的私有方法,可以在类别中定义一下,这样就可以使用了。

4.在categroy中定义的方法可以被子类继承。

扩展:

1.扩展中可以定义属性,变量,和方法

2.扩展中定义的属性和方法不能被子类继承

3.扩展中只能对自定义的类添加方法,不能对系统类添加方法。

3.10. 继承和类别的区别?

  • 继承可以增加, 修改 或者 删除方法, 并且可以增加属性,添加新方法和父类方法一致, 但父类方法仍需要使用.

    0. 类别:

    1. 系统提供的一些类, 系统本身不提倡继承, 因为这些类的内部实现对继承有所限制.

    2. 类别可以将自己构建的类中的方法进行分组, 对于大型的类, 提高可维护性,.

    3. 分类的作用:

       将类的实现分散到多个不同文件 或 多个不同框架中 
    
       创建对私有方法的前向引用
    
       向对象添加非正式协议 ( 声明方法可以不实现, 不调用只警告. 不报错. )  正式协议的优点:   可继承.  泛型约束. 
    

    4. 类别可以在不获悉 不改变原来代码的情况下往里面添加新的方法.

    只能添加. 不能删除修改,  并且如果类别和原来类中的方法产生名称冲突, 则类别将覆盖原来的方法, 因为类别具有更高的优先级. 
    

    5. 分类的局限性:

     无法向类中添加新的实例变量, 类别没有位置容纳实例变量
    
     无法添加实例变量的局限可以使用字典对象解决 
    

3.11. 在一个对象的方法里面. self.name = “ object ” 和 _name = “ object ” 有什么不同?

Self.name = “ object “ 会调用对象的 setName() 方法

_name = “ object ” 会直接把 object 赋值给当前对象的name 属性

3.12. 内存分区有哪些? 分别用来存放什么?

  • 代码区: 存放函数二进制代码

  • 数据区: 系统运行时申请内存并初始化, 系统退出时由系统释放, 存放全局变量. 静态变量. 常量

  • 堆区: 通过 malloc 等函数 或 new 等动态申请到的, 需手动申请和释放

  • 栈区: 函数模块内申请, 函数结束时由系统自动释放, 存放局部变量, 函数参数

3.13. 有哪些设计模式? 各自的区别有哪些?

KVO: 一对多, 观察者模式, 键值观察机制, 提供了观察某一属性变化的方法, 极大简化了代码

KVC: 键值编码, 一个对象在调用 setValue时做的事: ( 即KVC 底层实现原理 )步骤如下:

  - 检查是否存在相应 key 的 set 方法,  存在就调用 set 方法

  - set 方法不存在时, 就查找 _key 的成员变量 是否存在, 存在就直接赋值

  - 如果 _ key 成员变量 没有找到, 就查找相同名称的 key.  存在就赋值

  - 如果都没有找到则会调用 valueForUndefinedKey和 setValue: forUndefinedKey

delegate : 发送者和接收者的关系是直接 . 一对一的关系

Notification : 观察者模式, 发送者 和接收者的关系是间接, 多对多的关系.

区别:

1. Delegate 的 效率比 Notification 高

2. Delegate 比 Notification 更直接, 需要关注返回值, 常带有 should 关键词; Notification 不关心结果, 常带有 did 关键词

两个模块之间的联系如果不是很亲密, 就用 Notification 传值, 比如多线程之间的传值,

3. KVO 容易实现 两个对象的同步, 比如 Model 和 View 的同步

3.14. 字典的value可为空吗? OC中表示为空有几种? 区别是什么?

NULL : 是宏, 是对于 C 语言指针而使用的, 表示空指针

nil : 是宏. 是对于 OC 中的对象而使用的, 表示对象为空

Nil : 是宏, 是对于 OC 中的 类 而使用的, 表示 类 指向空

NSNull : 是类类型, 用于表示空的占位对象,

字典的value不可直接设置为空, 会carsh, 但是可以使用 NSNull 来占位. 例如:

NSDictionary *params = @{ @“arg1” : @“value1”, @“arg2” : value2.isEmpty ? [ NSNull null ] : value2 } ;

3.17. 数据存储 数据持久化方式有哪几种, 有什么区别?

iOS有4种数据持久化:

1.属性列表 ( plist ) .

2.对象归档 . Sqlite.

3.Core Data

4.NSUserDefaults 用于存储配置信息

keychain 存储用户的敏感信息, 如登录的token . 需要导入Security 框架

3.18. imageName 和 imageWithContextOfFile 的区别? 哪个性能更高?

  • imageName的方式加载时, 图片使用完毕后缓存到内存中 内存消耗多. 加载速度快, 即使生成的对象被 内存管理池自动释放了. 这份缓存也不会被释放, 如果图像过大, 或者较多. 用这种方式会消耗很大的内存.

imageName 采用了缓存机制, 如果缓存中已加载了图片, 直接从缓存读取,不用每次读文件. 效率会更高.

  • imageWithContentOfFile 加载 图片是不会缓存的,. 加载速度慢

大量使用 imageName方式会在不需要缓存的地方额外增加开销 CPU 的时间, 当应用程序需要加载一张比较大的图片并且只使用一次时. 是没有必要去缓存这个图片的.

用imageWithContentOfFile是最为经济的方式,. 这样不会因为UIImage元素较多情况下. CPU 会被逐个分散 在不必要的缓存上, 浪费过多时间.

3.19 静态链接是什么? 静态库和动态库有什么区别?

静态链接是指 将多个目标文件合并为一个可执行文件, 直观感觉就是将所有目标文件的段 合并,

静态库, 链接时完整的拷贝至可执行文件中, 被多次使用就又多份冗余拷贝

动态库: 链接时不复制, 程序运行时由系统动态加载到内存, 供程序调用, 系统只加载一次 多个程序公用. 节省内存

3.20 Static 和 Const 有什么区别?

const 是指声明一个常量

Static 修饰全局变量时, 表示此全局变量只在当前文件可见.

Static 修饰局部变量时, 表示每次调用的初始值 ,为上一次调用的值, 调用结束后存储空间不会释放.

3.21 深拷贝和浅拷贝的区别?

深拷贝: 对一个对象进行拷贝, 相当于对对象进行复制, 产生一个新的对象, 会存在两个指针分别指向两个对象, 当一个对象改变或者被销毁后, 深拷贝出来的新对象不会受到影响

浅拷贝: 对一个对象进行浅拷贝. 相当于对指向对象的指针进行复制, 产生一个新的指针指向这个对象, 存在两个指针指向同一个对象, 对象销毁后两个指针都应该被置空,

总结: 深拷贝 产生了新对象. 浅拷贝: 本质上并没有产生新对象.

4.1. UIView 和 CALayer 的关系和区别有哪些?

UIView 是 iOS 中所有的界面元素都继承自它 每一个 UIView 内部都默认关联着一个layer 真正的绘图部分, 是由一个叫 CALayer 的类来管理的

UIView 有个 layer 属性 可以返回它的主 CALayer 实例,

UIView 有一个layerClass 方法, 返回主layer所使用的类.

UIView 的子类. 可以通过重载这个方法, 来让UIView 使用不同的CALayer 来显示,

** 区别:

UIView 继承自UIResponder, 能接收并响应事件, 负责显示内容的管理,

而CALayer继承自 NSObject, 不能响应事件, 负责显示内容的绘制,

UIView 侧重展示内容 ,CALayer 侧重于图形和界面的绘制,

当 view 展示的时候, View 是 layer 的CALayerDelegate, view 展示的内容是由 CALayer 进行 display 的.

View 内容的展示 依赖 CALayer 对内容的绘制, UIView 的frame 也是由内部的 CALayer 进行绘制.

对 UIView 的属性修改. 不会引起动画效果. 但是对于 CALayer 的属性修改, 是支持默认动画效果的, 在 view 执行动画的时候, view 是layer 的代理,

layer 通过 actionForLayer: forKey 向对应的代理 view 请求动画 action.

每个UIView 内部都有一个 CALayer 在背后提供内容的绘制和显示, 并且 UIView 的尺寸样式都由内部的 layer 所提供, layer 比 view 多了个 anchorPoint

一个 CALayer 的 frame 是由其 anchorPoint position bounds. Transfrom 共同决定的, 而 UIView 的 frame 只是简单的返回 CALayer 的frame,

4.2. layoutSubView 和 drawRects 区别?

layoutSubView 在以下情况会被调用:

1. init 初始化不会触发 layoutSubView

2. addSubView会触发layoutSubView

3. 设置 view 的Frame 会触发 layoutSubview . 当然前提是 frame 的值设置前后发生了变化.

4. 滚动一个 UIScrollView 会触发 layoutSubView

5. 旋转 Screen 会触发父View上的 layoutSubView事件.

6. 改变一个 UIView 大小的时候.也会触发父 UIView 上的 layoutSubView 事件

7. 直接调用 setLayoutSubView

drawRect 在以下情况会被调用:

1.如果 在 UIView 初始化时没有设置 rect 大小, 将导致 drawRect 不被自动调用,

2.drawRect 调用是在 Controller —-> loadView. Controller —-> viewDidLoad 两方法之后调用.

3.在调用 sizeToFit 后被调用. 所以可以先调用 sizeToFit 计算出 size . 然后系统自动调用 drawRect 方法.

4.通过设置 contentMode 属性值 为 UIViewContentModeRedraw. 那么每次设置 或更改 frame 的时候 自动调用 drawRect.

5.直接调用 setNeedsDisplay 或者 setNeedsDisPlayInRect 触发 drawRect. 但是有个前提条件是 rect 不能为0.

drawRect 方法使用注意点:

1> 若使用 UIView 绘图, 只能在 drawRect 方法中获取相应的contextRef 并绘图,

   如果在其他方法中获取将获取到一个 invalidate 的 ref 并且不能用于画图,  drawRect 方法不能手动显示调用, 

   必须通过 setNeedsDisplay  或者 setNeedsDisplayInRect  ,. 让系统自动调用该方法. 

2> 若使用 calayer 绘图, 只能在 drawInContext  中 绘制, 或者在 delegate 中的相应方法绘制,  同样也是调用 setNeedDisplay 等间接调用以上方法. 

3> 若要实时画图, 不能使用 gestureRecognizer  只能使用 touchbegan  等方法来调用 setNeedsDisplay 实时刷新屏幕. 

4.3 哪些操作会出发离屏渲染?

光栅化 layer. shouldRasterize = YES

遮罩. layer. mask

圆角. 同时设置, layer.maskToBounds = yes layer.cornerRadis 大于 0

阴影. layer.shadow 如果设置了 layer. shadowPath 就不会产生离屏渲染.

4.7 声明一个 NSString 或 NSArray 使用 Strong 修饰 可能会造成什么问题?

父类指针可以指向子类对象, 使用 Copy 修饰的目的是为了让该对象的属性不受外界影响,

使用Strong修饰可能使这个属性指向一个可变对象, 如果这个可变对象在外部被修改了. 那么会影响到该属性.

4.8 retain 和 copy 的区别?

copy 是建立了一个相同的对象, 而 retain 不是.

retain: 是指针拷贝, copy 是内容拷贝,

retain: 是创建一个指针, copy 是创建一个新的对象,

copy : 建立一个索引计数 为 1 的新对象, 然后释放旧对象, 新的对象 retain 为1, 与旧有对象引用计数 无关, 减少了对象对上下文的依赖.

retain: 释放旧的对象, 将旧对象的值赋予输入给新对象, 再提高输入对象的索引计数 为 1, 新对象和旧对象的指针相同.

4.9 readwrite readonly assign retain copy weak strong nonatomic 属性的作用? 即声明属性时可修饰属性特性的修饰词作用?

1.getter = getterName , setter = setterName 设置 getter 和 setter的方法名

2.readwrite, readonly : 设置属性可访问级别, readwrite 可读可写, readonly 可读不可写

3.assign: setter方法直接赋值, 不会进行任何retain操作, 用于非指针变量, 一般用于修饰基础数据类型 和 C 数据类型,

4.retain : setter 方法对参数进行release旧值在retain新值,

5.copy : setter 方法对参数进行copy 操作, 和 retain处理流程一样, 先旧值 release, 再 Copy 出新的对象, retainCount 为1.

6.nonatomic , atomic: 非原子操作 和 原子操作, 非原子操作是线程不安全的, 但在多线程并发访问时会提高性能, 原子操作是线程安全的, 但不是决定的, 性能比非原子操作低. 一般使用nonatomic

7.weak : 用于指针变量, 比 assign 多了一个功能, 当对象消失后自动把指针置为nil, 可以避免循环引用.

8.strong : 用于指针变量, setter方法对参数进行release 旧值再retain新值, 属于强引用

4.10. OC堆和栈的区别?

管理方式: 对于栈而言, 是由编译器自动管理, 无需手动控制,

     对于堆而言, 释放工作需要程序猿控制, 容易造成内存泄漏. 

分配方式: 堆都是动态分配的, 没有静态分配的堆,

     栈有两种分配方式, 静态分配和动态分配,    静态分配是由编译器完成的, 比如 局部变量的分配,  动态分配由 alloca 函数进行分配,  但是栈的动态分配和堆是不同的,  他的动态分配是由编译器进行释放, 无需手动实现, 

空间分配的区别:

栈:  由操作系统自动分配释放, 存放函数的参数值, 局部变量的值等, 使用的是一级缓存, 通常都是被调用时处于存储空间中, 调用完毕立即释放. 

堆:  一般由程序猿手动释放, 若不释放, 程序结束时可能由OS 回收,  存放在二级缓存, 生命周期由虚拟机的垃圾回收算法来决定, 

4.13. 用assign 修饰对象会怎么样?

如果用assign修饰一个对象后,当对象被释放后,存在于栈上的指针还是存在的,

假如此时使用指针,它就是一个野指针了,就容易造成程序崩溃,如果是用weak修饰的对象,则不会产生上面的情况,

因为对象销毁的时候,系统会将指针置nil,也就不会产生野指针了。

所以: 修饰对象用 weak, 基础数据类型用 assgin

4.14 PerformSelector 的实现原理

1. 当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。

2. 当调用 performSelector:onThread: 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。

4.15 @ property 会自动帮我们做的事有哪些?

1. 生成一个带下划线的成员变量

2. 声明一个 set 方法. 一个 get 方法

3. 生成 set 方法 和 get 方法的具体实现.

4.16 什么是垂悬指针? 什么是野指针?

垂悬指针: 指针指向的内存已经被释放了. 但是指针还在, 这就是一个垂悬指针, 或者说是迷途指针

野指针: 没有进行初始化的指针, 其实都是野指针.

4.17 实例方法和类方法有什么本质区别和联系?

类方法:

1.类方法是属于类对象的    

2.类方法只能通过类对象调用  

3.类方法中的self是类对象 

4.类方法可以调用其他的类方法

5.类方法中不能访问成员变量  

6.类方法中不能直接调用对象方法

实例方法:

1.实例方法是属于实例对象的  

2.实例方法只能通过实例对象调用    

3.实例方法中的self是实例对象   

4.实例方法中可以访问成员变量 

5.实例方法中直接调用实例方法 

6.实例方法中也可以调用类方法(通过类名)

4.19 为什么一定要在主线程里面更新UI?

像UIKit这样大的框架上确保线程安全是一个重大的任务,会带来巨大的成本。

UIKit不是线程安全的,假如在两个线程中设置了同一张背景图片,很有可能就会由于背景图片被释放两次,使得程序崩溃。

或者某一个线程中遍历找寻某个subView,然而在另一个线程中删除了该subView,那么就会造成错乱。

apple有对大部分的绘图方法和诸如UIColor等类改写成线程安全可用,可还是建议将UI操作保证在主线程中

5.5. 线程间通信

1> NSThread 调用系统的 performSelectorOnMainThread 回到主线程处理UI或其他事件. performSelector:(SEL)aSelector 回到指定线程处理事件

2> GCD: 子线程与主线程通过系统方法 dispatch_async(dispath_get_main_queue( ) ) 进行线程间通信

3> NSOperation : 设置依赖关系 手动调用主线程[NSOperationQueue mainQueue] 进行线程间通信

5.6. 用过NSOperationQueue吗?如果用过或者了解的话,为什么要使用 NSOperationQueue?实现了什么?简述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来述)

使用NSOperationQueue 用来管理子类化的NSOperation 对象. 可以控制其线程并发数目.

GCD 和 NSOperation 都可以实现对线程的管理.

区别是 NSOperation 和 NSOperationQueue 是多线程的 面向对象抽象.

项目中使用 NSOperation的优点是对线程的高度抽象,会使得项目的程序结构更好,

子类化NSOperation是具有面向对象的优点, 复用 和 封装

使得实现是多线程支持, 接口简单, 适合在复杂的项目中使用,

GCD的优点是 本身非常简单, 易用, 对于不复杂的多线程操作, 会节省代码量, 而Block参数的使用, 使代码更为易读, 适合在简单项目中使用,

GCD是纯C语言的API. NSOperationQueue 是基于GCD的 OC 版本的封装

GCD只支持 FIFO 队列, 即先入先出, NSOperationQueue 可以调整执行顺序, 设置最大并发数量, 可以在Operation 间设置依赖关系,

而GCD需要写很多代码才能实现依赖.

NSOperationQueue 支持KVO 可以检测operation 是否正在执行, 是否结束, 是否取消,

GCD的执行速度比NSOperationQueue快. 任务间不太互相依赖,

5.7. NSOperation. GCD. NSThread 的区别?

NSOperation 与 GCD 的区别:

GCD:

GCD 纯C

GCD 是将任务添加到队列 *( 队列有 串行 并行 全局 主队列 ) 并且以 同步 或 异步的方式执行任务函数

GCD提供NSOperation 不具备的功能有以下:

   一次性执行

   延迟执行

   调度组

   GCD 是严格的队列. 先进先出. FIFO

NSOperation:

NSOperation 在 iOS 2.0 推出. 4.0 重写

NSOperation 将操作 ( 异步任务 )添加到队列 ( 并发队列 ) 就会执行指定的函数

NSOperation 提供的方便操作有以下:

   最大并发数

   队列暂停和继续

   取消所有的操作

   指定操作之间的依赖关系. 可以让异步任务同步执行

   可以利用KVO监听一个任务是否完成

   可以设置任务的优先级,  能使同一个并行队列中的任务区分先后地执行

   对NSOperation 继承, 在这之上添加成员变量和成员方法, 提高代码的复用度

GCD 与 NSThread 的区别:

NSThread 使用 @selector 指定要执行的方法. 代码分散

GCD通过 block 指定执行的方法, 代码集中

GCD 不用管理线程的生命周期 ( 创建 销毁 复用 )

如果要开多个线程 NSThread 必须实例化多个线程对象

NSThread 通过 performSelector 方法实现线程间通信

为什么要取消和恢复队列?

一般内存警告后 取消队列中的操作

为保证ScrollView 在滚动时候的流畅, 通常在滚动开始时, 暂停队列中的所有操作, 滚动结束后, 恢复操作.

5.9 什么是异步绘制?

异步绘制 就是可以在子线程把需要绘制的图形, 提前在子线程处理好,

将准备好的图像数据直接返给主线程使用, 这样可以降低主线程的压力.

5.11 例举下你所知道的线程同步策略?

1. OSSpinLock 自旋锁, 已不再安全, 除这个锁, 下列锁在等待时, 都会进入线程休眠状态, 而非忙等.

2. os_unfair_lock atomic就是使用此锁来保证原子性的.

3. pthread_mutex_t 互斥锁. 并且支持递归实现和条件实现,

4. NSLock, NSRecursiveLock. 基本的互斥锁. 后者支持递归调用, 都是对 pthread_mutex_t 的封装.

5. NSCondition. NSConditionLock. 条件锁. 对pthread_mutex_t 的封装,

6. dispatch_semaphore_t 信号量.

7. @synchronized 也是对 pthread_mutex_t 的封装.

5.12 Runtime 如何实现 weak 变量的自动置为nil 功能?

Runtime 在注册和初始化一个类时. 当一个属性被修饰为 weak 时, 会将 weak 变量指向的地址作为 Value 放入一张 hash 表中.

将 weak变量的值 作为key. 这样形成一个 key - value 的键值对.

当引用计数变为 0 的时候. 系统通过 key - value 查找指向weak 变量的地址,. 将变量赋值为 nil.

5.13 atomic 的实现机制是怎样的? 为什么不能保证绝对的线程安全?

atomic 是在setter和getter方法里会使用自旋锁 spinlock_t 来保证setter 和 getter方法的线程安全, 可以看做是 getter方法获取到返回值之前不会执行setter方法里的赋值代码,

如果不加 atomic. 可能在getter方法读取的过程中, 在别的线程已经发生setter操作, 从而出现异常值,

atomic 不能绝对的保证线程安全, 因为出了getter和setter方法后就不能继续保证线程安全

5.14 RunLoop 的作用是什么?

字面意思是 消息循环, 运行循环, runloop内部实际上就是一个 do-while循环, 它在循环监听各种事件源, 消息, 对他们进行管理并分发给线程来执行. 主要作用有:

1.通知观察者将要进入运行循环, 线程和RunLoop之间是一一对应的.

2.通知观察者将要处理计时器

3.通知观察者任何非基于端口的输入源即将被触发.

4.触发任何准备触发的基于非端口的输入源

5.如果基于端口的输入源准备就绪并等待触发, 请立即处理该事件, 转到第9步

6.通知观察者线程即将睡眠

7.将线程置于睡眠状态, 直到发生以下事件之一:

  • 事件到达基于端口的输入源

  • 计时器运行

  • 为运行循环设置的超时值到期

  • 运行循环被明确唤醒

8. 通知观察者线程被唤醒

9. 处理待处理事件

 - 如果触发了用户定义的计时器, 则主力计时器事件并重新启动循环, 转到第二步, 

 - 如果输入源被触发, 则传递事件, 

 - 如果运行循环被明确唤醒但尚未超时, 请重新启动循环. 转到第二步. 

10. 通知观察者运行循环已退出.

6.2. SDWebImage 底层原理?

1.SDWebImageManager 根据URL 开始处理图片

2.先根据URL作为下标从缓存查找图片是否已经下载.

3.如果本地缓存图片中有图片, SDImageCacheDelegate 直接调用图片显示.

4.如果本地缓存中没有, 则开始通过URL为key 从本地磁盘查找图片是否已经被缓存至硬盘.

5.本地磁盘如果存在图片, 则通过 SDWebImageManagerDelegate 到 UIImageView+WebCache 展示图片

6.如果本地磁盘不存在图片, 说明所有缓存都不存在该图片, 需要下载,

7.生成下载器 SDWebImageDownloader 开始向服务器请求下载,

8.图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。

9.数据下载完成后交给 SDWebImageDecoder 做图片解码处理。

10.通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。

11.将图片以URL为下标 . 保存到 SDImageCache 中,内

SDImageCache 分为两个部分:一个是内存层面的,一个是硬盘层面的。

内存层面的相当是个缓存器. 以Key-Value的形式存储图片。当内存不够的时候会清除所有缓存图片。

用搜索文件系统的方式做管理,文件替换方式是以时间为单位,剔除时间大于一周的图片文件。

当SDWebImageManager 向 SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,

将图片从磁盘读取出来,然后做Decoder,将图片对象放到内存层面做备份,再返回调用层。存缓存和硬盘缓存同时保存。

7.2. UITableView 的优化

1、注册重用标识符

2.避免cell的重新布局

3.提前计算并缓存cell的属性及内容

4.减少cell中控件的数量

5.不要使用ClearColor,无背景色,透明度也不要设置为0因为渲染耗时比较长

6.使用局部更新 如果只是更新某组的话,使用reloadSection进行局部更

7.加载网络数据,下载图片,使用异步加载,并缓存

8.少使用addView 给cell动态添加view

9.按需加载cell,cell滚动很快时,只加载范围内的cell

10.不要实现无用的代理方法,tableView只遵守两个协议

11.不要做多余的绘制工作。

12.使用正确的数据结构来存储数据。减少不必要的修改

7.3 如何对代码进行 性能优化?

1. 利用性能分析工具检测. 包括静态 Analyze 工具 , 以及运行时 profile 工具, 通过Xcode工具栏中 Product —> Profile 可以启动.

2. 测试程序的启动时间, 点击 Time Prefiler 应用程序开始运行后, 就能获取整个应用程序运行消耗时间分布 和 百分比,

为了保证数据分析在统一使用场景的真实. 要注意一定要使用真机. 模拟器此时是运行在 MAC 上. 而 Mac 上的 cpu 往往比 iOS 设备快.

3. 为防止一个应用占用过多的系统资源, 工程师设计了一个 “看门狗”机制. 在不同场景下, 看门狗 会检测应用的性能,

如果超出了该场景所规定的运行时间, 看门狗 就会强制终结这个应用的进程, 开发者在 crashlog 里面. 会看到如: 0x8badf00d 这样的错误代码.

8.0 网络相关

8.1. TCP 和 UDP的区别

TCP: 传输控制协议, 提供的是面向连接 可靠的字节流服务. 客户端和服务端在交换数据之前, 必须先在双方建立一个 TCP 连接, 一个TCP 连接必须经过三次握手才能建立.

  TCP 提供 超时重发, 丢弃重复数据,  检验数据,  流量控制等功能, 保证数据能从一端 传到另一端. 

UDP: 用户数据报协议, 是面向数据报的运输层协议.

  UDP 是面向 非连接的协议,  它不与对方建立连接, 而是只负责把数据包发送过去, 不提供可靠性,  可靠性不高,

  由于不需要在客户端与服务端建立连接, 并且没有超时重发等机制, 因此传输速度 很快. 

差别在于:

tcp —- 面向连接 udp —- 面向非连接

Tcp —- 传输可靠 udp —- 传输不可靠

tcp —- 传输大量数据 udp ——传输少量数据

tcp —— 速度慢 udp —— 速度快

8.2. Socket 和 http 连接的区别

socket 连接 和 http 连接的区别:

http 是基于socket 之上的, socket 是一套完整的 tcp udp 协议的接口

HTTP 协议: 简单对象访问协议, 对应于应用层, HTTP 协议是基于 TCP连接的.

TCP 协议: 对应于传输层

IP 协议: 对应于网络层

TCP/IP 是传输层协议, 主要解决数据如何在网络中传输, 而 HTTP 是应用层协议, 主要解决如何包装数据,

Socket 是对 TCP/IP协议的封装, Socket 本身并不是协议, 而是一个调用接口, 通过Socket 我们才能使用TCP/IP 协议.

HTTP 连接: 短连接, 即客户端向服务端发送一次请求, 服务端响应后连接, 请求结束后, 会主动释放连接.

Socket连接: 长连接, 理论上客户端和服务端一旦建立连接将不会主动断掉. 但由于各种因素可能会断开.

例如: 服务端或客户端主机挂掉了. 网络故障, 或两者之间长时间没有数据传输, 网络防火墙可能会断开该连接以释放网络资源,

  所以当一个 Socket 连接中没有数据的传输, 为了 维持连接 需要发送 心跳包. 

一、scoket 相关知识点(概念、三次握手四次断开、心跳包、重连以及重连次数)

Scoket 是客户端和服务端之间相互通信的桥梁,想要实现两者之间的通讯,就必须建立连接俗称三次握手。

第一次、由客户端向服务端发送 SYN 同步报文。

第二次、当服务端收到 SYN 同步报文之后,会返回给客户端 SYN 同步报文和 ACK 确认报文。

第三次、客户端会向服务端发送 ACK 确认报文,此时客户端和服务端的连接正式建立。

建立连接之后为保证两端时刻活跃,客户端需要每隔一段时间向服务端发送心跳包

有时由于网络波动或则其他一些因素导致两者断开了连接,我们需要做重连处理,一般情况重连时间2的指数级增长当达到一定次数将放弃重连。

注:SYN 同步序列编号(Synchronize Sequence Numbers)。是TCP/IP建立连接时使用的握手信号。

二、MQTT相关知识点(概念、订阅、发布、心跳包、重连)

消息队列遥测传输简称MQTT,他是发布/订阅型消息协议,MQTT协议具有轻量、简单、开放和易于实现等优点因此它适用范围非常广泛。

MQTT协议有三种身份:发布者、代理、订阅者,发布者和订阅者都为客户端,代理为服务器,同时消息的发布者也可以是订阅者。

MQTT订阅

传输的消息分为主题(Topic,可理解为消息的类型)和负载(可以理解为消息的内容)两部分

通过消息的内容我们可以得到是否订阅成功,订阅失败需要重新订阅,一般我们也会设置一个最大订阅次数,

MQTT发布

发布消息会携带主题(topic)和消息内容,我们接收到MQTT发布topic之后根据topic来做相应的处理

8.3. AFN 是如何实现断点续传的??

1.检查服务端文件信息

2.检查本地文件

3.如果本地文件比服务端文件小, 断点续传, 利用 HTTP 请求头的 Range 实现断点续传

4.如果比服务端文件大, 说明本地文件错误, 重新请求下载

5.如果和服务端文件一样, 下载完成

AFN 默认超时 时间 为 60s

8.4 项目中网络层如何做安全处理?

  • 尽量使用 https

  • 不要传输明文密码

  • post 并不比 get 安全 事实上, post 和 get 一样不安全, 都是明文 参数放在 queryString 或者 body 没有任何安全上的差别,

    在http的环境下. 使用post 或者 get 都需要做加密和签名处理.

8.5 HTTPS 协议 与 HTTP 协议有什么区别与联系?

HTTPS 协议是由 SSL + HTTP 协议构建的可进行加密传输身份认证的网络协议. 要比 http协议安全.

HTTPS 安全超文本传输协议 . 它是一个安全通信通道. 基于 HTTP 开发. 用于在 客户计算机 和服务器之间交换信息,.

它使用安全套接字层 ( SSL ) 进行信息交换, 简单来说 它是 HTTP 的安全版,

区别在于:

https协议需要用到 ca 申请证书, 一般需要交费.

http 是超文本传输协议, 信息是明文传输, https 则是具有安全性的 SSL 加密传输协议

http 和 https 使用的是完全不同的连接方式, 用的端口也不一样. http 是 80 . Https 是443

http 的连接很简单. 无状态.

十一、 HTTP协议中 POST 方法和 GET 方法有那些区别?

  1. GET用于向服务器请求数据,POST用于提交数据

  2. GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,因此GET请求不适合用于验证密码等操作

  3. GET请求的URL有长度限制,POST请求不会有长度限制

1.6 设计模式的工厂方法?

工厂模式是利用了 面向对象3大特性之一 ---> 多态.

父类指针指向子类对象这个特性,

父类定义方法, 子类实现, 是一种创建类模式,

在任何需要生产复杂对象的地方, 都可以使用工厂方法,

良好的封装性, 代码结构清晰. 屏蔽产品类, 调用者只需要关心产品的接口 而不关心内部的实现,

复杂对象比较适合工厂模式. 简单对象有时仅需要 new 创建就可以了.

工厂模式依赖抽象架构, 它把实例化任务交给实现类. 扩展性较好.

1.2 什么是响应者链?

响应者链用于确定事件响应者的一种机制, 其中的事件主要指 触摸事件.

响应触摸事件的都是屏幕上的界面元素, 而且必须是继承自 UIResponse 类的界面类. 才可以响应触摸事件.

一个事件响应者的完成 主要经过两个过程, hitTest 方法命中视图 和相应这链 确定相应者,

hitTest 方法首先从顶部 UIApplication 往下调用, ( 从父类到子类. ) 直到找到命中者, 然后从命中者视图 沿着 相应者链 往上传递寻找真正的相应者 ,

一个继承自 UIResponder 的视图 要想能响应事件, 需要满足以下条件:

1. 必须要有对应的视图控制器, 即响应函数的逻辑代码要写在控制器内, 另外,(对于不支持默认能响应事件的控件 如” UIImageView ) userInteractionEnabled 属性必须设置为 YES , 否则会忽视事件不响应.

2. hidden 属性必须为NO, 隐藏的视图不可以响应事件, alpha 透明度属性的值不能过低,. 低于 0.01 接近透明也会因影响响应.

3. 需要注意保证树形结构的正确性. 子节点的 frame 一定都要在 父节点的 frame 内

3.16. 简要说明 APP的启动过程, 从main 文件说起. Main 函数中有什么? 作用是什么?

打开程序 —> 执行main 函数. —-> UIApplicatonMain 函数. ——-> 初始化 UIApplicationMain 函数 (包括 设置代理, 开启事件循环 ) —— > 监听系统事件 —— > 程序启动结束

UIApplicationMain 函数作用:

1> 根据传入的第三个参数 创建 UIApplication 对象 或它的子类对象, 如果该参数为nil,. 直接使用该UIApplication对象的 代理 属性,

2> 根据传入的第四个参数 创建 AppDelegate 对象, 并将该对象赋值给第一步 创建的 UIApplication 对象的 delegate 属性.

3> 开启一个事件循环. 循环监控应用程序发生的事件. 每监听到对应的系统事件时, 就会通知 AppDelegate.

Main 函数作用:

1> 创建 UIApplication 对象,

2> 创建应用程序代理/

3> 开启时间循环,. 包括应用程序的循环运行, 并开始处理用户事件.

1.4 什么是 Cocoa 和 Cocoa Touch?

相同之处: 两者都包含 OC 运行时的两个核心框架

  • cocoa 包含 Foundation 和 AppKit 框架, 可用于开发 Mac OS X 系统的应用程序

  • cocoa touch 包含 Foundation 和 UIKit 框架, 可用于开发IPhone OS 系统的应用程序,

  • Cocoa 是 Mac OS X 的开发环境, Cocoa Touch 是 iPhoen OS 的开发环境.

9.0 Swift 相关

9.1 Swift 内联函数是什么? 在什么情况下会不起作用?

内联的前提是确定调用的函数体内容, 将函数调用展开成函数体

以下情况函数不会被内联

1> 函数体比较长

2> 包含了递归调用.

3> 包含了动态派发.

9.2 举例说明Swift 里面有哪些类型是 OC 中没有的?

Swift 引入了在Object-C 中没有的一些高级数据类型,

例如:

1.tuples 元组. 可以创建和传递一组数值.

2.Optionals 可选项类型 用于处理变量值不存在的情况,

9.3 Swift 中如何阻止方法, 属性 下标 被子类改写?

在类的定义中使用 final 关键字声明类, 属性, 方法和下标,

final 声明的类不能被继承, final 声明的属性方法和下标 不能被重写.

如果只是限制一个方法或者属性不被重写, 只需要在该方法和该属性前加一个 final

如果需要限制整个类无法被继承, 那么可以在类名之前加一个 final

9.4 Swift 中 closure 和 OC 中的 Block 有什么区别?

1.closure 是匿名函数. block 是一个结构体对象

2.closure 通过逃逸闭包来在内部修改变量, block 通过 __block 修饰符

9.5 什么叫 逃逸闭包? 如何让一个 自动闭包可以”逃逸”?

逃逸闭包: 一个传入函数的闭包, 如果在函数执行结束之后才会被调用, 那么这个闭包就称为逃逸闭包.

如果想让自动闭包可以”逃逸”, 需要同时使用 @autoclosure 和 @escaping 进行修饰.

9.6 Swift 中 Class (类 )和 Struct ( 结构体 ) 的区别?

1. Class 是引用类型. Struct 是值类型.

2. 类可以被继承 , 结构体不可以继承

3. 值类型 struct 被赋予给一个变量, 常量 或者被传递给一个函数的时候, 其值会被拷贝,

4. 引用类型 class 在被赋予一个变量 常量 或者传递到一个函数时 , 其值不会被拷贝,

  • 因此. 引用的是已存在的实例本身 而不是其拷贝.

9.7 Swift 是面向对象语言 还是面向过程的函数式编程语言?

Swift 既是面向对象的, 又是函数式的编程语言,

因为Swift 支持类的封装, 继承, 和多态 所以是面向对象的.

又因 Swift 支持 map. Reduce. filter, flatmap 这类去除中间状态.数学函数式的方法, 所以也支持面向过程编程.

2.1 Swift 和 OC 的相比有哪些优点?

swift 的特点有:

  • 快速, 现代. 安全, 互动, 而且明显优于 OC

  • 可以使用现有的 Cocoa 和 Cocoa Touch 框架

  • Swift 取消了 Objective-C 的指针 / 地址 等不安全访问的使用.

  • 提供命名空间, 泛型, 运算对象重载,

  • Swift 被简单的形容为 “ 没有C 的Objective-C”

  • 为开发工具 Xcode 带来了 Xcode Playground 功能, 提供强大的互动效果, 能让 Swift 源代码在撰写的过程中实时显示出其运行结果,

  • 基于C 和 OC. 却没有C 的一些兼容约束

  • 采用了安全的编程模式

  • 界面基于 Cocoa 和 Cocoa Touch 框架

  • 舍弃 OC 早期应用 Smalltalk 的语法, 保留Smalltalk 的动态特性, 全面改为句点表示法.

  • 对比OC 的动态绑定, 类型严谨.

相关文章

网友评论

      本文标题:iOS相关知识(一)-- 大杂烩

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