美文网首页iOS面试
iOS面试之基础部分

iOS面试之基础部分

作者: 大猿媛 | 来源:发表于2017-07-14 12:19 被阅读32次

    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原理:
    调用类别的方法:

    1. 从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。
    2. 从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。
    3. 从网络上获取,使用,缓存到内存,缓存到沙盒。

    待续。。。。。。

    相关文章

      网友评论

      • Heap:基本数据类型放在栈里面?

      本文标题:iOS面试之基础部分

      本文链接:https://www.haomeiwen.com/subject/xhhwhxtx.html