1、category和extension的区别
- category是分类,可以为类增加自定义方法
- extension是扩展或者延展,能为类增加属性、成员变量和方法
- 区别呢就是category不能增加属性和成员变量,而extension可以
- 实际项目中,category一般用于增加系统类的方法以方便使用,因为用该系统类的成员变量可以直接调用,还可以在内部修改该变量,如NSString+MD5;extension一般用于扩展自定义类的属性或成员变量,经常见的ViewController的.m文件中,我们增加的私有成员变量
- 如果非要给category添加成员变量也不是不可,用runtime吧
objc_setAssociatedObject
2、define 和 const常量的区别
- define宏定义在预编译阶段就进行替换;而const常量则在编译阶段被编译
- define不会检测数据类型只是替换,易导致错误;const参与编译,会检测数据类型,较为安全
- define定义的常量在替换后运行过程中会不断地占用内存,而const定义的常量存储在数据段只有一份copy,效率更高
- define可以定义一些简单的函数如 #define sum(a) (a+a),const不可以;
3、static关键字的作用
一句概括,限制变量和函数的作用域
- 函数(方法)体内 static 变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
- 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
- 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;
- 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
- 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量
4、 堆和栈的区别
- 从数据存储方面讲,栈用于存放基本数据类型,对象的地址等;堆用于于存放对象类型,block的copy等
- 从分配空间大小看,栈分配的空间小;堆分配的空间大
- 从管理角度看,栈由编译器自动管理,无需我们手工控制;堆的释放需要手动控制,容易造成内存泄漏,因此为什么堆是需要内存管理的。
5、OC的内存管理机制
- ARC: Automatic Referance Count,通过retainCount引用计数来判断对象是否应该被释放,每次 runloop 的时候,都会检查自动对象的 retainCount,如果retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了。系统会在编译的时候,在代码之间插入类似内存管理的代码,判断retainCount是该+1 还是-1,合理控制retainCount的计数
- Autorelease pool:自动释放池,程序中所有用autoreleased释放的对象都会加入到Autorelease pool中,Autorelease pool会在线程结束的时候drain,这时Autorelease pool中的所有对象都会被release一次
- 内存管理的问题: 尽管ARC自动引用计数帮助解决了MRC手动管理内存的问题,但是ARC下还是会存在内存问题,如 1、循环引用会导致内存泄漏 2、Core Foundation和OC框架下的对象进行桥接的时候,处理不当也会引起内存泄漏 3、Core Foundation框架下的对象对象不受ARC管理,需要开发者手动释放,存在安全隐患
6、weak和assign的区别
- weak只能修饰引用类型,而且是弱引用类型,比如会出现循环引用的delegate代理属性,delegate代理属性也可以用assign修饰
- assign本质上既可以修饰基本数据类型,也可以修饰引用类型,但是实际使用只用来修饰基本数据类型。
- 不同点:weak修饰引用类型,定义了一种“非拥有关系”,会在对象释放之后,将对象置为nil,OC中向nil对象发送消息不会引起崩溃;而assign修饰引用类型,不会在对象释放之后置为nil,会造成野指针;
7、使用automic一定是线程安全的吗?
不是,automic原子属性,只是在对象的setter和getter方法中是线程安全的,例如self.arr = array 线程安全,但是[self.arr objectAtIndex:3] 就不是线程安全的,需要的话只能另外加锁
8、 在有了自动生成属性变量之后,@synthesize还有什么用处
- 在没有自动生成属性变量之前,我们必须用@systhesize才能生成对应的setter和getter方法以及属性变量
- 既然要用@synthesize,证明此情况下不能自动生成属性变量,这些情况包含如下:
1、同时重写setter和getter方法时
2、只读变量 重写getter方法时
3、在 @protocol 中定义属性
4、在category中定义属性 - 以上方法都不会自动生成属性变量,此时就需要@systhesize name = _name
9、copy关键字什么时候用
- 对于不可变集合,使用copy只是对原对象的引用;对于可变集合,使用copy则是重新分配了一块新的内存,与原对象毫无关系
- 实际项目中,对于NSString、NSDictionary、NSArray都用copy,而可变集合NSMutableString、NSMutableArray使用copy,进行一次内容的拷贝,内容改变不会影响原对象的数据
- block使用copy,只是MRC延续下来的习惯,在MRC中,block默认创建在栈区,使用copy则可以把它放到堆区;在ARC中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但是建议写上copy,因为这样显示告知调用者“编译器会自动对 block 进行了 copy 操作”
10、对于不可变集合使用copy只是对原对象的引用,那为什么不用strong呢
- 不可变集合如NSArray用copy只是增加对象的引用,不会影响到对象的内容,不论接收者是可变还是不可变,持有的就是原对象的一个副本
- 如果换成是用strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
11、关于复制copy和mutableCopy
浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。
非集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //深复制
[可变对象 copy] //深复制
[可变对象 mutableCopy] //深复制
- 集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //单层深复制
[可变对象 copy] //单层深复制
[可变对象 mutableCopy] //单层深复制
这里需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制
如:@property(nonatomic, copy)NSMutableArray *arr;这个写法会出什么问题?
1、添加、删除、修改数组元素的时候,程序会因为找不到对应的方法而崩溃;
2、copy后返回的复制得到的不可变对象,即NSArray,NSArray类型对象不能调用NSMutableArray类型对象的方法
原因:是因为copy就是复制一个不可变NSArray的对象,不能对NSArray类型的对象进行添加删除修改
12、+(void)load; +(void)initialize;有什么用处?
- load方法
1、当类对象被导入项目时,runtime会向每一个类对象发送load消息
2、load方法在类或者分类被引入时仅调用一次;调用顺序是父类、子类、分类
3、load方法不会被类自动继承
4、因为load方法是在导入类的时候就被调用且一次,所以在load方法中我们可以做一些runtime的操作或者希望只执行一次的操作 - initiallize方法
在第一次使用这个类的时候被调用一次,也就是懒加载 - 总结
1、 在OC中,runtime都会自动调用每个类的这两个方法
2、load是在类初始加载的时候调用;initiallize是在第一次调用该类方法或者实例方法的时候被调用
3、共同点: 只有在实现的前提下才会被调用; 只会调用一次\
13、addObserVer: keyPath: options: context: ,KVO的实现原理
- observer,观察者;keyPath,要观察的属性或成员变量;options:观察值的选项(新值、旧值还是都观察);context传入的参数; KVO是基于runtime实现的
- 在类的某个属性被第一次观察的时候,系统会在runtime时期动态的创建一个该类的派生类如NSKVONotifing_Persion,在该派生类中,会重写被观察属性的setter方法,该setter方法中,派生类实现了真正的通知机制
- OC中每个类对象都会有一个isa指针指向该类,当该类的属性第一次被观察,系统会将该类的isa指针去指向派生类,从而属性值改变调用setter的时候会调用派生类的setter方法
- 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
14、 什么是block
- block是一个匿名函数,也是一个代码块,本质上也是OC中的对象,底层也是结构体
- block用于回调,是一个不需要立即执行的代码块,可以控制执行时机
- block属性用copy修饰,使用中要注意避免循环引用
+block的注意点:
1、block内部使用外部指针会造成循环引用,需要用__weak修饰外部指针;
__weak typeOf(self) weakSelf = self;
2、block内部如果调用了延时函数还是用弱指针会取不到该指针,因为已经被销毁了,需要在block内再将弱指针强引用一下
__strong typeOf(self) strongSelf = weakSelf;
3、如果需要在block内部修改外部栈区变量,需要用__block修饰外部变量。
15、block要用copy修饰,还是用strong
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,
因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。
16、KVC 和KVO
KVC,即NSKeyValueCoding兼职编码, 一种非正式的protocol,提供了一种机制来间接访问对象的属性,而不是通过setter和getter方法 ,以key为例,当然还有keyPath
1、在使用KVC去访问属性变量的时候,系统首先是查找该类的setter和getter方法;
2、如果没有找到setter和getter方法,就会找key对应的带下划线的属性或成员变量;
3、如果没有带下划线的属性或成员变量,就找不带下划线的属性或成员变量
4、如果不带下划线的属性和成员变量也没有,就会执行setValue:(id)value forUndefinedKey或者valueForUndefinedKey,如果该类没有实现对应的方法,就会导致崩溃KVO,KVC是KVO的基础,通过键值路径keypath观察对象的某个属性或成员变量,在KVC赋值事件发生的时候,KVO的观察者就会发起内部的通知机制,详细看编号13
17、设计模式是什么? 你知道哪些设计模式,并简要叙述
- 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情
1、MVC模式: Model View Control,把模型 视图 控制器层进行解耦和编写;
2、MVVM模式:Model View ViewModel,把模型 视图 业务逻辑 层进行解耦和编写;
3、单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次;
4、观察者模式KVO: KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者;
5、委托模式:代理+协议的组合,实现一对一的反向传值操作;
6、工厂模式:通过一个类方法,批量的根据已有模板生产对象。
18、#import 、 #include 、 @class有什么区别 ? #import<> 和#import" " 有什么区别
-
import是OC导入头文件的关键字,#include是C\C++导入头文件的关键字;使用#import导入头文件会自动只导入一次,不会重复导入
- @class是OC中告诉编辑器某个类的声明,不会立即去查看这个类的实现,只有执行时才回去查看类的实现,可以解决头文件的相互包含
-
import<> 用于导入系统头文件, #import "" 用于导入自定义类头文件
19、@property的本质是什么?ivar、getter、setter是如何生成并添加到这个类中的
- @property的本质 = ivar + getter + setter,“属性”有两大概念:ivar(实例变量) 、 getter+setter(存取方法)
- “属性”(property)作为OC的一项特性,主要的作用就在于封装对象的数据。OC对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过存取方法来访问,其中,“获取方法”(getter)用于读取变量值,“设置方法”(setter)用于写入变量值。
20、属性关键字assign、retain、copy、nonatomic各是什么作用,在哪种情况下使用?
- assign是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时,assign用于基本数据类型;
- retain(MRC)/strong(ARC)表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retainCount会加1;
- copy表示拷贝特性。setter方法会将传入对象复制一份,需要完全一份新的变量时
- nonatomic表示非原子特性,决定编译器生成的setter和getter方法是否是原子操作,atomic表示多线程安全,一般使用nonatomic,效率高。
21、如何让自己的类用copy修饰符?如何重写带copy关键字的setter?
- 需要实现NSCoping协议,如果自定义的对象分为可变和不可变版本,那么就要同时实现NSCoping和NSMutableCoping协议。
具体步骤:
1、遵循NSCoping协议
2、实现协议方法copyWithZone
22、ViewController的生命周期
- 按照执行顺序排列
1、initWithCoder: 通过nib文件初始化时触发
2、awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象
3、loadView: 开始加载试图控制器自导的view
4、viewDidLoad:试图控制器的view加载完成
5、viewWillAppear:试图控制器的view将要显示在window上
6、updateViewConstraints:试图控制器的view开始更新autoLayout约束
7、viewWillLayoutSubviews:试图控制器的view将更新内容视图的位置
8、viewDidLayoutSubviews:试图控制器的view已经更新完使徒的位置
9、viewDidAppear:试图控制器的view已经展示到window上
10、viewWillDisappear:试图控制器的view将从window上消失
11、viewWillDisappear:试图控制器的view已经从window上消失
23、为什么NSArray、NSDictionary、NSString经常使用copy关键字
- 是因为他们有对应的可变类型NSMutableArray、NSMutableDictionary、NSMutableString,可变与不可变之间可能会进行赋值操作,如果不可变赋值给可变对象,那可变对象修改数据可能会影响到原来的不可变对象,如果用copy的话,不管怎么赋值,都是原对象copy了一份,被赋值对象作何修改不会影响到原对象
24、谈一下OC的反射机制
- class反射
1、通过类名的字符串形式实例化对象
Class class = NSClassFromString(@"Person");
Person *person = [[class alloc]init];
person.name = @"lisa";
2、将类名变为字符串
NSString *personStr = NSStringFromClass([Person class]);
- SEL反射
1、通过方法的字符串实例化方法
SEL selector = NSSelectorFromString(@"testSel");
2、方法变成字符串
SEL sel = @selector(testSel);
NSString *str = NSStringFromSelector(sel);
25、什么是谓词?
- 谓词就是通过NSPredicate给定的逻辑条件作为约束条件,完成对数据的筛选
NSArray *persons = @[person1,person2,person3];
//定义谓词, 设置过滤条件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age>%d",30];
//通过谓词条件过滤数组中的元素,过滤之后返回查询结果
NSArray *arr = [persons filteredArrayUsingPredicate:predicate];
26、isa指针问题
- isa: 是一个Class类型的指针,每个实例对象都有一个isa指针,指向其所对应的类,而类Class里也有个isa指针,指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,会先从本身查找类方法的实现,如果没有,元类会向它父类查找该方法。需要注意的是,元类也是类,他也是对象。元类也有isa指针,指向的是根元类(root meteClass),根元类的isa指针指向本身,这样形成了一个封闭的内循环。
27、如何修改一个类的私有属性
- KVC: setValue:属性值 forKey:属性名
- runtime: object_setIvar(self, 属性名, 属性值)
28、isKindOfClass isMemberOfClass selector
- isKindOfClass: 判断某个对象是否属于该类型或者继承自该类型
- isMemberOfClass: 判断某个对象是否是该类型
- selector 通过方法名获取该函数在内存中的入口
29、delegate和NSNotification的区别
- 两者都用于传递消息,不同之处在于一个是一对一的,一个是一对多的
- Notification需要维护一个数组,实现一对多的消息的转发
- delegate需要两者之间建立联系,不然没法调用代理的方法;Notification不需要两者建立联系
30、iOS中常用的数据存储方式
数据存储有四种方案:NSUserDefault、KeyChain、file(文件存储)、DB(数据库存储)
file包括:plist、Archive(归档)
DB包括:SQLite、FMDB、CoreData
31、iOS的沙盒目录结构
- Application:存放程序源文件
- Documents:常用目录,iCloud备份目录,存放数据。
- Library :
1、Caches:存放体积大又不需要备份的数据。(常用的缓存路径)
2、Preference:设置目录,iCloud会备份设置信息 - tmp:存放临时文件,不会备份,而且这个文件下的数据有可能随时被清除。
32、iOS中的几种多线程实现方案
- pthread:适用于Unix、Linux、Windos等系统,跨平台可移植,线程生命周期由程序员管理
- NSThread:面向对象,可直接操作线程对象,线程生命周期由程序员管理
- GCD: 充分利用设备的多核,基于C语言底层的多线程API,线程生命周期系统自动管理
- NSOperation:基于GCD,更加面向对象,线程生命周期系统自动管理
33、简明扼要的说一下runloop
runloop也叫做运行循环,循环内部处理着各种事务。一个线程对应一个runloop,基本作用就是保持线程的持续运行,处理线程中的各种事件。通过runloop,可以让线程在有事件的时候处理事件,没事件的时候休息,可以节省cpu资源,提高运行效率;
34、什么是runtime
- runtime是OC语言的重要特性,叫做运行时,是一套底层的C语言的API,平时编写的OC代码,底层都是用他来实现的
35、Runtime实现的机制是什么?怎么用,一般用于干什么?
- Runtime是iOS运行时特性实现的基础
1、使用时需导入头文件 <objc/message.h> <objc/runtime.h>
2、Runtime 运行时机制,它是一套C语言库。
3、实际上我们编写的所有OC代码,最终都是转成了runtime库的东西。比如:1)OC中的类转成了Runtime库里的结构体等数据类型 2)方法转成了Runtime库里C语言函数 3)方法的调用都是Runtime库里通过objc_msgSend,OC是动态语言,每个方法在运行时都会动态转化为消息发送,objc_msgSend(receiver, selector)
4、由此,可以说Runtime是OC的底层实现,是OC的幕后执行者
+Runtime能做什么
- Runtime库里包含了和类、成员变量、方法相关的API。比如:
1)获取类中的所有成员变量
2)获取类中的所有方法
3)为类动态添加成员变量
4)为类动态添加新的方法 - 什么是 Method Swizzle(黑魔法),什么情况下会使用?
1)在没有一个类的方法的实现源码的函数,想要修改它,除了通过继承和category之外,还可以通过Method Swizzle
2)每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP就类似于函数指针,指向方法的真正实现
3)在OC中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以实现在运行时偷换selector对应的方法实现。
//根据 SEL 获取到 Method
Method method1 = class_getClassMethod(self, @selector(function1));
Method method2 = class_getClassMethod(self, @selector(function2));
//根据SEL 获取到 方法的实现 IMP
IMP imp = class_getMethodImplementation(self, @selector(function3));
//修改方法Method 的实现IMP
method_setImplementation(method1, imp);
//为类添加方法
class_addMethod(self, @selector(function3), imp, "");
//交换两个方法的实现,传入的是Method,实际交换的是IMP
method_exchangeImplementations(method1, method2);
IMP imp2 = class_getMethodImplementation(self, @selector(function2));
//替换SEL对应的实现IMP
class_replaceMethod(self, @selector(function3), imp2, "");
- _objc_msgForward 函数是做什么的
_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
36、HTTP协议中 POST 方法和 GET 方法有那些区别?
- GET方法用于请求数据,POST方法用于提交数据;
- GET请求,参数拼接在访问路径url上,安全性不高;POST请求参数放在请求体body里面,参数与访问路径url分开,较为安全;
- GET请求,访问路径url有长度限制,不超过255个字节,而POST请求访问路径长度没有限制
37、简述APNS发送系统消息即推送的机制
1)应用程序在通知中心注册推送,由iOS系统向APNS请求返回设备令牌device Token;
2)应用程序接收到APNS返回的设备令牌device Token,发送给自己的服务器;
3)服务器把需要推送的内容和设备令牌device Token,发送给APNS
4)APNS通过device Token找到要发送的设备,iOS系统根据APPID把推送内容展示
38、方法和选择器(Selector)的区别
选择器Selector是方法的名字,通过它可以找到方法;
方法是相对对象来说的,包含方法的名字和实现。
39、[self class] 和 [super class]输出同样的结果-当前类Son,[super class]输出的不是父类Father,为什么?
#import "Father.h"
@interface Son : Father
@property(nonatomic,assign)int age;
@end
@implementation Son
-(instancetype)init{
if (self = [super init]) {
NSLog(@"[self class]=====%@",[self class]); //Son
NSLog(@"[super class]=====%@",[super class]); //Son
}
return self;
}
@end
self 表示当前这个类的对象,而 super 是一个编译器标示符,和 self 指向同一个消息接受者。在本例中,无论是[self class]还是[super class],接受消息者都是Son对象,但super与self不同的是,self调用class方法时,是在子类Son中查找方法,而super调用class方法时,是在父类Father中查找方法。
当调用[self class]方法时,会转化为objc_msgSend函数,这个函数定义如下:
id objc_msgSend(id self, SEL op, ...)
这时会从当前Son类的方法列表中查找,如果没有,就到Father类查找,还是没有,最后在NSObject类查找到。我们可以从NSObject.mm文件中看到- (Class)class的实现:
- (Class)class {
return object_getClass(self);
}
所以NSLog(@"%@", NSStringFromClass([self class]));会输出Son。
当调用[super class]方法时,会转化为objc_msgSendSuper,这个函数定义如下:
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
objc_msgSendSuper函数第一个参数super的数据类型是一个指向objc_super的结构体,从message.h文件中查看它的定义:
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
结构体包含两个成员,第一个是receiver,表示某个类的实例。第二个是super_class表示当前类的父类。
这时首先会构造出objc_super结构体,这个结构体第一个成员是self,第二个成员是(id)class_getSuperclass(objc_getClass("Son")),实际上该函数会输出Father。然后在Father类查找class方法,查找不到,最后在NSObject查到。此时,内部使用objc_msgSend(objc_super->receiver, @selector(class))去调用,与[self class]调用相同,所以结果还是Son。
第三方框架
- AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有以下类:
1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变之后,这个工具类就可以检测到。
4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式
(AFJSONRequestSerializer).使用不多。
6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多:
7). AFJSONResponseSerializer; JSON解析器,默认的解析器.
8). AFHTTPResponseSerializer; 万能解析器; JSON和XML之外的数据类型,直接返回二进制数据.对服务器返回的数据不做任何处理.
9). AFXMLParserResponseSerializer; XML解析器;
- 描述下SDWebImage里面给UIImageView加载图片的逻辑
内存 ——> 沙盒 ——> 网络
SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最常用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载图片的过程大致如下:
1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
2.如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
3.如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
4.下载后的图片会加入缓存中,并写入磁盘中
5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
SDWebImage原理:
调用类别的方法:
- 从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。
- 从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。
- 从网络上获取,使用,缓存到内存,缓存到沙盒。
待续。。。。。。