OC面试题 九、三方库相关

254 阅读9分钟

AFNetworking

AFNetworking底层实现

AFNetworking是封装的NSURLSession的网络请求。

AFNetworking由五个模块组成: 分别由NSURLSession,Security,Reachability,Serialization,UIKit五部分组成

  • NSURLSession网络通信模块(核心模块) 对应 AFNetworking中的 AFURLSessionManager和对HTTP协议进行特化处理的AFHTTPSessionManager,AFHTTPSessionManager是继承于AFURLSessionmanager的。

  • Security网络通讯安全策略模块  对应 AFSecurityPolicy

  • Reachability网络状态监听模块 对应AFNetworkReachabilityManager

  • Seriaalization网络通信信息序列化、反序列化模块 对应 AFURLResponseSerialization

  • UIKit:对于IOSUIKit的扩展库

网络请求的过程:

创建NSURLSessionConfig对象--用创建的config对象配置初始化NSURLSession--创建NSURLSessionTask对象并resume执行,用delegate或者block回调返回数据。

AFURLSessionManager封装了上述网络交互功能

AFURLSessionManager请求过程

1.初始化AFURLSessionManager。

2.获取AFURLSessionManager的Task对象

3.启动Task

AFURLSessionManager会为每一个Task创建一个AFURLSessionmanagerTaskDelegate对象,manager会让其处理各个Task的具体事务,从而实现了manager对多个Task的管理

初始化好manager后,获取一个网络请求的Task,生成一个Task对象,并创建了一个AFURLSessionmanagerTaskDelegate并将其关联,设置Task的上传和下载delegate,通过KVO监听download进度和upload进度

NSURLSessionDelegate的响应

因为AFURLSessionmanager所管理的AFURLSession的delegate指向其自身,因此所有的

NSURLSessiondelegate的回调地址都是AFURLSessionmanager,而AFURLSessionmanager又会根据是否需要具体处理会将AFdelegate所响应的delegate,传递到对应的AFdelegate去

AFNetworking 中如何运用 Runloop?

AFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop:

(void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
     [[NSThread currentThread] setName:@"AFNetworking"];
     NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
     [runLoop run];
     }
}

SDWebImage

SDWebImage的实现原理

  • 入口setImageWithURL:placeholderImage:options:会先把placeholderImage显示,然后SDWebImageManager根据URL开始处理图片。

  • 进入SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给SDImageCache缓存查找图片是否已经下载queryDiskCacheForKey:delegate:userInfo:.

  • 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCahceDelegate回调imageCache:didFindImage:forKey:userInfo:到SDWebImageManager

  • SDWebImageManagerDelegate回调webImageManager:didFinishWithImage:UIImageView+WebCache等前端展示图片。

  • 如果内存缓存中没有,生成NSInvocationOperation添加到队列开始从硬盘查找图片时否已经缓存。

  • 根据URLKey在硬盘缓存目录下尝试读取图片文件。这一步是在NSOperation进行的操作、所以回主线程进行结果回调notifyDelegate:

  • 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:。进行回调展示图片

  • 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userInfo:

  • 共享或重新生成一个下载器SDWebImageDownloadManager开始下载图片。

  • 图片下载由NSURLConnection来做,实现相关delegate来判断图片下载中,下载完成和下载失败。

  • connection:didReceiveData:中利用ImageIO做了按图片下载进度加载效果。

  • connectionDidFinishLoading:数据下载完成后交给SDWebImageDecoder做图片解码处理。

  • 图片解码处理在一个NSOperationQueue完成,不会拖慢主线程UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。

  • 在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:回调给SDWebImageManager告知图片下载完成。

  • 通知所有的downloadDelegates下载完成,回调给需要的地方展示图片。

  • 将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独NSInvocationOperation完成,避免拖慢主线程

  • SDImageCache在初始化的时候z会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

  • SDWI也提供了UIButton+WebCcache和MKAnnotationView+WebCache,方便使用。

  • SDWebImagePrefetcher可以预先下载图片,方便后续使用。

  • 如何解决tableView卡顿问题,前面也提了很多方案。通过设置最大并发数设置当前页的cell,而不是把所有cell一次性设置完,以及数据图片的三级缓存,直接保存在内存中和沙盒缓存中进行读取,降低网络请求的次数,不仅节约用户流量,也会保证tableView滑动的流畅性。

image.png

Masonry

Masonry常见的一些问题?

  1. 在制定约束时,约束的对象和参考对象没有使用同一约束的成员属性 image.png 前面使用leading那么后者也要用leading,保持前后一致就好。

  2. 使用Masonry时出现couldn't find a common superview,根据错误提示我们大概知道了错在哪里,你所设置约束的这个控件和所依赖的控件没有共同的父视图。因为没有共同的视图作为参照,frame 就不能转换到一个相同的坐标系。这个问题经常会出现在,我们创建了要设置约束的视图,而没有将它添加到父控件中,又或者,要设置约束的这个视图和所依赖的视图没有共同的父视图,也就是你遇到的这种情况。

  3. 使用了_下划线语法,刚好你有对label进行了懒加载,重载了其get方法。self.cashLabel会调用属性的getter方法,触发懒加载,但是_cashLabel只是访问一个局部变量。所以这样添加的控件总是出错的原因也就在这里了。

  4. reason: 'Attributes should be chained before defining the constraint relation'

image.png

image.png

  1. reason:couldn't find a common superview for<UIView: ...frame: ...layer: ...>

image.png

蓝牙开发

蓝牙开发

相比于网络请求,蓝牙是一种低功耗(low-energy)的通信手段。目前iOS的开发都是针对CoreBluetooth 4.0版本。

iOS的蓝牙开发,是基于Core Bluetooth framework

Core Bluetooth framework中几个主要的类的相关概念如下:

  1. 中心设备:CBCentral 外围设备管理类:CBPeripheralManager

外围设备的服务:CBMutableService

外围设备的特征:CBMutableCharacteristic

作为外围设备服务特征的唯一标志:CBUUID

  1. 外围设备:CBPeripheral 中心设备管理类:CBCentralManager

外围设备的服务:CBService

外围设备的特征:CBCharacteristic

读写中心设备数据的请求:CBATTRequest

上面两种区别,是以不同的角色开发,1是以外围角色当作开发,远端设备是中心角色;2是以中心角色作为开发,远端为外围角色。如下图:

image.png

注意:iOS10以后,所有的蓝牙开发都需要向用户申请蓝牙权限,所以需要在项目的info.plist设置NSBluetoothPeripheralUsageDescription.

一、以中心设备CBCentral作为本地,外围设备CBPeripheral作为远端的开发模式方案:

1. 首先我们需要初始化一个CBCentralManager的实例对象,是用来扫描发现外围设备的服务跟特征:

NSDictionary *options = @{CBCentralManagerOptionShowPowerAlertKey: @YES};

CBCentralManager* tM = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];

self.myCentralManager = tM;

2. 其次开始扫描外围设备:

[self.myCentralManager scanForPeripheralsWithServices:nil options:nil];

如果没有指定扫描哪些外围设备,services可以传nil,option也可以为nil。

3. 扫描的结果会通过CBCentralManagerDelegate回调:

///@required 蓝牙状态变化监听
- (void)centralManagerDidUpdateState:(CBCentralManager*)central;

///@optional发现外围设备,双方可以约定外围设备唯一参数,一般是外围设备的MAC地址
- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber*)RSSI;

4. 发现设备后,开始连接设备:

[self.myCentralManager connectPeripheral:self.peripheral options:nil];

5. 连接结果也是通过CBCentralManagerDelegate回调:

///连接成功
- (void)centralManager:(CBCentralManager*)central didConnectPeripheral:(CBPeripheral*)peripheral;

///连接失败
- (void)centralManager:(CBCentralManager*)central didFailToConnectPeripheral:(CBPeripheral*)peripheral error:(nullableNSError*)error;

///断开连接结果
- (void)centralManager:(CBCentralManager*)central didDisconnectPeripheral:(CBPeripheral*)peripheral error:(nullableNSError*)error;

6. 连接成功后,扫描外围设备的服务CBService:

///UUID双方约定好的
[self.myPeripheral discoverServices:@[[CBUUID UUIDWithString:KServiceUUID]]];

7. 扫描服务结果会通过CBPeripheralDelegate回调:

///如果失败,error不为空,否则为nil,比较双方约定的UUID
- (void)peripheral:(CBPeripheral*)peripheral didDiscoverServices:(nullableNSError*)error;

8. 发现CBSerive后,查询服务的特征CBCharacteristic:

[self.myPeripheral discoverCharacteristics:nil forService:service];

9. 查询特征结果会通过CBPeripheralDelegate回调:

///如果失败,error不为空,否则为nil,比较双方约定的特征UUID
-(void)peripheral:(CBPeripheral*)peripheral didDiscoverCharacteristicsForService:(CBService*)service error:(NSError*)error;

10. 发现服务特征后,注册特征通知,这样中心设备就可以读取外围发过来的数据了:

[self.myPeripheral setNotifyValue:YES forCharacteristic:characteristic];

11. 注册通知的结果通过CBPeripheralDelegate回调:

///如果失败,error不为空,否则为nil
-(void)peripheral:(CBPeripheral*)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic*)characteristic error:(NSError*)error;

12. 特征的数据也是通过CBPeripheralDelegate回调:

///如果失败,error不为空,否则为nil。数据在characteristic的value中
- (void)peripheral:(CBPeripheral*)peripheral didUpdateValueForCharacteristic:(CBCharacteristic*)characteristic error:(NSError*)error;

13. 如果给对应的特征发送数据,则调用:

///type有两个值,一个是不用返回发送的结果,一个需要返回发送数据的结果,默认是返回CBCharacteristicWriteWithResponse =0,CBCharacteristicWriteWithoutResponse,
[self.myPeripheral writeValue:sendData forCharacteristic:_mWriteCharacteristic type:CBCharacteristicWriteWithResponse];

14. 如果需要返回结果,发送数据结果也是通过CBPeripheralDelegate回调:

///失败error不为nil
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic*)characteristic error:(nullableNSError*)error;

蓝牙设备的几种状态

  1. 准备(standby)
  2. 广播(advertising)
  3. 监听扫描(Scanning)
  4. 发起连接(Initiating)
  5. 已连接(Connected)

蓝牙设备作为中心模式流程

  1. 建立中心角色
  2. 扫描外设(discover)
  3. 连接外设(connect)
  4. 扫描外设中的服务和特征(discover)
    • 4.1 获取外设的services
    • 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值。
  5. 与外设做数据交互(explore and interact)
  6. 订阅Characteristic的通知
  7. 断开连接(disconnect)

蓝牙设备作为外设模式流程

  1. 启动一个Peripheral管理对象
  2. 本地Peripheral设置服务,特性,描述,权限等等
  3. peripheral发送广告
  4. 设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法。

YYKit

  • YYModel - iOS高性能模型框架

  • YYCache - iOS高性能缓存框架

  • YYImage - iOS的图像框架用于显示/编码/解码动画WebP,APNG,GIF

  • YYWebImage - 异步图像加载框架

  • YYText - iOS中强大的文本组件

  • YYKeyboardManager - 访问键盘视图并跟踪键盘动画

  • YYDispatchQueuePool - iOS实用程序类来管理全局调度队列

  • YYAsyncLayer - 用于异步渲染和显示的iOS实用程序类

  • YYCategories - Foundation和UIKit的一组有用分类。