阿里面试
iOS面试题:腾讯一、二面以及参考思路
1在ARC下获取oc对象的引用计数器
NSLog(@"retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(str5)));
2autorelease
给对象发送一条autorelease消息,会将对象放到一个自动释放池中
当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
会返回对象本身,调用完autorelease方法后,对象的计数器不变
简述一下自动释放池底层怎么实现?
自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶,当一个对象收到发送autorelease消息时,他被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,他们从栈中被删除,并且会给池子里所有的对象都会做一次release操作
2KVO
假设有两个类A和B,其中B有个属性age(int类型),现在让A类监听B类的age属性变化,当发生变化时打印一句话。
/**
* A对象监听B对象的age属性发生变化
* @param options 值变化
1. NSKeyValueObservingOptionNew:新值、
2. NSKeyValueObservingOptionOld:旧值
* @param event
*/
[B addObserver:A forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
//KVO实现过程,其代码在运行时会创建一个B类的派生类NSKVONotifying_B,并在NSKVONotifying_B类里重写属性age的setter方法
- (void)setAge:(int)age
{
[supper setAge:age];
//并在sette方法里调用这两个方法,当调用了这两个方法,就会通知B类执行那个监听方法
[self willChangeValueForKey:@"age"];
[self didChangeValueForKey:@"age"];
}
//MARK:- 在B类里
/**
* 属性发生改变时执行
*
* @param keyPath 检测的属性 此时是age
* @param object 谁的属性
* @param change 改变(oldValue、newValue)
* @param context
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"监听到%@属性发生改变了,change= %@,context= %@",object,change,context);
}
作者:只敲代码不偷桃
链接:http://www.jianshu.com/p/7d73654ed91a
來源:
KCO的底层实现原理:
KVO是基于runtime机制实现的。当某个类的对象第一次被观察时,系统就会在运行期动态的创建该类的一个派生类,在这个派生类中重写基类的中任何被观察属性的setter方法。派生类在被重写的setter方法实现真正的通知机制(Person -> NSKVONotifying_Person)
4获取某个view所在的控制器
- (UIViewController *)viewController
{ UIViewController *viewController = nil;
//最主要的是这个方法
UIResponder *next = self.nextResponder; while (next)
{ if ([next isKindOfClass:[UIViewController class]])
{
viewController = (UIViewController *)next;
break;
}
next = next.nextResponder;
}
return viewController;
}
5NSArray 快速求总和 最大值 最小值 和 平均值
NSArray *array = [NSArray arrayWithObjects:@"2.0", @"2.3", @"3.0", @"4.0", @"10", nil];
CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
NSLog(@"%f\n%f\n%f\n%f",sum,avg,max,min);
6把MRC转换成ARC
1.把Build Phases -->Complie Sources 下文件的-fno-objc-arc去掉
2.Build Settings 下搜索arc,找到Object-C Automatic Reference Counting把值改为NO;
3.选择Xcode --> Edit --> Conver --> To Object-C ARC
7post和get的区别
一般回答
GET在浏览器回退时是无害的,而POST会再次提交请求。 GET产生的URL地址可以被Bookmark,而POST不可以。 GET请求会被浏览器主动cache,而POST不会,除非手动设置。 GET请求只能进行url编码,而POST支持多种编码方式。 GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。 GET请求在URL中传送的参数是有长度限制的,而POST么有。 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。 GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。 GET参数通过URL传递,POST放在Request body中。
GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
具体请移步
8代理
代理的设计模式,这是iOS中一种消息传递的方式,也可以通过这种方式来传递一些参数。
在ios中,有特定的语法来实现代理模式,OC语言可以通过@Protocol实现协议。
设置委托代理就是代理属性指针指向代理对象,相当于代理对象只是在委托方中调用自己的方法,
代理主要由三部分组成:
协议:用来指定代理双方可以做什么,必须做什么。(协议中的内容一般都是方法列表,当然也可以定义属性)
代理:根据指定的协议,完成委托方需要实现的功能。
委托:根据指定的协议,指定代理去完成什么功能
原理
在iOS中代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。
8单例模式引起的内存泄漏
长生命周期对象持有短生命周期对象,导致短生命周期对象不能回收
9引起的内存泄漏原因
1.单例
2.Block循环引用
3.代理delegate
4.NStimer(当前类销毁执行dealloc的前提是定时器需要停止并滞空,而定时器停止并滞空的时机在当前类调用dealloc方法时,这样就造成了互相等待的场景,从而内存一直无法释放。因此需要注意cleanTimer的调用时机从而避免内存无法释放)
5.C语言代码中的malloc等需要对应free等
6.地图的使用--若项目中使用地图相关类,一定要检测内存情况,因为地图是比较耗费App内存的,因此在根据文档实现某地图相关功能的同时,我们需要注意内存的正确释放,大体需要注意的有需在使用完毕时将地图、代理等滞空为nil,注意地图中标注(大头针)的复用,并且在使用完毕时清空标注数组等
7.、大次数循环内存暴涨问题
创建单例
static Myclass _instance;
方法一:
+(id)shareInstance{
@synchronized(self){
if(_instance == nil)
_instance = [MyClass alloc] init];
}
return _instance;
}
方法二:
+(id)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if(_instance == nil)
_instance = [MyClass alloc] init];
});
return _instance;
}
以上两种方法都是线程安全的.不过苹果官方现在提倡
销毁单例
- 必须把static dispatch_once_t onceToken; 这个拿到函数体外,成为全局的.
+(void)attempDealloc{
onceToken = 0; // 只有置成0,GCD才会认为它从未执行过.它默认为0.这样才能保证下次再次调用shareInstance的时候,再次创建对象.
[_instance release];
_instance = nil;
}
10XIB的布局优先级
11消息推送
12哈希表和哈希冲突怎么解决
原因:
当关键字值域远大于哈希表的长度,而
且事先并不知道关键字的具体取值时。冲突就难免会发 生。另外,当关键字的实际取值大于哈希表的长度时,而且表中已装满了记录,如果插入一个新记录,不仅发生冲突,而且还会发生溢出。因此,处理冲突和溢出是 哈希技术中的两个重要问题
处理:
13NSSet和NSArray的区别
在搜索一个一个元素时NSSet比NSArray效率高,主要是它用到了一个算法hash(散列,也可直译为哈希);
比如你要存储元素A,一个hash算法直接就能直接找到A应该存储的位置;同样,当你要访问A时,一个hash过程就能找到A存储的位置。而对于NSArray,若想知道A到底在不在数组中,则需要便利整个数组,显然效率较低了
NSSet,NSArray都是类,只能添加cocoa对象,如果需要加入基本数据类型(int,float,BOOL,double等),需要将数据封装成NSNumber类型。
14控件响应的事件
触摸事件
加速计事件
远程控制事件
按照时间顺序,事件的生命周期是这样的:
事件的产生和传递(事件如何从父控件传递到子控件并寻找到最合适的view、寻找最合适的view的底层实现、拦截事件的处理)->找到最合适的view后事件的处理(touches方法的重写,也就是事件的响应)
其中重点和难点是:
1.如何寻找最合适的view
2.寻找最合适的view的底层实现(hitTest:withEvent:底层实现)
iOS中的事件的产生和传递
3.1.事件的产生
发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,为什么是队列而不是栈?因为队列的特点是FIFO,即先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。
UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。
3.2.事件的传递
触摸事件的传递是从父控件传递到子控件
也就是UIApplication->window->寻找处理事件最合适的view
注 意: 如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件
应用如何找到最合适的控件来处理事件?
1.首先判断主窗口(keyWindow)自己是否能接受触摸事件
2.判断触摸点是否在自己身上
3.子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
4.view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
5.如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。
具体请移步
14沙盒模型和哪些操作是私有api
每个 iOS 应用程序都有一个单独的文件系统(存储空间),而且只能在对应的文件系统中进行操作,此区域被称为沙盒。所有的非代码文件都要保存在此,例如属性文件 plist、文本文件、图像、图标、媒体资源等。
程序包(NSBundle)
iOS 应用都是通过 bundle 进行封装的,可以狭隘地将 bundle 理解为上述沙盒中的 AppName.app 文件
私有API
私有API是指苹果未公开的一些方法,通常情况下这些方法不允许开发者使用,通常情况是指上架AppStore。私有API可以实现一些开放API不能实现的效果,功能强大,效果非凡。苹果不允许使用,是因为有些私有API会侵犯用户隐私,但使用私有API也并非一定会侵犯用户隐私,这要看开发者怎么用了。
企业级账号发布供内部人使用的APP,可以使用私有API。发布到其他APP平台供越狱手机下载的APP,也有可能使用了私有API。这两者没有苹果审核把关,私有API可以随便使用。苹果明令禁止使用私有API的APP上架AppStore!但凡事无绝对,AppStore上也不乏使用私有API的应用,使用办法有很多,比如热更新。不被苹果发现就行,发现了轻者下架,重者封号。
1.获取iOS设备上安装的应用列表
Class LSAppClass = objc_getClass("LSApplicationWorkspace");
NSObject workspace = [LSAppClass
performSelector:@selector(defaultWorkspace)];
NSArray appsArray = [workspace
performSelector:@selector(allApplications)];
2.获取获取APP图标也是私有的api
3.打开APP
在iOS 9以后要想打开其他app需要添加URL Scheme,设置白名单,否则将无法打开,白名单的上限为50个。上文中我们可以获取APP的Bundle Id,依靠Bundle Id使用私有API可以打开其他APP,并没有数量限制。
Class LSAppClass = NSClassFromString(@"LSApplicationWorkspace");
id workSpace = [ (id)LSAppClass
performSelector:@selector(defaultWorkspace)];
[workSpace
performSelector:@selector(openApplicationWithBundleID:) withObject:self.appsObj.bundleId
];
具体请移步
15ios保存数据
1.NSKeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法。前一个方法告诉系统怎么对对象进行编码,而后一个方法则是告诉系统怎么对对象进行解码。
2.NSUserDefaults:用来保存应用程序设置和属性、用户保存的数据。用户再次打开程序或开机后这些数据仍然存在。
- Write写入方式:永久保存在磁盘中。具体方法为
- SQLite:采用SQLite数据库来存储数据。SQLite作为一中小型数据库
16TCP的3次握手
两次握手可以么?
TCP连接时是三次握手,那么两次握手可行吗?
在《计算机网络》中是这样解释的:已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送ACK包。这样就会白白浪费资源。
而经过三次握手,客户端和服务器都有应有答,这样可以确保TCP正确连接。
为什么TCP连接是三次,挥手确是四次?
在TCP连接中,服务器端的SYN和ACK向客户端发送是一次性发送的,而在断开连接的过程中,B端向A端发送的ACK和FIN是是分两次发送的。因为在B端接收到A端的FIN后,B端可能还有数据要传输,所以先发送ACK,等B端处理完自己的事情后就可以发送FIN断开连接了。
为什么在第四次挥手后会有2个MSL的延时?
前文说到
MSL是Maximum Segment Lifetime,最大报文段生存时间,2个MSL是报文段发送和接收的最长时间。
假定网络不可靠,那么第四次发送的ACK可能丢失,即B端无法收到这个ACK,如果B端收不到这个确认ACK,B端会定时向A端重复发送FIN,直到B端收到A的确认ACK。所以这个2MSL就是用来处理这个可能丢失的ACK的。
17内存的几大区
1.栈区--存放程序中的临时变量,程序运行时由编译器自动分配,
2.栈区--
3.全局区(bss段)--编译器编译时即分配内存。全局变量和静态变量的存储是放在一块的
4.常量区--常量字符串
5.代码段-- 2进制代码
完整解释请移步
19多线程的理解
18runloop理解
20同步,异步,串行,并行
同步异步是对队列
串行和并行是对线程
GCD 将任务添加到队列并指定队列的执行函数
---在使用GCD的时候,我们会把需要处理的任务放到Block中,然后将任务追加到相应的队列里面,这个队列,叫做Dispatch Queue。然而,存在于两种Dispatch Queue,一种是要等待上一个执行完,再执行下一个的Serial Dispatch Queue,这叫做串行队列;另一种,则是不需要上一个执行完,就能执行下一个的Concurrent Dispatch Queue,叫做并行队列。这两种,均遵循FIFO(先进先出)原则。
21app反编译
危害:
1、内购破解iOS应用需防反编译风险之一:插件法(仅越狱)、iTools工具替换文件法(常见为存档破解)、八门神器修改
2、网络安全风险iOS应用需防反编译风险之二:截获网络请求,破解通信协议并模拟客户端登录,伪造用户行为,对用户数据造成危害
3、应用程序函数PATCH破解iOS应用需防反编译风险之三:利用FLEX 补丁软件通过派遣返回值来对应用进行patch破解
4、源代码安全风险iOS应用需防反编译风险之四:通过使用ida等反汇编工具对ipa进行逆向汇编代码,导致核心代码逻辑泄漏与被修改,影响应用安全
5、面对这些iOS应用存在的风险,iOS应用如何防止被反编译,下面看下iOS应用加密技术
22app的优化的方向
1.首页启动速度
启动过程中做的事情越少越好(尽可能将多个接口合并)
不在UI线程上作耗时的操作(数据的处理在子线程进行,处理完通知主线程刷新节目)
在合适的时机开始后台任务(例如在用户指引节目就可以开始准备加载的数据)
尽量减小包的大小
优化方法:
量化启动时间
启动速度模块化
辅助工具(友盟,听云,Flurry)
2.页面浏览速度
json的处理(iOS 自带的NSJSONSerialization,Jsonkit,SBJson)
数据的分页(后端数据多的话,就要分页返回,
例如网易新闻,或者 微博记录)
数据压缩(大数据也可以压缩返回,减少流量,
加快反应速度)
内容缓存(例如网易新闻的最新新闻列表都是要
缓存到本地,从本地加载,可以缓存到内存,或者
数据库,根据情况而定)
延时加载tab(比如app有5个tab,可以先加载第一
个要显示的tab,其他的在显示时候加载,按需加
载)
算法的优化(核心算法的优化,例如有些app
有个 联系人姓名用汉语拼音的首字母排序)
3.操作流畅度优化
Tableview 优化(tableview cell的加载优化)
ViewController加载优化(不同view之间的跳转,可以提前准备好数据)
4.数据库的优化
数据库设计上面的重构
查询语句的优化
分库分表(数据太多的时候,可以分不同的表或者库)
5.服务器端和客户端的交互优化
服务端尽量做多的逻辑处理
服务器端和客户端采取推拉结合的方式(可以利用一些同步机制)
通信协议的优化。(减少报文的大小)
电量使用优化(尽量不要使用后台运行
6.非技术性能优化
产品设计的逻辑性(产品的设计一定要符合逻辑,或者逻辑尽量简单,否则会让程序员抓狂,有时候用了好大力气,才可以完成一个小小的逻辑设计问题)
界面交互的规范(每个模块的界面的交互尽量统一,符合操作习惯)
代码规范(这个可以隐形带来app 性能的提高,比如 用if else 还是switch ,或者是用!还是 ==)
code review(坚持code Review 持续重构代码。减少代码的逻辑复杂度)
日常交流(经常分享一些代码,或者逻辑处理中的坑)
23tableView的优化
遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?
可能造成tableView卡顿的原因有:
缓存行高
不要动态的创建子视图- 所有的子视图必须要有北京颜色
所有的颜色都不要使用alpha
cell 的 栅格化和 cell的异步绘制cell的
子视图添加到contView上
1.最常用的就是cell的重用, 注册重用标识符
如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell;
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell
2.避免cell的重新布局
cell的布局填充等操作 比较耗时,一般创建时就布局好
如可以将cell单独放到一个自定义类,初始化时就布局好
3.提前计算并缓存cell的属性及内容
当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度
而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell
4.减少cell中控件的数量
尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,
不适用的可以先隐藏
5.不要使用ClearColor,无背景色,透明度也不要设置为0
渲染耗时比较长
6.使用局部更新
如果只是更新某组的话,使用reloadSection进行局部更新
7.加载网络数据,下载图片,使用异步加载,并缓存
8.少使用addView 给cell动态添加view
9.按需加载cell,cell滚动很快时,只加载范围内的cell
10.不要实现无用的代理方法,tableView只遵守两个协议
11.缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这两者同时存在才会出现“窜动”的bug。所以我的建议是:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可
12.不要做多余的绘制工作。
在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。
13.预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;
14.使用正确的数据结构来存储数据。
24.1容错处理
1.对数据NSArray,字典,野指针,NSNull
24.2如果项目开始容错处理没做?如何防止拦截潜在的崩溃
1.写分类重写方法替换原来方法
2.运行时替换方法
3.利用异常的捕获来防止程序的崩溃,并且进行相应的处理。
总结:
1、不要过分相信服务器返回的数据会永远的正确。
2、在对数据处理上,要进行容错处理,进行相应判断之后再处理数据,这是一个良好的编 程习惯。
25耗电量优化
一.代码层面
合理使用NSDateFormatter 和 NSCalendar这种高开销对象
性能测试表明,NSDateFormatter的性能瓶颈是由于NSDate格式到NSString格式的转化,所以把NSDateFormatter创建单例意义不大.推荐的做法是,把最常用到的日期格式做缓存.
static NSDateFormatter *cachedDateFormatter = nil;
+ (NSDateFormatter *)cachedDateFormatter {
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat: @“YYYY-MM-dd HH:mm:ss”];
}
return dateFormatter;
}
2.不要频繁的刷新页面,能刷新1行cell最好只刷新一行,尽量不要使用reloadData.
3.选择正确的集合
NSArray,使用index来查找很快(插入和删除很慢)
字典,使用键来查找很快
NSSets,是无序的,用键查找很快,插入/删除很快
4.少用运算获得圆角,不论view.maskToBounds还是layer.clipToBounds都会有很大的资源开销,必须要用圆角的话,不如把图片本身就做成圆角
5.懒加载,不要一次性创建所有的subview,而是需要时才创建.
7.图片处理
图片与imageView相同大小,避免多余运算
可以使用整副的图片,增加应用体积,但是节省CPU
可调大小的图片,可以省去一些不必要的空间
CALayer,CoreGraphics,甚至OpenGL来绘制,消耗CPU
8.cache,cache,cache(缓存所有需要的)
服务器相应结果的缓存(图片)
复杂计算结果的缓存(UITableView的行高)
9.尽量少用透明或半透明,会产生额外的运算.
10.使用ARC减少内存失误,dealloc需要重写并对属性置为nil
11.避免庞大的xib,storyBoard,尽量使用纯代码开发
CPU层面
1.Timer的时间间隔不宜太短,满足需求即可
2.线程适量,不宜过多,不要阻塞主线程
3.优化算法,减少循环次数
4.定位和蓝牙按需取用,定位之后要关闭或降低定位频率
26了解HTTP协议吗
HTTP协议的主要特点可概括如下:
1.支持客户/服务器模式。
2.简单快速:
客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:
HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4.无连接:
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:
-HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
具体请移步
27缓存策略
1.NSCache
28GET网络请求缓存
GET网络请求缓存
概述
首先要知道,POST请求不能被缓存,只有 GET 请求能被缓存。因为从数学的角度来讲,GET 的结果是 幂等 的,就好像字典里的 key 与 value 就是幂等的,而 POST 不 幂等 。缓存的思路就是将查询的参数组成的值作为 key ,对应结果作为value。从这个意义上说,一个文件的资源链接,也叫 GET 请求,下文也会这样看待。
80%的缓存需求:两行代码就可满足
设置缓存只需要三个步骤:
第一个步骤:请使用 GET 请求。
第二个步骤:
如果你已经使用 了 GET 请求,iOS 系统 SDK 已经帮你做好了缓存。你需要的仅仅是设置下内存缓存大小、磁盘缓存大小、以及缓存路径。甚至这两行代码不设置也是可以的,会有一个默认值。代码如下:
NSURLCache *urlCache = [[NSURLCache alloc]
initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:urlCache];
29GCD定时器
Xcode
只能提示 输入dispatch_sources
其实 GCD 就是一种更底层的API 不依赖RunLoop 更准确