iOS开发之-- DNS解析(网络切换的问题解决)
iOS开发之--- NSURLProtocol
最近在项目里由于电信那边发生dns发生域名劫持,因此需要手动将URL请求的域名重定向到指定的IP地址,但是由于请求可能是通过NSURLConnection,NSURLSession或者AFNetworking等方式,因此要想统一进行处理,一开始是想通过Method Swizzling去hook cfnetworking底层方法,后来发现其实有个更好的方法–NSURLProtocol。
NSURLProtocol
NSURLProtocol能够让你去重新定义苹果的URL加载系统 (URL Loading System)的行为,URL Loading System里有许多类用于处理URL请求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSession等,当URL Loading System使用NSURLRequest去获取资源的时候,它会创建一个NSURLProtocol子类的实例,你不应该直接实例化一个NSURLProtocol,NSURLProtocol看起来像是一个协议,但其实这是一个类,而且必须使用该类的子类,并且需要被注册。
使用场景
不管你是通过UIWebView, NSURLConnection 或者第三方库 (AFNetworking, MKNetworkKit等),他们都是基于NSURLConnection或者 NSURLSession实现的,因此你可以通过NSURLProtocol做自定义的操作。
- 重定向网络请求
- 忽略网络请求,使用本地缓存
- 自定义网络请求的返回结果
- 一些全局的网络请求设置
Xcode中统计代码覆盖率
最近在项目里需要在单元测试时统计一下代码覆盖率,其实就是被用来作为测量单元测试的指标,看看测试的代码覆盖到多广的范围,下面介绍一下如何统计单元测试的代码覆盖率:
下载工具
主要采用XcodeCoverage工具,它可以将代码覆盖的结果以html的形式展现,方便我们查看,可以采用pod的方式安装:1
pod 'XcodeCoverage', '~>1.0'
工程配置
pod安装后需要配置工程:
- 设置 Build Settings
需要设置两个地方,找到 “Generate Test Coverage Files”和“Instrument Program Flow”两个选项,分别设置Debug下为YES:
ios runtime浅析(三):Method Swizzling
看到nshipster 的Method Swizzling这篇不错的文章还没翻译,就补充一下,没有逐字翻译,关于associated objects已经有翻译了,大家也可以去了解一下。
method swizzling也许是runtime中最有争议的技术,它的作用就是改变已经存在selector的实现,之所以可以这样是因为方法调用可以在运行时改变:通过改变类的分发表( dispatch table,该表包含selector的名称及对应实现函数的地址)里selector和实现之间的对应关系。
举个例子,比如你想记录一个iOS应用里每个view controller显示的次数:可以在每个view controller添加记录的代码,但这会导致大量的重复代码;通过继承也是一个方法,但需要同时创建UIViewController, UITableViewController, UINavigationController及其它中view controller的子类,同样也会产生许多重复的代码出现。
幸运的是,在UIViewController的category使用method swizzling:
ios runtime浅析(二):消息转发
如果你给一个对象发送它不认识的消息时,系统会抛出一个错误,但在错误抛出之前,运行时会给改对象发送forwardInvocation:消息,同时传递一个NSInvocation对象作为该消息的参数,NSInvocation对象包封装原始消息和对应的参数。你可以实现forwardInvocation:方法来对不能处理的消息做一些默认的处理,以避免程序崩溃,但正如该函数的名字一样,这个函数主要是用来将消息转发给其它对象。每一个对象都从NSObject类继承了forwardInvocation:方法,但在NSObject中,该方法只是简单的调用doesNotRecognizeSelector:,通过重写该方法你就可以利用forwardInvocation:将消息转发给其它对象。
为了转发一个消息,forwardInvocation:需要做的事:
- 确定消息该往哪发
- 同时发送对应的原始参数
消息可以通过invokeWithTarget:方法发送:
1 | - (void)forwardInvocation:(NSInvocation *)anInvocation |
forwardInvocation:方法可以作为无法认识的消息的分发中心,将它们打包发给不同的接受者,或者作为一个中转站将所有消息发往同一个目的地。它可以将一个消息转换成另外的一个消息,或者只是单纯的把消息“吞下”,不对外响应和抛出错误,forwardInvocation:做什么取决于实现者。
利用Charles分析iOS app的网络通信
ios runtime浅析(-):消息传递与动态解析
在object-c中,消息直到运行的时候才会绑定对应的实现,编译器将消息表达式:1
[receiver message]
转换成对objc_msgSend消息传递函数的调用:1
objc_msgSend(receiver, selector)//or objc_msgSend(receiver, selector, arg1, arg2, ...)
该函数包含消息的接受者、函数的名称以及对应的参数。该消息传递函数首先去receiver类寻找selector所对应的实现,接着调用该实现并传递对应的参数,最后返回函数的返回值。objc_msgSend函数由编译器生成的,自己在代码中不能直接调用该函数。
消息传递的关键在于每个类结构都有两个重要的元素:
- 指向父类的指针;
- 类的分发表( dispatch table),该表包含selector的名称及对应实现函数的地址;
消息传递的过程:当给一个对象发送消息的时候,消息传递函数首先根据对象的isa指针找到类的结构,然后在其分发表中寻找对应的selector,如果找到的话就调用对应的实现;如果找不到则会根据super class指针去其父类寻找,如果父类还找不到,会接着去父类的父类中寻找,直到NSObject类为止。
为了加速消息传递的过程,运行时系统会缓存用到过的selector,每个类都有一个单独的cache,它可以缓存继承或自己定义的方法。在根据selector名字搜索分发表之前,消息路由会首先检查receiver类的cache是否已经缓存对应的selector,如果有的话就直接调用对应的实现。