AFNetworking
AFNetworking底层实现
AFNetworking是封装的NSURLSession的网络请求。AFNetworking由五个模块组成: 分别由NSURLSession,Security,Reachability,Serialization,UIKit五部分组成
NSURLSession:
网络通信模块(核心模块) 对应 AFNetworking中的AFURLSessionManager和对HTTP协议进行特化处理的AFHTTPSessionManager,AFHTTPSessionManager是继承于AFURLSessionmanager的。Security:
网络通讯安全策略模块对应AFSecurityPolicyReachability:
网络状态监听模块对应AFNetworkReachabilityManagerSeriaalization:
网络通信信息序列化、反序列化模块对应AFURLResponseSerializationUIKit:对于
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滑动的流畅性。
Masonry
Masonry常见的一些问题?
在制定约束时,
约束的对象和参考对象没有使用同一约束的成员属性前面使用leading那么后者也要用leading,保持前后一致就好。
使用Masonry时出现
couldn't find a common superview,根据错误提示我们大概知道了错在哪里,你所设置约束的这个控件和所依赖的控件没有共同的父视图。因为没有共同的视图作为参照,frame 就不能转换到一个相同的坐标系。这个问题经常会出现在,我们创建了要设置约束的视图,而没有将它添加到父控件中,又或者,要设置约束的这个视图和所依赖的视图没有共同的父视图,也就是你遇到的这种情况。使用了
_下划线语法,刚好你有对label进行了懒加载,重载了其get方法。self.cashLabel会调用属性的getter方法,触发懒加载,但是_cashLabel只是访问一个局部变量。所以这样添加的控件总是出错的原因也就在这里了。reason: 'Attributes should be chained before defining the constraint relation'
- reason:couldn't find a common superview for<UIView: ...frame: ...layer: ...>
蓝牙开发
蓝牙开发
相比于网络请求,蓝牙是一种低功耗(low-energy)的通信手段。目前iOS的开发都是针对
CoreBluetooth 4.0版本。iOS的蓝牙开发,是基于
Core Bluetooth framework。Core Bluetooth framework中几个主要的类的相关概念如下:
- 中心设备:
CBCentral外围设备管理类:CBPeripheralManager外围设备的服务:
CBMutableService外围设备的特征:
CBMutableCharacteristic作为外围设备服务特征的唯一标志:
CBUUID
- 外围设备:
CBPeripheral中心设备管理类:CBCentralManager外围设备的服务:
CBService外围设备的特征:
CBCharacteristic读写中心设备数据的请求:
CBATTRequest上面两种区别,是以不同的角色开发,1是以外围角色当作开发,远端设备是中心角色;2是以中心角色作为开发,远端为外围角色。如下图:
注意: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;
蓝牙设备的几种状态
- 准备(standby)
- 广播(advertising)
- 监听扫描(Scanning)
- 发起连接(Initiating)
- 已连接(Connected)
蓝牙设备作为中心模式流程
- 建立中心角色
- 扫描外设(discover)
- 连接外设(connect)
- 扫描外设中的服务和特征(discover)
- 4.1 获取外设的services
- 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值。
- 与外设做数据交互(explore and interact)
- 订阅Characteristic的通知
- 断开连接(disconnect)
蓝牙设备作为外设模式流程
- 启动一个Peripheral管理对象
- 本地Peripheral设置服务,特性,描述,权限等等
- peripheral发送广告
- 设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法。
YYKit
-
YYModel- iOS高性能模型框架 -
YYCache- iOS高性能缓存框架 -
YYImage- iOS的图像框架用于显示/编码/解码动画WebP,APNG,GIF -
YYWebImage- 异步图像加载框架 -
YYText- iOS中强大的文本组件 -
YYKeyboardManager- 访问键盘视图并跟踪键盘动画 -
YYDispatchQueuePool- iOS实用程序类来管理全局调度队列 -
YYAsyncLayer- 用于异步渲染和显示的iOS实用程序类 -
YYCategories- Foundation和UIKit的一组有用分类。