KVO 实现原理?
- KVO 是基于 runtime 机制实现的
- 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在被重写的 setter 方法内实现真正的通知机制,如果原类为 Person ,那么生成的派生类名为 NSKVONotifying_Person。
- 每个类对象中都有一个 isa 指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将 isa 指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的 sette r方法。
- 键值观察通知依赖于 NSObject 的两个方法:
willChangeValueForKey:
和didChangevlueForKey:
,在一个被观察属性发生改变之前,willChangeValueForKey:
一定会被调用,这就会记录旧的值。而当改变发生后,didChangeValueForKey:
会被调用,继而observeValueForKey:ofObject:change:context:
也会被调用。
KVC 实现原理?
-
setValue:forKey
:搜索方式,首先搜索 setKey: 方法。( key 指成员变量名,首字母大写), setter 方法没找到,如果类方法accessInstanceVariablesDirectly
返回 YES,那么按 _key,_isKey,key,iskey 的顺序搜索成员名。(NSKeyValueCodingCatogery 中实现的类方法,默认实现为返回 YES ),如果没有找到成员变量,调用setValue:forUnderfinedKey:
方法 -
valueForKey
:搜索方式,首先按 getKey,key,isKey 的顺序查找 getter 方法,找到直接调用。如果是 BOOL、int 等内建值类型,会做 NSNumber 的转换。getter 没找到,查找 countOfKey、objectInKeyAtindex、KeyAtindexes 格式的方法。如果 countOfKey 和另外两个方法中的一个找到,那么就会返回一个可以响应 NSArray 所有方法的代理集合的 NSArray 消息方法。如果没找到,查找 countOfKey、enumeratorOfKey、memberOfKey 格式的方法。如果这三个方法都找到,那么就返回一个可以响应 NSSet 所有方法的代理集合。如果还没找到,假如类方法accessInstanceVariablesDirectly
返回 YES。就按 _key,_isKey,key,iskey 的顺序搜索成员名。再没找到,调用 valueForUndefinedKey。 - KVC 运用了 isa-swizzing 技术。isa-swizzing 就是类型混合指针机制。KVC 通过 isa-swizzing 实现其内部查找定位。isa 指针(is kind of)指向维护分发表的对象的类,该分发表实际上包含了指向实现类中的方法的指针和其他数据。
消息转发机制原理?
- 动态方法解析:
对象在接收到未知的消息时,首先会调用所属类的类方法+resolveInstanceMethod:
或者+resolveClassMethod:
。在这个方法中,我们有机会为该未知消息新增一个”处理方法”“。不过使用该方法的前提是我们已经实现了该”处理方法”,只需要在运行时通过 class_addMethod 函数动态添加到类里面就可以了。 - 备用接受者:
动态方法解析无法处理消息,则会走备用接受者。这个备用接受者只能是一个新的对象,不能是 self 本身,否则就会出现无限循环。如果我们没有指定相应的对象来处理 selector,则应该调用父类的实现来返回结果。 - 完整消息转发:
首先创建 NSInvocation 对象,把尚未处理的消息有关的内容封于其中,此对象包括 selector、目标(target 及参数。在触发 NSInvocation 对象时,”消息派发系统“(message-dispatch sys)将会把消息指派给目标对象。若发现某个操作不应该是本类来处理,就需要调用父类的同名方法。这样,继承体系中的每个类都有机会处理此调用请求,直至 NSObject。如果最后调用了 NSObject 的方法,最终该方法就会继续调用doesNotRecognizeSelector:
抛出异常,表明'selector'未能被处理。
理解 weak 属性?
- Runtime 维护了一个 weak 表,用于存储指向某个对象的所有 weak 指针。weak 表其实是一个 hash(哈希)表,Key 是所指对象的地址,Value 是 weak 指针的地址(这个地址的值是所指对象的地址)数组。
- 初始化时:runtime 会调用
objc_initWeak
函数,初始化一个新的 weak 指针指向对象的地址。 - 添加引用时:
objc_initWeak
函数会调用objc_storeWeak()
函数,objc_storeWeak()
的作用是更新指针指向,创建对应的弱引用表。 - 释放时,调用
clearDeallocating
函数。clearDeallocating
函数首先根据对象地址获取所有 weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后从 weak 表中删除并清理对象的记录。
项目中网络层如何做安全处理?
- 尽量使用 https:
https 可以过滤掉大部分的安全问题。https 在证书申请,服务器配置,性能优化,客户端配置上都需要投入精力,所以缺乏安全意识的开发人员容易跳过 https,或者拖到以后遇到问题再优化。https 除了性能优化麻烦一些以外其他都比想象中的简单,如果没精力优化性能,至少在注册登录模块需要启用 https,这部分业务对性能要求比较低。 - 不要传输明文密码:
不知道现在还有多少 app 后台是明文存储密码的。无论客户端,server 还是网络传输都要避免明文密码,要使用hash 值。客户端不要做任何密码相关的存储,hash 值也不行。存储 token 进行下一次的认证,而且 token 需要设置有效期,使用 refreshtoken 去申请新的 token。 - Post 并不比 Get 安全:
事实上,Post 和 Get 一样不安全,都是明文。参数放在QueryString 或者 Body 没任何安全上的差别。在 Http 的环境下,使用 Post 或者 Get 都需要做加密和签名处理。 - 不要使用301跳转:
301跳转很容易被 Http 劫持攻击。移动端 http 使用301比桌面端更危险,用户看不到浏览器地址,无法察觉到被重定向到了其他地址。如果一定要使用,确保跳转发生在 https 的环境下,而且 https 做了证书绑定校验。 - http 请求都带上 MAC:
所有客户端发出的请求,无论是查询还是写操作,都带上MAC(Message AuthenticationCode)。MAC 不但能保证请求没有被篡改(Integrity),还能保证请求确实来自你的合法客户端(Signing)。当然前提是你客户端的 key 没有被泄漏,如何保证客户端 key 的安全是另一个话题。MAC 值的计算可以简单的处理为 hash(request params+key)。带上 MAC 之后,服务器就可以过滤掉绝大部分的非法请求。MAC 虽然带有签名的功能,和 RSA 证书的电子签名方式却不一样,原因是 MAC 签名和签名验证使用的是同一个 key,而 RSA 是使用私钥签名,公钥验证,MAC 的签名并不具备法律效应。 - http 请求使用临时密钥:
高延迟的网络环境下,不经优化 https 的体验确实会明显不如 http。在不具备 https 条件或对网络性能要求较高且缺乏 https 优化经验的场景下,http 的流量也应该使用 AES 进行加密。AES 的密钥可以由客户端来临时生成,不过这个临时的 AESkey 需要使用服务器的公钥进行加密,确保只有自己的服务器才能解开这个请求的信息,当然服务器的 response 也需要使用同样的 AESkey 进行加密。由于 http 的应用场景都是由客户端发起,服务器响应,所以这种由客户端单方生成密钥的方式可以一定程度上便捷的保证通信安全。 - AES 使用 CBC 模式:
不要使用 ECB 模式,记得设置初始化向量,每个 block 加密之前要和上个 block 的密文进行运算。
main() 之前的过程有哪些?
- dyld 开始将程序二进制文件初始化
- 交由 ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
- 由于 runtime 向 dyld 绑定了回调,当 image 加载到内存后,dyld 会通知 runtime 进行处理
- runtime 接手后调用 map_images 做解析和处理
- 接下来 load_images 中调用 call_load_methods 方法,遍历所有加载进来的 Class,按继承层次依次调用 Class 的
+load
和其他 Category 的+load
方法 - 至此 所有的信息都被加载到内存中
- 最后 dyld 调用真正的 main 函数
- dyld 会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点
为什么说 Objective-c 是一门动态的语言?
- Objective-c 是 c 语言的一个子类,所以 Objective-c 是一个静态语言,但 Objective-c 的三大特性之一的多态让其拥有了动态性。
- Objective-c 的动态性,让程序在运行时判断其该有的行为,而不是像 c 等静态语言在编译构建时就确定下来。它的动态性主要体现在3个方面:
1.动态类型:如 id 类型。实际上静态类型因为其固定性和可预知性而使用的特别广泛。静态类型是强类型,动态类型是弱类型,运行时决定接收者。
2.动态绑定:让代码在运行时判断需要调用什么方法,而不是在编译时。与其他面向对象语言一样,方法调用和代码并没有在编译时连接在一起,而是在消息发送时才进行连接。运行时决定调用哪个方法。
3.动态载入。让程序在运行时添加代码模块以及其他资源。用户可以根据需要执行一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。
MVC 和 MVVM,MVP ?
MVC
- view 传送指令到 controller
- controller 完成业务逻辑后,要求 model 改变状态
- model 将新的数据发送到 view,用户得到反馈
- 所有通信都是单向的
MVP
- 将 controller 改名为presenter,同时改变了通信方向
- 各部分之间的通信,都是双向的
- view 与 model 不发生联系,都通过 presenter 传递
- view 不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 presenter 部署所有逻辑
MVVM
- 将 presenter 改名为 viewModel,基本上与 MVP 模式完全一致
- MVVM 采用双向绑定(data-binding):View 的变动,自动反映在 ViewModel,反之亦然
为什么代理要用 weak?代理的 delegate 和dataSource 有什么区别?block 和代理的区别?
- 代理使用 weak 来修饰:
1.为了避免循环引用。
2.当对象释放的时候,系统会对属性赋值 nil,Objective-c 有个特性就是对 nil 对象发送消息也就是调用方法,不会 cash。 - delegate:传递的是事件(even)
- dataSource:传递的是数据
- block 和代理的区别
1.代理更面向过程,block更面向结果
属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic 关键字和 @synthesize 关键字是用来做什么的?
- @property = ivar + getter + setter
- 基本数据 atomic, readwrite, assign
- 对象 atomic, readwrite, strong
- @dynamic 修饰的属性,其 getter 和 setter 方法编译器是不会自动帮你生成,必须自己是实现的。
- @synthesize 修饰的属性,其 getter 和 setter 方法编译器是会自动帮你生成,不必自己实现,且指定与属性相对应的成员变量。
atomic 安全么?
- atomic 原子操作,所谓原子,就是不可再化分,已经是最小的操作单位(所谓操作指的是对内存的读写)
- 一个数据的线程安全,简单点来说就是这块数据即使有多个线程同时读写,也不会出现数据的错乱,内存的最后状态是可预见的
- 在64位的操作系统下,所有类型的指针,包括 void * 都是占用8个字节,以 oc 下的 NSArray * 为例子,如果一个多线程操作这个数据,会有两个层级的并发问题,1、指针本身。2、指针所指向的内存,所以这个数据 array 多线程操作的时候,必须分成两部分来描述,一个是 &array 这个指针本身,另一个则是它所指向的内存 array
-
@property(atomic)NSArray *array
其实修饰的是这个指针,也就是这个8字节内存,跟第二部分数据 n 字节没有任何关系,被 atomic 修饰之后,你不可能随意去多线程操作这个8字节,但是对8字节里面所指向的n字节没有任何限制 - atomic 只对 get 和 set 方法起作用:我们知道,这个8字节里面存储的数据,是 n 字节数据的头地址,如果更改8字节数据的内容,那么最后通过这个指针访问到的数据就会完全不一样
如何令自己所写的对象具有拷贝功能?
- 遵循 NSCopying 协议,并且实现
- (id)copyWithZone:(NSZone *)zone
方法 - 如果让自己的类具备 mutableCopy 方法,必须遵守NSMutableCopying,并实现
- (id)mutableCopyWithZone:(nullable NSZone *)zone
方法
进程和线程的区别?同步异步的区别?并行和并发的区别?
进程和线程的区别
- 进程是资源的分配和调度的一个独立单元,而线程是 CPU 调度的基本单元
- 同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程。
- 进程的创建调用 fork 或者 vfork,而线程的创建调用 pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
- 线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
- 线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
- 线程有自己的私有属性 TCB,线程 id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块 PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
同步和异步的区别
- 同步(synchronous):进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件
- 异步(asynchronous):异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情
并行和并发的区别
- 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这个几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。
互斥:进程间相互排斥的使用临界资源的现象
同步:进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步说明就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。
其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。 - 并行(parallelism):在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。
Designated Initializer ?
- 指定初始化函数对一个类来说非常重要,通常参数也是最多的,试想每次我们需要创建一个自定义类都需要一堆参数,那岂不是很痛苦。便利初始化函数就是用来帮我们解决这个问题的,可以让我们比较的创建对象,同时又可以保证类的成员变量被设置为默认的值。
- 子类如果有指定初始化函数,那么指定初始化函数实现时必须调用它的直接父类的指定初始化函数
- 如果子类有指定初始化函数,那么便利初始化函数必须调用自己的其它初始化函数(包括指定初始化函数以及其他的便利初始化函数),不能调用 super 的初始化函数
能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?
- 不能向编译后得到的类增加实例变量,编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定, runtime 会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理 strong weak 引用,所以不能向存在的类中添加实例变量
- 能向运行时创建的类中添加实例变量,运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是需要在调用 objc_allocateClassPair 之后, objc_registerClassPair 之前,原因同上
给类添加一个属性后,在类结构体里哪些元素会发生变化?
- instance_size: 实例的内存大小
- objc_ivar_list *ivars: 属性列表
runloop 的 mode 是用来做什么的?有几种 mode?
- model 是 runloop 里面的模式,不同的模式下的 runloop 处理的事件和消息有一定的差别,系统默认注册了5个 Mode。
- kCFRunLoopDefaultMode: App 的默认 Mode,通常主线程是在这个 Mode 下运行的。
- UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
- kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
- 5种 model 进行了封装
NSDefaultRunLoopMode
NSRunLoopCommonModes - NStime 对象默认是在 NSDefaultRunLoopMode 下面调用消息的,但是当我们滑动 scrollview 的时候,NSDefaultRunLoopMode 模式就自动切换到UITrackingRunLoopMode 模式下面
isa 指针?
- 对象的isa 指针指向所属的类
- 类的 isa 指针指向了所属的元类(metaclass)
- 元类的 isa 指向了根元类(root metaclass),根元类本身的isa指针指向自己,这样就形成了一个闭环
Objective-c 中向一个 nil 对象发送消息将会发生什么?
- Objective-c 中向 nil 发送消息是完全有效的,只是在运行时不会有任何作用。
- 如果一个方法返回值是一个对象,那么发送给 nil 的消息将返回 0 (nil)。
- 如果方法返回值为指针类型,其指针大小为小于或者等于 sizeof(void*),float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回 0。
- 如果方法返回值为结构体,发送给 nil 的消息将返回 0。结构体中各个字段的值将都是 0。其他的结构体数据类型将不是用 0 填充的。
- 如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。
.a 与 .framework 库的区别?静态库和动态库的区别?
- .a 是一个纯二进制文件,不能直接使用,至少要有 .h 文件配合。
- .framework 中除了有二进制文件之外还有资源文件,可以直接使用。
- 静态库:以 .a 和 .framework 为文件后缀名,链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝。
- 动态库:以 .tbd (之前叫.dylib) 和 .framework 为文件后缀名,链接时不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用(如系统的 UIKit.framework 等),节省内存。
- Apple 不让使用自己的动态库,iOS8 之后虽然可以上传含有动态库的 app,但是 Apple 不仅需要你动态库和 app 的签名一致,而且苹果会在你上架的时候再经过一次AppStore 的签名。
如何让静态库中的 Category 变得可用?
- Objective-C 不会为方法定义 linker symbols,它只会为每一个类定义 linker symbols。如果你使用 category 扩展了一个已经存在的类,那么 linker 不会将已有类的实现跟 category 的实现连接起来,这就导致了调用静态库中 category 中新增加的方法时抛出 selector not recognized 的异常。
- 通过在 Other Linker Flags 添加 -all_load ,它会告诉编译器“对于所有文档中的所有对象文件,不管里面的符号有没有被用到,都载入”,这种方法确实可以,但是会产生比较大的二进制文件。
- 添加 -force_load 和指定的路径,这种方法和 -all_load 很像,不同的是它只使用指定的归档。
- 在 Other Linker Flags 中添加 -ObjC,这个标识告诉编译器“如果你在文档里的对象文件中发现了OC代码,就把它载入“,Category 里当然也有 OC 代码。使用这种方法不会载入任何没有 OC 代码的文件
- 启用 Xcode 里 build setting 中的 PerformSingle-Object PreLink,所有的对象文件都会被合并成一个单文件(这不是真正的链接,所以叫做预链接),这个对象文件(有时被称做主对象文件(masterobject file))被添加到文档中。现在如果主对象文件中的任何符号被认为是“在使用”,整个主对象文件都会被认为在使用,这样它里面的 OC 部分就会被载入了。因为里面的类都被正常符号化了,所以能使从这样的静态库中使用所有的 category。
- 在只有category的源文件里添加Fakesymbol。如果你想在runtime里使用category,一定要确保你以某种方法在编译时引用了fake symbol,这会使得对象文件以及它里面的OC代码被载入。和上面其他的解决方法不一样,这种解决方法可以控制哪些category可以在runtime里被编译后的代码使用(可以通过使用这个符号,使它们被链接并变得可用;也可以不使用这个符号,这样链接器就会忽略它)。
BitCode 的理解。
- Bitcode 是被编译程序的一种中间形式的代码。包含 Bitcode 配置的程序将会在 App store 上被编译(可执行的 64 位或 32 位程序)和链接。
- Bitcode,做的事情是指令集优化,根据你设备的状态去做编译优化,进而提升性能,对包的大小优化起不到什么本质上的作用。
- APP Thining 是由 App Slicing、On Demand Resources和 Bitcode 组成。
- App Slicing:根据你设备型号,生成对应资源的 ipa,以节省空间。
- On Demand Resources:按需加载资源
WKWebView 和 UIWebView 的区别?无痕浏览的实现。
- 在性能、稳定性、功能方面有很大提升,直观体现是内存占用变少。
- 允许 JavaScript 的 Nitro 库加载并使用(UIWebView 中限制)。
- 支持了更多的 HTML5 特性。
- 高达 60fps 的滚动刷新率以及内置手势。
- 将 UIWebViewDelegate 与 UIWebView 重构成了14类与3个协议(详见官方文档)。
- WKWebView的Cookie 存储在 WKWebsiteDataStore中。WKWebsiteDataStore 中存储了包括 cookies、disk、memory caches、WebSQL、IndexedDB 数据库和本地存储等 Web 内容。
//defaultDataStore 是默认选择的存储容器
+ (WKWebsiteDataStore *)defaultDataStore;
//nonPersistentDataStore 会禁止任何数据写入文件系统,可用于无痕浏览
+ (WKWebsiteDataStore *)nonPersistentDataStore;
//可以查看到容器中存储的网站数据的所有种类
+ (NSSet<NSString *> *)allWebsiteDataTypes;
//获取容器中的数据记录
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;
//删除容器中的数据记录
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;
NSURLSession 和 NSURLConnection 的区别?
- NSURLConnection 是 iOS2.0 后推出的,NSURLSession是 iOS7.0 后推出的,用于代替NSURLConnection。
- 下载任务方式:NSURLConnection 下载文件时,先是将整个文件下载到内存,然后再写入到沙盒,如果文件比较大,就会出现内存暴涨的情况。而使用 NSURLSessionUploadTask 下载文件,会默认下载到沙盒中的 tem 文件中,不会出现内存暴涨的情况,但是在下载完成后会把 tem 中的临时文件删除,需要在初始化任务方法时,在 completionHandler 回调中增加保存文件的代码。
- 请求方式的控制:NSURLConnection 实例化对象,实例化开始,默认请求就发送(同步发送),不需要调用 start 方法。而 cancel 可以停止请求的发送,停止后不能继续访问,需要创建新的请求。NSURLSession 有三个控制方法,取消 (cancel)、暂停 (suspend)、继续 (resume),暂停以后可以通过继续恢复当前的请求任务。
- 断点续传实现方式 :NSURLConnection 进行断点下载,通过设置访问请求的 HTTPHeaderField 的 Range 属性,开启运行循环,NSURLConnection 的代理方法作为运行循环的事件源,接收到下载数据时代理方法就会持续调用,并使用 NSOutputStream 管道流进行数据保存。NSURLSession 进行断点下载,当暂停下载任务后,如果downloadTask(下载任务)为非空,调用
cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler
这个方法,这个方法接收一个参数,完成处理代码块,这个代码块有一个 NSData 参数 resumeData,如果 resumeData 非空,我们就保存这个对象到视图控制器的 resumeData 属性中,在点击再次下载时,通过调用[ [self.session downloadTaskWithResumeData:self.resumeData] resume]
方法进行继续下载操作,使用 NSURLSession 进行断点下载更加便捷. - 配置信息:NSURLSession 的构造方法
(sessionWithConfiguration:delegate:delegateQueue)
中有一个 NSURLSessionConfiguration 类的参数可以设置配置信息,其决定了cookie,安全和高速缓存策略,最大主机连接数,资源管理,网络超时等配置。 NSURLConnection 不能进行这个配置,相比较与 NSURLConnection 依赖与一个全局的配置对象,缺乏灵活性而言,NSURLSession 有很大改进。 - NSURLSession 支持 HTTP 2.0
WKWebView 支持 NSURLProtocol 吗
- WKWebView 是支持 NSURLProtocol 拦截的,只是 WebKit.framework还不完善。
WebKit 源码由三大部分组成:
- WebCore:HTML 排版引擎核心,主要包含 Loader,Parser(DOM,Render),Layout,Paint 等模块
- WebKit:移植层,主要包括 GUI,File System,Thread,图片解码等与平台相关的模块
-
JavaScriptCore:JS 虚拟机,主要用于操作 DOM,解析执行 JavaScript 代码
WebKit - WKWebView:网页的渲染与展示,通过 WKWebViewConfiguration 可以进行配置。
- WKWebViewConfiguration:这个类专门用来配置 WKWebView。
- WKPreference:这个类用来进行相关设置。
- WKProcessPool:这个类用来配置进程池,与网页视图的资源共享有关。
- WKUserContentController:这个类主要用来做 native 与JavaScript的交互管理。
- WKUserScript:用于进行 JavaScript 注入。
- WKScriptMessageHandler:这个类专门用来处理 JavaScript 调用 native 的方法。
- WKNavigationDelegate:网页跳转间的导航管理协议,这个协议可以监听网页的活动。
- WKNavigationAction:网页某个活动的示例化对象。
- WKUIDelegate:用于交互处理 JavaScript 中的一些弹出框。
- WKBackForwardList:堆栈管理的网页列表。
- WKBackForwardListItem:每个网页节点对象。
WKWebView 在 WebKit 中的初始化流程:
- 根据配置项 WKWebViewConfiguration 创建新 WKWebView,同时会初始化 WKScrollView 和 WKContentView;
- WKContentView 从进程池 WKProcessPool 中分配 WebProcessProxy 和 WebPageProxy,同时根据当前 Page 初始化 WKBrowsingContextController,提供了大部分交互操作功能;
- WKWebView 在独立于 App Process 进程之外的 Network Process 进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。
一个完整的网络请求代理拦截处理流程:
- WKBrowsingContextController 通过 registerSchemeForCustomProtocol 向 WebProcessPool 注册全局自定义 scheme
- WebProcessPool 使用已注册的 scheme 初始化 Network Process 进程配置,同时设置 CustomProtocolManager,负责把网络请求通过 IPC 发送到 App Process 进程、也接收从 App Process 进程返回的网络响应 response
- CustomProtocolManager 注册了 NSURLProtocol 的子类 WKCustomProtocol,负责拦截网络请求处理
- CustomProtocolManagerProxy 中的 WKCustomProtocolLoader 使用 NSURLConnection 发送实际的网络请求,并将响应 response 返回给 CustomProtocolManager
- 详见NSURLProtocol-WebKitSupport
__weak & __unsafe_unretained 的区别
- __unsafe_unretained: 不会对对象进行retain,当对象销毁时,依然会指向之前的内存空间(野指针)。
- __weak:不会对对象进行retain,当对象销毁时,会自动置为 nil。
NSCatch 和 NSDictionary区别?
- NSCache 在系统内存很低时会自动释放对象。
- NSCache 是线程安全的,在进行多线程操作时,不需要进行加锁。
- NSCatch 可以给对象设置上限,用以限制缓存中的对象总个数。
- NSCache 不会拷贝Key。
实例方法与类方法的区别
实例方法
- 减号 - 开头。
- 只能由对象来调用。
- 对象方法中能访问当前对象的成员变量(实例变量)。
- self 是对象的首地址。
类方法
- 加号 + 开头。
- 只能由类(名)来调用。
- 类方法中不能访问成员变量(实例变量)。
- self 是 Class。
网友评论