KVO 实现原理?
- 利用
Runtime
动态生成一个子类,并且让instance
对象的isa
指向这个全新的子类 - 当修改
instance
对象的属性时,会调用Foundation
框架的_NSSetXXXValueAndNotify
函数 ,该函数里面会先调用willChangeValueForKey:
然后调用父类原来的setter
方法修改值,最后是didChangeValueForKey:
。didChangeValueForKey 内部会触发监听器(Oberser)的监听方法observeValueForKeyPath:ofObject:change:context:
- 如果需要手动触发
KVO
,只需要手动调用willChangeValueForKey:
和didChangeValueForKey:
两个方法即可。
KVC 实现原理?
setValue:forKey
- 按照
setKey:, _setKey:
顺序查找方法 ,找到了就传递参数,调用方法。 - 如果没有找到,就查看
accessInstanceVariablesDirectly
方法(默认为 YES)的返回值。 - 如果返回值是 YES,那么按
_key,_isKey,key,iskey
的顺序查找成员变量,找到了直接赋值,找不到就调用setValue:forUnderfinedKey:
方法并抛出异常NSUnknownKeyException。 - 如果返回值是 NO,就调用
setValue:forUnderfinedKey:
方法并抛出异常 NSUnknownKeyException。
valueForKey
- 按照
getKey,key,isKey, _key
的顺序查找方法,找到直接调用。 - 如果没有找到,就查看
accessInstanceVariablesDirectly
方法(默认为 YES)的返回值。 - 如果返回值是 YES,那么按
_key,_isKey,key,iskey
的顺序查找成员变量,找到了直接取值,找不到就调用setValue:forUnderfinedKey:
方法并抛出异常NSUnknownKeyException。 - 如果返回值是 NO,就调用
setValue:forUnderfinedKey:
方法并抛出异常 NSUnknownKeyException。
消息转发机制原理?
- 动态方法解析:
对象在接收到未知的消息时,首先会调用所属类的类方法+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 个字节,以 Objective-C 下的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 个 model 。 - 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
扩展了一个已经存在的类,那么linke
r 不会将已有类的实现跟category
的实现连接起来,这就导致了调用静态库中category
中新增加的方法时抛出selector not recognized
的异常。 - 通过在
Other Linker Flags
添加-all_load
,它会告诉编译器“对于所有文档中的所有对象文件,不管里面的符号有没有被用到,都载入”,这种方法确实可以,但是会产生比较大的二进制文件。 - 添加
-force_load
和指定的路径,这种方法和-all_load
很像,不同的是它只使用指定的归档。 - 在
Other Linker Flags
中添加-ObjC
,这个标识告诉编译器“如果你在文档里的对象文件中发现了 Objective-C 代码,就把它载入“,Category 里当然也有 Objective-C 代码。使用这种方法不会载入任何没有 Objective-C 代码的文件 - 启用 Xcode 里 build setting 中的
PerformSingle-Object PreLink
,所有的对象文件都会被合并成一个单文件(这不是真正的链接,所以叫做预链接),这个对象文件(有时被称做主对象文件(masterobject file)被添加到文档中。现在如果主对象文件中的任何符号被认为是“在使用”,整个主对象文件都会被认为在使用,这样它里面的 Objective-C 部分就会被载入了。因为里面的类都被正常符号化了,所以能使从这样的静态库中使用所有的category
。 - 在只有
category
的源文件里添加Fakesymbol
。如果你想在runtime
里使用category
,一定要确保你以某种方法在编译时引用了fake symbol
,这会使得对象文件以及它里面的 Objective-C 代码被载入。和上面其他的解决方法不一样,这种解决方法可以控制哪些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 *)allWebsiteDataTypes;
//获取容器中的数据记录
- (void)fetchDataRecordsOfTypes:(NSSet *)dataTypes completionHandler:(void (^)(NSArray *))completionHandler;
//删除容器中的数据记录
- (void)removeDataOfTypes:(NSSet *)dataTypes forDataRecords:(NSArray *)dataRecords completionHandler:(void (^)(void))completionHandler;
- (void)removeDataOfTypes:(NSSet *)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
代码
- 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
。