今天的电脑还是没有到呢,今天的学习还是只能从理论继续啦
不过再不到的话,还有两天我就要学完了,那可怎么办呀…
IOS文件管理
每一个只能访问自己沙盒中的文件资源
每一个Bundles的App都有一个自己的文件夹,下面有一个应用配置信息和二进制文件 & 资源
在程序开发时访问的是一个文件系统,存放在系统的Data文件夹,App下面有一些子文件夹
我们在开发的过程中可以存取到自己App下的子文件夹目录
沙盒机制
只能访问程序自己的目录,是每个App特有的文件夹
Documents 可以进行备份和恢复,体积较大,存放用户数据,支持用户共享
Library 开发者最常使用的文件夹,可以自定义子文件夹例如Cache Preferences(用户偏好设置,NSUserDefault,支持备份)Cache(不需要缓存的,体积较大,一般的删除缓存操作)
SystemData 系统数据
tmp 临时文件不会备份,启动会被清除
IOS获取沙盒地址
NSSearchPathForDirectoriesInDomains可以相应的取出文件地址
使用范例:
1 |
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); |
该函数返回的是一个数组,一般来说我们取第一个元素就代表了我们要找的目录
苹果提供了一部分常用的宏来快速访问一些常用的地址
1 2 3 4 5 |
FOUNDATION_EXPORT NSString *NSUserName(void); FOUNDATION_EXPORT NSString *NSFullUserName(void); FOUNDATION_EXPORT NSString *NSHomeDirectory(void); FOUNDATION_EXPORT NSString *NSHomeDirectoryForUser(NSString *_Nullable username); FOUNDATION_EXPORT NSString *NSTemporaryDirectory(void); |
NSFileManager
关注文件和文件夹层面的操作
创建、删除、查询、移动和复制等
通过NSURL或者NSString作为Path
- createDirectoryAtPath
- createFileAtPath
- fileExistsAtPath
- contentsOfDirectoryAtPath
- contensAtPath
- attributesOfItemsAtPath
- removeItemAtPath
- NSFileManagerDelegate提供移动、复制、删除等操作的具体自定义实现,完成一定操作过后会进行回调
NSFileHandle
更加细化功能,可以在指定位置写入,读入指定长度等
- 读取文件 & 写文件
- 读取指定的长度 & 在指定位置追加/截断
- 截断 & 立即刷新
- 常用于追加数据
1 2 3 4 5 |
+ (nullable instancetype) fileHandlerForReadingAtPath: (NSSrting *)path; + (nullable instancetype) fileHandlerForWritingAtPath: (NSSrting *)path; + (nullable instancetype) fileHandlerForUpdatingAtPath: (NSSrting *)path; - (void) seekToFileOffset: (unsigned long long)offset; - (void) writeData:(NSData *)data; |
取数据示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSCashesDirectory, NSUserDomainMask, YES); NSString *cachePath = [pathArray firstObject]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *dataPath = [cachePath stringByAppendingPathComponent:@"GTData"];//自动添加子目录 NSError *createError; [fileManager createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes;nil error:&createError]; //创建文件夹 NSString *listDataPath = [dataPath stringByAppendingPathComponent:@"list"]; NSData *listData = [@"abc" dataUsingEncoding:NSUTF8StringEncoding]; [fileManager createFileAtPath:listDataPath contents:nil attributes:nil];//创建文件 BOOL fileExist = [fileManager fileExistsAtPath:listDataPath];//查询文件 if(fileExist){ [fileManager removeItemAtPath:listDataPath error:nil]; }//删除文件 NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:listDataPath]; [fileHandle seekToEndOfFile]; [fileHandle writeData:[@"def" dataUsingEncoding:NSUTF8StringEncoding]]; [fileHandle synchronizeFile]; [fileHandle closeFile];//文件的追加 |
NSCoder
归档和解归档(序列化和反序列化)
提供简单的函数可以在Object和二进制数据间进行转换
抽象类具体功能需要子类实现
encode和decode都需要提供一个key进行一个标志,为了序列化向前向后兼容
- 顺序存储 如果新插入了一个属性,在decode的时候就无法兼容
- 使用属性名称做Key 在删除这个属性的时候也会出问题
- 自定义Key 这样就不会存在问题了,我们可以在业务层逻辑保证这个事情
NSKeyedArchiver
NSCoder的子类
提供简单的函数,在Object和二进制数据间进行切换
提供基本的delegate
需要Object自己处理Key-Value对应关系,即需要遵循NSCoder协议
该协议包含两个函数
- – (nullable instancetype)initWithCoder:(NSCoder *) aDecoder
- – (void) encodeWithCoder:(NSCoder *)aCoder
NSArray和NSDictionary等类型系统以及实现协议
NSSecureCoding 解决文件替换攻击、序列化时规定class包含函数supportsSecureCoding
序列化和反序列化:
1 2 3 4 |
NSData *listData = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil]; [fileManager createFileAtPath: listDataPath contents:listData attributes:nil];//序列化 NSData *readListData = [fileManager contentsAtPath:listDataPath];//读取出来 id unarchiveObj = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSSet setWithObjects:[NSarray class], [GTListItem class], nil]fromData:readListData error:nil]; |
NSUserDefault
可以看成一个Key-Value类型的数据库,保存在/Library/performances下
- 存储轻量级的数据
- 一般用于用户的偏好设置
- 支持基本数据类型
相比于之前的NSCoder这个是二进制流和Object转换上层的一层封装
1 2 3 |
[[NSUserDefault standardU[serDefaults]setObject:listData forKey:@"listData"]; NSData *test = [[NSUserDefaults standardUserDefaults] dataForKey:@"listData"]; id unarchiveObj = [NSKeyedUnarchiver unarchiveObjectOfClasses:[NSSet setWithObjects:[NSArray class], [GTListItem class], nil]fromData:readListData error:nil];] |
开源存储框架对比
系统级存储
NSUserDefault / Keychain / CoreData / NSKeyedArchived
Key-Value类型
LevelDB / MMKV / Realm
关系数据库类型
SQLite / FMDB / WCDB
缓存数据
- 对于列表数据可以通过NSKeyedArchiver保存列表数据
加载本地数据 -> 网络请求成功后替换
- 使用NSUserDefault保存已读状态
已读过的新闻,列表显示不同颜色
- 实际项目中的使用
结合项目架构,选择存储方式
数据库/KV/系统级
- BOOL不建议存储在NSUserDefault中
网络和存储的结合
- 优化占位体验
- 使用缓存数据防止重复请求
IOS中多线程
IOS中的进程 & 线程
- 一个App就是一个进程
- IOS开发中较少用到进程间通信,一般都是线程通信
IOS中的主线程/非主线程
- 保证流畅体验及线程安全,所有UI操作都集中到主线程
- 影响UI体验(较长时间的操作)
IOS中多线程相关的知识点
- NSThread / GCD / NSOperation / Runloop …
NSThread
是IOS中的线程,作为pthread的封装
创建新的线程、执行过后自动销毁、每次都要创建、手动进行线程同步、手动管理线程的生命周期
initWithTarget:selector:object:创建新的线程
@property (class, readonly, strong) BOOL isMainThread;
@property (class, readonly) NSThread *currentThread;
initWithBlock等
GCD(Grand Central Dispatch)
- 高性能的多线程解决方案,自动利用CPU
- 线程池模式,自动分配/调度线程,管理线程的生命周期
- 通过队列管理多任务,FIFO
- 对开发者使用队列代替线程的创建
- 主线程对应主队列
- 非主线程按照优先级分为4个队列High / Default / Low / Background
- 自定义队列(dispatch_queue_serial、dispatch_queue_concurrent)
三个函数
- dispatch_async(queue, block);异步执行,可以跳出运行块,先运行后面的部分
- dispatch_sync(queue, block);同步执行
- dispatch_after(when, queue, block);可以设定延迟时间等
注意:循环引用T_T
1 2 3 4 5 6 7 |
dispatch_async(downloadQueue, ^{ UIImage *image = [UIImage imageWithData:[NSData]]...; dispatch_async(mainQueue, ^{ self.rightImageView.image = image; //UI操作在主线程进行 }); }); |
常用函数
- dispatch_once 只运行一次
- dispatch_source 事件源,自定义触发和监听
- dispatch_group 多个互相引用的时候会使用这个
- dispatch_semaphore 信号量线程间同步
- dispatch_barrier_async 并发队列的同步点
NSOperation
由于GCD缺少可定制性和复杂的依赖
Block变成一个面向对象的封装,可以提供任务之间的依赖关系,支持取消暂停任务,优先级的设置,子类可以继承实现,队列支持最大并发数设置等
NSOperationQueue
对应了GCD中的队列
Runloop
主线程有Runloop,当空闲的时候会进入阻塞状态,伴随线程的任务处理循环
Runloop和线程一一对应
应用:
- NSTimer
- performselector
图片的下载和存储
从网络加载图片:
1 2 3 4 |
NSURL *url = [NSURL URLWithString: path]; NSData *data = [NSData dataWithContentsOfURL:url];//图片获取并解码 UIImage *img = [[UIImage alloc]initWithData:data]; UIImageView *imageView = [[UIImageView alloc] initWithImage:img; |
网络上读取
磁盘中读取(可以加速)
缓存中读取(最快)
使用异步的请求加载 & 缓存,尽量减少网络请求
多个请求的管理 / 优先级等
图片库需要拥有以下特点
- 提供基本的下载管理
- 提供基本的存储逻辑
- 提供常用的编解码
- 方便使用
SDWebImage
使用CocoaPod集成
sd_setImageWithURL
具有存储缓存方案,cacheType具有磁盘和缓存的二级存储关系
执行完了过后会有回调
其他开源框架
- FastImageCache
- LKImageKit
对于图片库的修改 & 二次封装
- 特殊的编解码
- 大量图片同时渲染
- 本地图片的加载和使用
- 特殊的裁剪
- Cache策略 & 用户隔离
网络、存储、多线程模型
如果在缓存中找到需要的,就直接返回即可,如果没有,从网络下载并保存到缓存中
音视频框架
AVKit
封装了UIKit等UI组件,对应AVPlayerViewController,是一个较高级的接口,创建全部视图层服务、用户控制、导航等
AVFoundation
AVPlayer/AVAsset,基于时间的音视频框架
MVC结构划分
M:AVAsset,提供与格式无关的媒体数据
AVPlayerItem协调AVAsset和AVPlayer,Notification,AVPlayerItemStatus等
C:AVPlayer 开始 / 暂停,封装对播放资源的简单操作
V:AVPlayerLayer 播放器画面
实践
定义一个coverView和一个playButton,将所有的指定位置放置展位图和playButton的图片
定义一个外部接口加载视频的图片和_coverView和_playButton.image等
AVAsset加载videoURL,AVPlayerItem加载AVAsset,AVPlayer加载AVPlayerItem(其实也可以直接加载URL)以上两个类可以省略,但是给我们提供了重定义的一个接口
使用avPlayer生成一个playerLayer
playerLayer.frame = _coverView.bounds;
[_coverView.layer addSublayer:PlayerLayer];
最后[avPlayer play]即可
注意我们播放的时候,视频的页面要加载到整个View的最上面,挡住button
知识链接:__kindof修饰词,用于修饰函数返回值,当使用__kindof的时候,本类和子类都可以作为返回值返回
NSNotification
Delegate
只能一对一,可以有返回值,互相引用
KVO
监听的方式,一对多,只能作用在value和变化上,需要互相依赖,单方向无协同
forKeyPath:@”status”表示对属性监听,属性变化:NSKeyValueOb…OptionNew
dealloc时也需要移除Observer
上述视频播放中,AVPlayerItem可以直接addObserver监听的加载和播放状态
addObserver:forKeyPath:context:
Notification
一对多,以一种事件的方式进行传播,可以携带更多的信息,常用于系统级消息,无依赖,单方向无协同
中心化管理NSNotificationCenter,对象需要在center中注册,广播也是通过center广播,主要关注系统级事件例如:前后台切换,内存警告等
接受通知addObserver发送通知postNotification
在上述的视频播放中,我们可以监听AVPlayerItem的Status,如果开始play的时候我们在进行play
操作是向中心注册AVPlayerItemDidPlayToEndTimeNotification,并添加相应函数
当对象销毁的时候我们需要移除掉这个Observer(dealloc中重写即可)
AVPlayer播放进度
可以由CMTime CMTimeMakeduration和currentTime实现
添加一个监听,forKeyPath与下面的监听方法对应
在KVO监听方法中添加上对应的forKeyPath即可
value是帧timesacle是帧率相除即为时间
但是CMTimeGetSeconds函数可以直接返回时长
使用的时候先从AVVideoItem中获取CMTime然后再调用函数返回视频时长和缓冲时长
我们如果要获取播放的时长可以采用addPeriodicTimeObserverForInterval:queue:usingBlock:方法,每秒回调一次
播放进度调整
AVPlayer中的seekToTime:即可直接跳到播放的进度
0 条评论