原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、KeyChain
- 1、简介
- 2、使用KeyChain
- 二、NSFileManager和NSFileHandle
- 1、简介
- 2、NSFileManager 创建文件/文件夹
- 3、NSFileManager 写入/读取数据
- 4、NSFileManager 浏览文件
- 5、NSFileManager 移动文件
- 6、NSFileManager 删除文件
- 7、NSFileManager 文件信息
- 8、NSFileHandle(文件连接器)
- 三、沙盒
- 1、简介
- 2、使用沙盒
- 四、plist(属性列表)
- 1、简介
- 2、Bundle中的plist文件
- 3、沙盒中的plist文件
- 五、NSUserDefault(偏好设置)
- 1、简介
- 2、使用NSUserDefault
- 六、NSKeyedArchiver(归档)
- 1、简介
- 2、基本数据类型的归档
- 3、自定义对象的归档
- 七、SQLite
- 1、简介
- 2、SQLite的增删改查
- 3、使用SQLite
- 八、CoreData
- 1、简介
- 2、通过视图方式创建Model
- 3、Core Data的增删改查操作
- 4、查看数据库
- 九、NSCache
- 1、简介
- 2、缓存操作
- 3、使用方式
- 十、BookMark
- 1、简介
- 2、使用BookMark
- Demo
- 参考文献
一、KeyChain
1、简介
作用
提供一种安全的存储敏感信息的工具,安全机制保证了存储这些敏感信息不会被窃取。
网络密码:用户访问服务器或者网站
通用密码:用来保存应用程序或者
数据库密码:用于认证的证书,密钥和身份信息
特点
-
keychain
的数据是经过加密的 -
keychain
的数据可以通过group
方式,让程序可以在App
间共享。不过得要相同TeamID
- 数据并不存放在
App
的Sanbox
中,即使删除了App
,资料依然保存在keychain
中。如果重新安装了app
,还可以从keychain
获取数据
2、使用KeyChain
a、往keychain里面添加一个账户
- (BOOL)addItemWithService:(NSString *)service account:(NSString *)account password:(NSString *)password
{
// 1、构造一个操作字典用于查询
NSMutableDictionary *searchDict = [[NSMutableDictionary alloc]initWithCapacity:4];
// 表明存储的是一个密码
[searchDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// 输入service
[searchDict setObject:service forKey:(__bridge id)kSecAttrService];
// 输入account
[searchDict setObject:account forKey:(__bridge id)kSecAttrAccount];
// 2、先查查是否已经存在
OSStatus status = -1;
CFTypeRef result =NULL;
// 核心API,查找是否匹配并且返回密码!
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDict, &result);
// 3、判断状态是否查询成功
if (status == errSecItemNotFound)// 没有找到则添加
{
// 把 password 转换为 NSData
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
// 添加密码
[searchDict setObject:passwordData forKey:(__bridge id)kSecValueData];
// !!!!!关键的添加API
status = SecItemAdd((__bridge CFDictionaryRef)searchDict, NULL);
}
else if (status == errSecSuccess)// 成功找到,说明钥匙已经存在则进行更新
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:searchDict];
// 把password 转换为 NSData
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
// 添加密码
[dict setObject:passwordData forKey:(__bridge id)kSecValueData];
// !!!!关键的更新API
status = SecItemUpdate((__bridge CFDictionaryRef)searchDict, (__bridge CFDictionaryRef)dict);
}
// 返回添加一个账户是否成功的状态
return (status == errSecSuccess);
}
b、从keychain里面查询密码
- (NSString *)passwordForService:(nonnull NSString *)service account:(nonnull NSString *)account
{
// 1、生成一个查询用的可变字典
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:4];
// 表明为一般密码,也可能是证书或者其他东西
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// 输入service
[dict setObject:service forKey:(__bridge id)kSecAttrService];
// 输入account
[dict setObject:account forKey:(__bridge id)kSecAttrAccount];
// 返回Data
[dict setObject:@YES forKey:(__bridge id)kSecReturnData];
// 2、查询
OSStatus status = -1;
CFTypeRef result = NULL;
// 核心API,查找是否匹配并且返回密码!
status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);
// 3、判断状态是否查询成功
if (status != errSecSuccess)
{
return nil;
}
// 4、将返回数据转换成string
NSString *password = [[NSString alloc] initWithData:(__bridge_transfer NSData *)result encoding:NSUTF8StringEncoding];
return password;
}
c、调用方法
- (void)viewDidLoad
{
[super viewDidLoad];
// 往keychain里面添加一个账户
BOOL isSuccess = [self addItemWithService:@"com.szzc.driver" account:@"xiejiapei" password:@"1997"];
if (isSuccess)
{
NSLog(@"成功往keychain里面添加一个账户,账户为:xiejiapei,密码为:1997");
}
// 从keychain里面查询密码
NSString *password = [self passwordForService:@"com.szzc.driver" account:@"xiejiapei"];
NSLog(@"查询到的密码为:%@",password);
}
d、输出结果
2020-10-16 18:11:03.818966+0800 LocalCacheDemo[97188:3473508] 成功往keychain里面添加一个账户,账户为:xiejiapei,密码为:1997
2020-10-16 18:11:03.819609+0800 LocalCacheDemo[97188:3473508] 查询到的密码为:1997
二、NSFileManager和NSFileHandle
1、简介
- 如果只是管理文件和目录,程序可以使用
NSFileManager
进行管理,包括创建、删除、移动和复制文件等 - 如果程序需要读取文件内容,则可通过
NSFileHandle
进行处理 - 如果需要读取网络资源,则可通过
NSURL
进行处理 - 如果程序只是读取项目内部资源,则可借助
NSBundle
进行处理
NSFileManager(文件管理对象)
主要是对文件进行的操作(创建/删除/改名等)以及文件信息的获取。
// 文件管理对象
@property (class, readonly, strong) NSFileManager *defaultManager
// 判断某个路径是否存在,isDirectory是一个指针,表示该路径是否是目录
-(BOOL)fileExistsAtPath:(NSString *)path isDirectory:(nullable BOOL *)isDirectory
// 创建一个目录
-(BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(nullable NSDictionary<NSString *, id> *)attributes error:(NSError **)error
// 创建一个文件,可顺便写入data
-(BOOL)createFileAtPath:(NSString *)path contents:(nullable NSData *)data attributes:(nullable NSDictionary<NSString *, id> *)attr
// 写入数据
-(BOOL)writeToFile:(NSString *)path atomically:
// 移动文件,可用来重命名
-(BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
// 复制文件
-(BOOL)copyItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL error:(NSError **)error
// 删除文件
-(BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
// 获取文件信息(文件大小、修改时间、所有者等)
-(nullable NSDictionary<NSFileAttributeKey, id> *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error
NSFileHandle(文件连接器)
对文件内容进行读取和写入操作。
//写的方式打开文件
+(nullable instancetype)fileHandleForWritingAtPath:(NSString *)path
//读的方式打开文件
+(nullable instancetype)fileHandleForReadingAtPath:(NSString *)path
//同步文件,通常用在写入数据后
-(void)synchronizeFile
//关闭文件
-(void)closeFile
//跳到文件末尾
-(unsigned long long)seekToEndOfFile
//跳到指定偏移位置
-(void)seekToFileOffset:(unsigned long long)offset
//将文件的长度设为offset字节
-(void)truncateFileAtOffset:(unsigned long long)offset
//从当前字节读取到文件到末尾数据
-(NSData *)readDataToEndOfFile
//从当前字节读取到指定长度数据
-(NSData *)readDataOfLength:(NSUInteger)lengthqawsfeip
//创建文件夹
[filemanager createDirectoryAtPath:documentsDir withIntermediateDirectories:YES attributes:nil error:nil];
//删除文件夹
[fileManager removeItemAtPath:wtdbPath error:nil];
//移动文件夹
[fileManager moveItemAtPath:filePath toPath:moveToPath error:nil];
//文件复制
[filemanager copyItemAtPath:dbBundlePath toPath:wtdbPath error:nil];
//文件写入
NSString *content=@"测试写入内容!";
[content writeToFile:testPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
//文件读取
NSString *content=[NSString stringWithContentsOfFile:testPath encoding:NSUTF8StringEncoding error:nil];
2、NSFileManager 创建文件/文件夹
a、创建目录
方式一:使用URL进行创建目录
- (NSURL *)createDirectory
{
NSURL *directoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSURL *directoryPath = [directoryURL URLByAppendingPathComponent:@"Directory"];
// 目录不存在则创建
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtURL:directoryPath withIntermediateDirectories:YES attributes:nil error:&error])
{
// 处理错误
return nil;
}
NSLog(@"创建的目录路径为:%@",directoryPath);
return directoryPath;
}
输出结果为:
2020-10-19 11:35:09.350784+0800 LocalCacheDemo[15052:4306020] 创建的目录路径为:file:///Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/7D945D03-B465-47EF-A192-B481E7E1211D/Documents/Directory
方式二:使用String进行创建目录
- (BOOL)creatDirectoryWithPath:(NSString *)path
{
if (path.length == 0)
{
return NO;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isSuccess = YES;
BOOL isExist = [fileManager fileExistsAtPath:path];
if (isExist == NO)
{
NSError *error;
if (![fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error])
{
isSuccess = NO;
NSLog(@"创建目录失败:%@",[error localizedDescription]);
}
}
return isSuccess;
}
调用方法:
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
if ([self creatDirectoryWithPath:directoryPath])
{
NSLog(@"创建的目录路径为:%@",directoryPath);
}
}
输出结果为:
2020-10-19 13:41:20.599656+0800 LocalCacheDemo[16233:4363015] 创建的目录路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/CA3912BF-535E-4038-8130-A5225853CA24/Documents/XieJiaPei
c、创建文件
- (BOOL)creatFile:(NSString *)filePath
{
if (filePath.length == 0)// 文件路径为空
{
return NO;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath])// 文件已经存在
{
return YES;
}
NSError *error;
NSString *directoryPath = [filePath stringByDeletingLastPathComponent];// 目录路径
// 创建目录
BOOL isSuccess = [fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
if (!isSuccess)
{
NSLog(@"创建文件夹失败:%@",[error localizedDescription]);
return NO;
}
// 创建文件
isSuccess = [fileManager createFileAtPath:filePath contents:nil attributes:nil];
return isSuccess;
}
调用方法
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.png"];
if ([self creatFile:filePath])
{
NSLog(@"创建的文件路径为:%@",filePath);
}
}
输出结果
2020-10-19 13:52:51.934110+0800 LocalCacheDemo[16369:4370599] 创建的文件路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/D1A18E6E-0364-416D-B6F1-F0B53E5847DA/Documents/XieJiaPei/luckcoffee.png
3、NSFileManager 写入/读取数据
a、向文件写入数据
- (BOOL)writeToFile:(NSString *)filePath contents:(NSData *)data
{
if (filePath.length == 0)
{
return NO;
}
// 文件路径不存在则创建
BOOL result = [self creatFile:filePath];
if (result)
{
result = [data writeToFile:filePath atomically:YES];
// 写入数据
if (result)
{
NSLog(@"写入成功");
}
else
{
NSLog(@"写入失败");
}
}
else
{
NSLog(@"写入失败");
}
return result;
}
调用方法
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.plist"];
NSDictionary *dict = @{@"诗人" : @"李太白"};
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
if ([self writeToFile:filePath contents:data])
{
NSLog(@"写入路径为:%@",filePath);
}
}
输出结果
2020-10-19 14:10:17.299826+0800 LocalCacheDemo[16606:4383756] 写入成功
2020-10-19 14:10:17.299944+0800 LocalCacheDemo[16606:4383756] 写入路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/45DC2013-6C52-44E5-B560-3671D5692984/Documents/XieJiaPei/luckcoffee.plist
b、读取文件中的数据
- (void)readFileWithPath:(NSString *)path
{
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *receiveString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSData *datas = [receiveString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:datas options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"文件读取成功,内容为: %@",jsonDict);
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.plist"];
NSDictionary *dict = @{@"Name" : @"LiTaiBai"};
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
if ([self writeToFile:filePath contents:data])
{
NSLog(@"写入路径为:%@",filePath);
}
[self readFileWithPath:filePath];
}
输出结果
2020-10-19 14:25:28.598547+0800 LocalCacheDemo[16878:4397635] 写入路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/95C1591A-40B9-4710-BB90-F1164076E045/Documents/XieJiaPei/luckcoffee.plist
2020-10-19 14:25:28.598801+0800 LocalCacheDemo[16878:4397635] 文件读取成功,内容为: {
Name = LiTaiBai;
}
4、NSFileManager 浏览文件
a、获取目录下一级的所有的文件
- (NSArray *)getFileListWithPath:(NSString *)path
{
if (path.length == 0)
{
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *fileList = [fileManager contentsOfDirectoryAtPath:path error:&error];
if (error)
{
NSLog(@"获取文件列表失败:%@",[error localizedDescription]);
}
return fileList;
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSArray *fileList = [self getFileListWithPath:directoryPath];
NSLog(@"文件列表:%@",fileList);
}
输出结果
2020-10-19 15:37:32.712218+0800 LocalCacheDemo[17843:4452689] 文件列表:(
".DS_Store",
XieJiaPei,
Directory
)
2020-10-19 14:49:07.588486+0800 LocalCacheDemo[17170:4414907] 文件列表:(
"luckcoffee.png",
"luckcoffee.plist"
)
b、获取文件夹下所有的文件列表
- (NSArray *)getAllFileListWithPath:(NSString *)path
{
if (path.length==0)
{
return nil;
}
NSArray *fileArray = [self getFileListWithPath:path];
NSMutableArray *newFileArray = [NSMutableArray array];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *filePath in fileArray)
{
NSString * fullPath = [path stringByAppendingPathComponent:filePath];
BOOL isDirectory = NO;
if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory])
{
if (isDirectory)
{
// 如果是目录进行递归
[newFileArray addObjectsFromArray:[self getAllFileListWithPath:fullPath]];
}
else
{
[newFileArray addObject:fullPath];
}
}
}
return newFileArray;
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSArray *fileList = [self getAllFileListWithPath:documentPath];
NSLog(@"文件列表:%@",fileList);
}
输出结果
2020-10-19 15:40:18.931575+0800 LocalCacheDemo[17898:4455962] 文件列表:(
"/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/38B9CD9D-5212-4503-962D-F978846ADEEE/Documents/.DS_Store",
"/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/38B9CD9D-5212-4503-962D-F978846ADEEE/Documents/XieJiaPei/.DS_Store",
"/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/38B9CD9D-5212-4503-962D-F978846ADEEE/Documents/XieJiaPei/luckcoffee.png",
"/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/38B9CD9D-5212-4503-962D-F978846ADEEE/Documents/XieJiaPei/luckcoffee.plist",
"/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/38B9CD9D-5212-4503-962D-F978846ADEEE/Documents/Directory/apple.zip"
c、遍历文件夹下所有项目
includingPropertiesForKeys属性
-
NSURLNameKey
:文件名 -
NSURLIsDirectoryKey
:是否是文件夹 -
NSURLIsPackageKey
:是否是一个包(比如APP) -
NSURLIsHiddenKey
:是否是隐藏文件 -
NSURLCreationDateKey
:创建日期
options 的属性
-
NSDirectoryEnumerationSkipsSubdirectoryDescendants
: 浅层的枚举,不会枚举子目录 -
NSDirectoryEnumerationSkipsPackageDescendants
: 不会扫描pakages
的内容 -
NSDirectoryEnumerationSkipsHiddenFile
: 不会扫描隐藏文件
遍历方法
- (void)enumeratorAtURL
{
NSURL *directoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSLog(@"目录为:%@",directoryURL);
// 遍历下级的所有文件和文件夹
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:directoryURL includingPropertiesForKeys:@[NSURLIsPackageKey] options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:^(NSURL *url, NSError *error) {// 处理错误
// 如果错误后应继续,则返回YES
return YES;
}];
// 每一个Item的url
for (NSURL *url in enumerator)
{
// Item的名称
NSString *localizedName = nil;
[url getResourceValue:&localizedName forKey:NSURLLocalizedNameKey error:NULL];
NSLog(@"Item的名称:%@", localizedName);
// Item是否是目录
NSNumber *isDirectory = nil;
[url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
if ([isDirectory boolValue])
{
// Item是否是一个包
NSNumber *isPackage = nil;
[url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:NULL];
if ([isPackage boolValue])
{
NSLog(@"这个包在:%@", localizedName);
}
else
{
NSLog(@"这个目录在:%@", localizedName);
}
}
}
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
[self enumeratorAtURL];
}
输出结果
2020-10-19 15:04:27.162043+0800 LocalCacheDemo[17412:4428845] 目录为:file:///Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/F2FC18E5-5BC9-4217-A556-AE5E16786D02/Documents/
2020-10-19 15:04:27.184191+0800 LocalCacheDemo[17412:4428845] Item的名称:XieJiaPei
2020-10-19 15:04:27.184342+0800 LocalCacheDemo[17412:4428845] 这个目录在:XieJiaPei
2020-10-19 15:04:27.184692+0800 LocalCacheDemo[17412:4428845] Item的名称:luckcoffee.png
2020-10-19 15:04:27.184841+0800 LocalCacheDemo[17412:4428845] Item的名称:luckcoffee.plist
2020-10-19 15:04:27.185013+0800 LocalCacheDemo[17412:4428845] Item的名称:Directory
2020-10-19 15:04:27.185087+0800 LocalCacheDemo[17412:4428845] 这个目录在:Directory
2020-10-19 15:04:27.185317+0800 LocalCacheDemo[17412:4428845] Item的名称:apple.zip
d、目录内容
- (void)contentsOfDirectoryAtURL
{
NSURL *directoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSError *error = nil;
NSArray *properties = [NSArray arrayWithObjects: NSURLLocalizedNameKey,nil];// 本地化文件名
// 获取目录下的所有文件和文件夹
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:directoryURL includingPropertiesForKeys:properties options:(NSDirectoryEnumerationSkipsHiddenFiles) error:&error];
if (array == nil)
{
NSLog(@"处理错误");
}
// 获取该文件夹或者文件的创建日期
NSDate *date = nil;
[array[0] getResourceValue:&date forKey:NSURLCreationDateKey error:nil];// 创建日期
if (date)
{
NSLog(@"该文件夹或者文件的创建日期为:%@",date);
}
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
[self contentsOfDirectoryAtURL];
}
输出结果
2020-10-19 15:09:18.669148+0800 LocalCacheDemo[17473:4432803] 该文件夹或者文件的创建日期为:2020-10-19 05:41:20 +0000
5、NSFileManager 移动文件
a、移动文件方法
- (BOOL)moveFileFromPath:(NSString *)fromPath toPath:(NSString *)toPath toPathIsDirectory:(BOOL)isDirectory
{
NSFileManager *fileManager = [NSFileManager defaultManager];
// 判断原始路径
if (![fileManager fileExistsAtPath:fromPath])
{
NSLog(@"错误: 原始路径不存在");
return NO;
}
// 判断目标路径是否存在
BOOL toPathIsDirectory = NO;
BOOL isExist = [fileManager fileExistsAtPath:toPath isDirectory:&toPathIsDirectory];
if (isExist)// 存在
{
if (toPathIsDirectory)
{
// 创建目标目录,已经存在则会直接跳过
if ([self creatDirectoryWithPath:toPath])
{
// 在目录下添加文件
NSString *fileName = fromPath.lastPathComponent;
toPath = [toPath stringByAppendingPathComponent:fileName];
// 移动文件
return [self moveItemAtPath:fromPath toPath:toPath];
}
}
else
{
// 移除目标路径的重复文件
[self removeFileWithPath:toPath];
// 移动文件
return [self moveItemAtPath:fromPath toPath:toPath];
}
}
else// 不存在
{
if (isDirectory)
{
// 创建目标目录,已经存在则会直接跳过
if ([self creatDirectoryWithPath:toPath])
{
// 在目录下添加文件
NSString *fileName = fromPath.lastPathComponent;
toPath = [toPath stringByAppendingPathComponent:fileName];
// 移动文件
return [self moveItemAtPath:fromPath toPath:toPath];
}
}
else
{
// 移动文件
return [self moveItemAtPath:fromPath toPath:toPath];
}
}
return NO;
}
b、封装系统的移动文件方法
- (BOOL)moveItemAtPath:(NSString *)fromPath toPath:(NSString *)toPath
{
BOOL result = NO;
NSError * error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
result = [fileManager moveItemAtPath:fromPath toPath:toPath error:&error];
if (error)
{
NSLog(@"移动文件失败:%@",[error localizedDescription]);
}
return result;
}
c、移动方法调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"Directory"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.png"];
NSString *toPath = [documentPath stringByAppendingPathComponent:@"LuckCoffee"];
if ([self moveFileFromPath:filePath toPath:toPath toPathIsDirectory:YES])
{
NSLog(@"原路径为:%@",filePath);
NSLog(@"目标路径为:%@",toPath);
NSLog(@"移动文件成功!");
}
}
d、输出结果
2020-10-19 16:02:55.739858+0800 LocalCacheDemo[18184:4471965] 原路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/BA2C1FE6-07C1-4A6C-BC40-E4DAAA28472F/Documents/Directory/luckcoffee.png
2020-10-19 16:02:55.740011+0800 LocalCacheDemo[18184:4471965] 目标路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/BA2C1FE6-07C1-4A6C-BC40-E4DAAA28472F/Documents/LuckCoffee
2020-10-19 16:02:55.740140+0800 LocalCacheDemo[18184:4471965] 移动文件成功!
6、NSFileManager 删除文件
a、移除文件
- (BOOL)removeFileWithPath:(NSString *)filePath
{
BOOL isSuccess = NO;
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
// 移除文件
isSuccess = [fileManager removeItemAtPath:filePath error:&error];
if (error)
{
NSLog(@"移除文件失败:%@",[error localizedDescription]);
}
else
{
NSLog(@"移除文件成功");
}
return isSuccess;
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"LuckCoffee"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.png"];
NSArray *fileList = [self getFileListWithPath:directoryPath];
NSLog(@"初始文件列表:%@",fileList);
if ([self removeFileWithPath:filePath])
{
NSLog(@"删除了的文件路径为:%@",filePath);
}
NSArray *deleteFileList = [self getFileListWithPath:directoryPath];
NSLog(@"删除后文件列表:%@",deleteFileList);
}
输出结果
2020-10-19 16:16:58.260872+0800 LocalCacheDemo[18365:4482756] 初始文件列表:(
"luckcoffee.png"
)
2020-10-19 16:16:58.261768+0800 LocalCacheDemo[18365:4482756] 移除文件成功
2020-10-19 16:16:58.261862+0800 LocalCacheDemo[18365:4482756] 删除了的文件路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/BD8B5406-20F7-48EC-8A3E-08C71BC2DF7D/Documents/LuckCoffee/luckcoffee.png
2020-10-19 16:16:58.262015+0800 LocalCacheDemo[18365:4482756] 删除后文件列表:(
)
b、根据后缀名移除一堆文件
- (void)removeFileSuffixList:(NSArray<NSString *> *)suffixList filePath:(NSString *)path deep:(BOOL)deep
{
// 找到路径下的所有文件
NSArray *fileArray = nil;
if (deep)// 是否深度遍历
{
fileArray = [self getAllFileListWithPath:path];
}
else
{
fileArray = [self getFileListWithPath:path];
NSMutableArray *fileArrayTmp = [NSMutableArray array];
for (NSString *fileName in fileArray)
{
NSString* fullPath = [path stringByAppendingPathComponent:fileName];
[fileArrayTmp addObject:fullPath];
}
fileArray = fileArrayTmp;
}
// 根据后缀名移除文件
for (NSString *filePath in fileArray)
{
for (NSString *suffix in suffixList)
{
if ([filePath hasSuffix:suffix])
{
[self removeFileWithPath:filePath];
}
}
}
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSArray *fileList = [self getFileListWithPath:directoryPath];
NSLog(@"初始文件列表:%@",fileList);
[self removeFileSuffixList:@[@"plist",@"zip"] filePath:directoryPath deep:NO];
NSArray *deleteFileList = [self getFileListWithPath:directoryPath];
NSLog(@"删除后文件列表:%@",deleteFileList);
}
输出结果
2020-10-19 16:31:33.020705+0800 LocalCacheDemo[18644:4497195] 初始文件列表:(
".DS_Store",
"luckcoffee copy 9.plist",
"luckcoffee copy 8.plist",
"apple.zip",
"luckcoffee copy 6.plist",
"luckcoffee copy 4.plist",
"luckcoffee copy 2.plist",
"luckcoffee copy.plist",
"luckcoffee copy 5.plist",
"luckcoffee copy 7.plist",
"luckcoffee.plist",
"luckcoffee copy 3.plist",
"apple copy 2.zip",
"apple copy.zip",
"apple copy 3.zip"
)
2020-10-19 16:31:33.084335+0800 LocalCacheDemo[18644:4497195] 删除后文件列表:(
".DS_Store"
)
7、NSFileManager 文件信息
a、获取文件大小
- (long long)getFileSizeWithPath:(NSString*)path
{
unsigned long long fileLength = 0;
NSNumber *fileSize;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:path error:nil];
if ((fileSize = [fileAttributes objectForKey:NSFileSize]))
{
fileLength = [fileSize unsignedLongLongValue]; //单位是 B
}
return fileLength;
}
b、获取文件信息
- (NSDictionary*)getFileInfoWithPath:(NSString*)path
{
NSError *error;
NSDictionary *reslut = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
if (error)
{
NSLog(@":%@",[error localizedDescription]);
}
return reslut;
}
c、调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.plist"];
NSLog(@"获取文件的大小:%lld",[self getFileSizeWithPath:filePath]);
NSLog(@"获取文件的信息:%@",[self getFileInfoWithPath:filePath]);
}
d、输出结果
2020-10-19 16:43:16.949318+0800 LocalCacheDemo[18800:4505220] 获取文件的大小:25
2020-10-19 16:43:16.950750+0800 LocalCacheDemo[18800:4505220] 获取文件的信息:{
NSFileCreationDate = "2020-10-19 06:37:50 +0000";
NSFileExtendedAttributes = {
"com.apple.macl" = {length = 72, bytes = 0x02009c35 d24be1bc 4fbd9baa 030c411f ... 00000000 00000000 };
};
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 20;
NSFileGroupOwnerAccountName = staff;
NSFileModificationDate = "2020-10-19 06:37:50 +0000";
NSFileOwnerAccountID = 501;
NSFilePosixPermissions = 420;
NSFileReferenceCount = 1;
NSFileSize = 25;
NSFileSystemFileNumber = 89083561;
NSFileSystemNumber = 16777220;
NSFileType = NSFileTypeRegular;
}
8、NSFileHandle(文件连接器)
a、方法和属性
对文件内容进行读取和写入操作。
//写的方式打开文件
+(nullable instancetype)fileHandleForWritingAtPath:(NSString *)path
//读的方式打开文件
+(nullable instancetype)fileHandleForReadingAtPath:(NSString *)path
//同步文件,通常用在写入数据后
-(void)synchronizeFile
//关闭文件
-(void)closeFile
//跳到文件末尾
-(unsigned long long)seekToEndOfFile
//跳到指定偏移位置
-(void)seekToFileOffset:(unsigned long long)offset
//将文件的长度设为offset字节
-(void)truncateFileAtOffset:(unsigned long long)offset
//从当前字节读取到文件到末尾数据
-(NSData *)readDataToEndOfFile
//从当前字节读取到指定长度数据
-(NSData *)readDataOfLength:(NSUInteger)lengthqawsfeip
//创建文件夹
[filemanager createDirectoryAtPath:documentsDir withIntermediateDirectories:YES attributes:nil error:nil];
//删除文件夹
[fileManager removeItemAtPath:wtdbPath error:nil];
//移动文件夹
[fileManager moveItemAtPath:filePath toPath:moveToPath error:nil];
//文件复制
[filemanager copyItemAtPath:dbBundlePath toPath:wtdbPath error:nil];
//文件写入
NSString *content=@"测试写入内容!";
[content writeToFile:testPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
//文件读取
NSString *content=[NSString stringWithContentsOfFile:testPath encoding:NSUTF8StringEncoding error:nil];
b、追加写入数据
- (BOOL)appendData:(NSData *)data withPath:(NSString *)filePath
{
if (filePath.length == 0)
{
return NO;
}
// 文件路径不存在则创建
BOOL result = [self creatFile:filePath];
if (result)
{
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[handle seekToEndOfFile];// 追加到文件内容末尾
[handle writeData:data];// 写入数据
[handle synchronizeFile];// 同步
[handle closeFile];// 关闭文件
}
else
{
NSLog(@"追加数据失败");
}
return result;
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.plist"];
NSDictionary *nameDict = @{@"Name" : @"LiTaiBai"};
NSData *nameData = [NSJSONSerialization dataWithJSONObject:nameDict options:NSJSONWritingPrettyPrinted error:nil];
if ([self writeToFile:filePath contents:nameData])
{
NSLog(@"写入路径为:%@",filePath);
}
NSDictionary *loveDict = @{@"Love" : @"Drink"};
NSData *loveData = [NSJSONSerialization dataWithJSONObject:loveDict options:NSJSONWritingPrettyPrinted error:nil];
if ([self appendData:loveData withPath:filePath])
{
NSLog(@"追加的数据为:%@",loveDict);
}
}
输出结果为:
2020-10-19 14:33:28.044625+0800 LocalCacheDemo[16966:4403540] 写入成功
2020-10-19 14:33:28.044754+0800 LocalCacheDemo[16966:4403540] 写入路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/45B1E1FF-03DE-4A93-AF74-2AA169BE366F/Documents/XieJiaPei/luckcoffee.plist
2020-10-19 14:33:28.045087+0800 LocalCacheDemo[16966:4403540] 追加的数据为:{
Love = Drink;
}
c、读取文件中的数据
- (void)useFileHandlerReadFileWithPath:(NSString *)path
{
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
NSData *fileData = [handle readDataToEndOfFile];
[handle closeFile];
NSString *receiveString = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
NSData *datas = [receiveString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:datas options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"文件读取成功,内容为: %@",jsonDict);
}
调用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [documentPath stringByAppendingPathComponent:@"XieJiaPei"];
NSString *filePath = [directoryPath stringByAppendingPathComponent:@"/luckcoffee.plist"];
NSDictionary *nameDict = @{@"Name" : @"LiTaiBai"};
NSData *nameData = [NSJSONSerialization dataWithJSONObject:nameDict options:NSJSONWritingPrettyPrinted error:nil];
if ([self writeToFile:filePath contents:nameData])
{
NSLog(@"写入路径为:%@",filePath);
}
[self useFileHandlerReadFileWithPath:filePath];
}
输出结果
2020-10-19 14:37:50.206317+0800 LocalCacheDemo[17014:4407298] 写入成功
2020-10-19 14:37:50.206432+0800 LocalCacheDemo[17014:4407298] 写入路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/1D2D63F5-AD2C-47A1-A9F9-ADB1238F7ADF/Documents/XieJiaPei/luckcoffee.plist
2020-10-19 14:37:50.206671+0800 LocalCacheDemo[17014:4407298] 文件读取成功,内容为: {
Name = LiTaiBai;
}
三、沙盒
1、简介
a、目录
- Bundle目录: 代码(可执行文件)与资源文件在硬盘上的存储目录。
-
Documents:用户数据基本上都放在这个位置(例如从网上下载的图片或音乐文件),该文件夹在应用程序更新时会自动备份,在连接
iTunes
时也可以自动同步备份其中的数据。 - Library: 希望被备份但不希望被用户看到的数据。
- Library/Caches:缓存文件(保存应用程序再次启动过程中需要的信息),还有日志文件最好也放在这个目录。
-
Library/Preferences:偏好设置文件
NSUserDefaults
类创建的数据和plist
文件都放在这里。会被iTunes
备份。 -
tmp:保存应用运行时所需要的临时数据。不会被
iTunes
备份。iPhone重启时,会被清空。
b、用途
手动将数据存放到沙盒,其实就是自己在沙盒的某一个指定路径下新建一个保存数据的文件(.txt
、.plist
、.data
等格式的文件),然后向其中写我们需要保存的数据即可。但是沙盒中只能保存OC
中的基本数据,自定义的对象不能直接存入,但是可以通过归档存为.data
文件。
2、使用沙盒
a、获取沙盒的相关路径
- (void)getSandBoxPath
{
// 获取APP沙盒位置
NSString *homeDirectory = NSHomeDirectory();
NSLog(@"APP沙盒位置:%@",homeDirectory);
// 获取Document目录
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"获取Document目录:%@",documentPath);
// 获取Library目录
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"获取Library目录:%@",libraryPath);
// 获取Caches目录
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"获取Caches目录:%@",cachesPath);
// 获取Preferences目录 通常情况下,Preferences有系统维护,所以我们很少去操作它。
NSString *preferencesPath = [libraryPath stringByAppendingPathComponent:@"Preferences"];
NSLog(@"获取Preferences目录:%@",preferencesPath);
// 获取tmp目录
NSString *temporaryPath = NSTemporaryDirectory();
NSLog(@"获取tmp目录:%@",temporaryPath);
}
输出结果为:
2020-10-19 17:18:22.285852+0800 LocalCacheDemo[19320:4534437] APP沙盒位置:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7
2020-10-19 17:18:22.285952+0800 LocalCacheDemo[19320:4534437] 获取Document目录:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7/Documents
2020-10-19 17:18:22.286020+0800 LocalCacheDemo[19320:4534437] 获取Library目录:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7/Library
2020-10-19 17:18:22.286102+0800 LocalCacheDemo[19320:4534437] 获取Caches目录:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7/Library/Caches
2020-10-19 17:18:22.286177+0800 LocalCacheDemo[19320:4534437] 获取Preferences目录:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7/Library/Preferences
2020-10-19 17:18:22.286242+0800 LocalCacheDemo[19320:4534437] 获取tmp目录:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/5F00BF7F-23EA-4C9C-A8EF-4732EF5809D7/tmp/
b、获取NSDocumentationDirectory的位置
- (void)URLSForNSDocumentationDirectory
{
NSArray<NSURL *> *arrayURLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask];
if (arrayURLs)
{
for (NSURL *urlItem in arrayURLs)
{
NSLog(@"绝对路径:%@",urlItem.absoluteString);
NSLog(@"相对路径:%@",urlItem.path);
}
}
else
{
NSLog(@"arrayURLs 为空");
}
}
输出结果为:
2020-10-19 10:36:26.187053+0800 LocalCacheDemo[14176:4257416] 绝对路径:file:///Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/FE5390A3-5C8F-4EC1-97C8-30BF6E76F66D/Library/Documentation/
2020-10-19 10:36:26.187147+0800 LocalCacheDemo[14176:4257416] 相对路径:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/FE5390A3-5C8F-4EC1-97C8-30BF6E76F66D/Library/Documentation
c、获取NSDocumentDirectory的位置
- (void)URLSForNSDocumentDirectory
{
NSArray<NSURL *> *arrayURLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
if (arrayURLs)
{
for (NSURL *urlItem in arrayURLs)
{
NSLog(@"路径为:%@",urlItem.path);
NSData *bookmark = [self.bookMarkVC bookmarkForURL:urlItem];
NSLog(@"书签为:%@",bookmark);
}
}
else
{
NSLog(@"arrayURLs 为空");
}
}
输出结果为:
2020-10-19 11:12:48.180285+0800 LocalCacheDemo[14688:4284625] 路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/D8D2D473-93D4-48EF-84A5-DDA0CF81ED0C/Documents
2020-10-19 11:12:48.254197+0800 LocalCacheDemo[14688:4284625] 书签为:{length = 1156, bytes = 0x626f6f6b 84040000 00000410 30000000 ... 18010000 00000000 }
四、plist(属性列表)
1、简介
plist
是iOS 系统中特有的文件格式。我们常用的NSUserDefaults
偏好设置实质上就是plist
文件操作。plist
文件是用来持久化存储数据的。
我们通常使用它来存储偏好设置,以及那些少量的、数组结构比较复杂的不适合存储数据库的数据。比如,我们要存储全国城市名称和id
,那么我们要优先选择plist
直接持久化存储,因为更简单。
通过XML
文件的序列化方式保存在目录中。
如果对象是NSString
、NSDictionary
、NSArray
、NSData
、NSNumber
等类型,就可以使用writeToFile:atomically:
方法直接将对象写到属性列表文件中,而Plist
不能存储自定义对象。
plist
的文件名不能叫做“info”
、“Info”
之类的,这是因为与系统属性文件重名。
2、Bundle中的plist文件
- (void)useBundlePlist
{
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"LuckCoffee" ofType:@"plist"];
NSArray *dataArray = [[NSArray alloc] initWithContentsOfFile:plistPath];
// 直接打印数据
NSLog(@"文件内容为:%@",dataArray);
}
输出结果为:
2020-10-19 17:03:25.765280+0800 LocalCacheDemo[19082:4522201] 文件内容为:(
"East China Normal University",
LuckCoffee,
"Thirst for knowledge, be modest and foolish",
"Xiamen University",
XieJiaPei
)
3、沙盒中的plist文件
a、简介
项目中plist
文件是存储在沙盒的documents
中,所以要获取某个plist
文件,只需要知道其文件名就可以了,如下方式就可以获取并读取其中的内容,读取时通过对应类型的方式来获取plist
的数据。
一般plist
中的内容都是以NSArray
或NSDictionary
的形式保存。实际使用场景比如我们通过将用户的登录状态保存到plist
(没有系统会自动新建一个plist
文件), 再获取位于沙盒中刚刚存储的plist
中的信息。
其中atomically
表示是否需要先写入一个辅助文件,再把辅助文件拷贝到目标文件地址。这是更安全的写入文件方法,一般都写YES
。
b、写入数组
- (void)arrayPlist
{
NSArray *dataArray = @[@"East China Normal University",@"LuckCoffee",@"Thirst for knowledge, be modest and foolish"];
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *plistFilePath = [documentPath stringByAppendingPathComponent:@"Xiejiapei.plist"];
[dataArray writeToFile:plistFilePath atomically:YES];
// 直接打印数据
NSLog(@"文件内容为:%@",[[NSArray alloc] initWithContentsOfFile:plistFilePath]);
}
输出结果为:
2020-10-19 17:08:58.500832+0800 LocalCacheDemo[19151:4525986] 文件内容为:(
"East China Normal University",
LuckCoffee,
"Thirst for knowledge, be modest and foolish"
)
c、写入字典
- (void)dictionaryWriteToFile
{
// 我发现要写入plist文件,key必须为string类型
NSDictionary *dict = @{@"name": @"XieJiaPei", @"age": @22, @"4": @5};
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentPath stringByAppendingPathComponent:@"jia.plist"];
// 将路径转换为本地url形式
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
// writeToURL 的好处是,既可以写入本地url也可以写入远程url,苹果推荐使用此方法写入plist文件
if ( [dict writeToURL:fileUrl atomically:YES] )
{
NSLog(@"成功写入文件,路径为:%@",filePath);
}
NSDictionary *dictionaryFromFile = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"从文件中读取到的字典为:%@",dictionaryFromFile);
}
输出结果为:
2020-10-30 17:58:34.469548+0800 FoundationDemo[47972:1807344] 成功写入文件,路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/4E4809CA-E567-4D3A-8ADE-790075200303/data/Containers/Data/Application/62175E05-6FFD-4EEF-A872-3C08B6FBACB4/Documents/jia.plist
2020-10-30 17:58:34.469723+0800 FoundationDemo[47972:1807344] 从文件中读取到的字典为:{
4 = 5;
age = 22;
name = XieJiaPei;
}
五、NSUserDefault(偏好设置)
1、简介
-
NSUserDefaults
是轻量级的数据持久化,主要用来保存一些用户程序配置信息和偏好设置,比如保存用户名、密码、字体大小、是否自动登录,成功后会写入到library(preference)
中。 -
NSUserDefault
使用单例,并且是线程安全的,数据以键值对的形式保存在沙盒中(KVC)
,支持存储的数据类型是NSObject
(自定义对象)、NSString
、NSNumber
、NSArray
、NSDictionary
、NSData
。
2、使用NSUserDefault
a、存入数据
- (void)saveData
{
// 获得NSUserDefaults文件
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 向文件中写入内容
[defaults setObject:@"谢佳培" forKey:@"name"];
[defaults setInteger:22 forKey:@"age"];
[defaults setFloat:18.0f forKey:@"text_size"];
UIImage *image = [UIImage imageNamed:@"luckcoffee.JPG"];
NSData *imageData = UIImageJPEGRepresentation(image, 100);
[defaults setObject:imageData forKey:@"image"];
// synchronize强制存储,并非必要,因为系统会默认调用,但是你确认了就会马上存储
[defaults synchronize];
}
b、取出数据
- (void)readData
{
// 获得NSUserDefaults文件
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults valueForKey:@"name"];
NSInteger age = [defaults integerForKey:@"age"];
UIImage *image = [defaults valueForKey:@"image"];
float textSize = [defaults floatForKey:@"text_size"];
NSLog(@"姓名:%@",name);
NSLog(@"年龄:%ld",(long)age);
NSLog(@"图片:%@",image);
NSLog(@"字号:%f",textSize);
}
c、调用方法后输出结果
2020-10-16 18:25:07.556885+0800 LocalCacheDemo[97416:3486193] 姓名:谢佳培
2020-10-16 18:25:07.556991+0800 LocalCacheDemo[97416:3486193] 年龄:22
2020-10-16 18:25:07.557075+0800 LocalCacheDemo[97416:3486193] 图片:{length = 507840, bytes = 0xffd8ffe0 00104a46 49460001 01000048 ... 0cfeade5 f89fffd9 }
2020-10-16 18:25:07.557145+0800 LocalCacheDemo[97416:3486193] 字号:18.000000
六、NSKeyedArchiver(归档)
1、简介
父类是NSSecureCoding
,归档成功会保存在Documents
下,以".archive"
后缀保存。
如果对象是NSString
、NSDictionary
、NSArray
、NSData
、NSNumber
等类型,可以直接用- NSKeyedArchiver
进行归档和恢复。不是所有的对象都可以直接用这种方法进行归档实现序列化,只有遵守了NSSecureCoding
协议的对象才可以。
encodeWithCoder:
每次归档对象时,都会调用这个方法指定如何归档对象中的每个实例变量,可以使用- encodeObject:forKey:
方法归档实例变量 。
initWithCoder:
每次从文件中恢复(解码)对象时,都会调用这个方法指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey
方法解码实例变量。
如果父类也遵守了NSSecureCoding
协议,应该在encodeWithCoder:
方法中加上一句:[super encodeWithCode:encode];
确保继承的实例变量也能被编码,即也能被归档,应该在initWithCoder:
方法中加上一句:self = [super initWithCoder:decoder];
确保继承的实例变量也能被解码,即也能被恢复。
相较NSUserDefaults
或者是 plist
都不能对自定义的对象进行存储,NSKeyedArchiver
需要在自定义的类中需要在.h
文件中加入< NSSecureCoding >
,在.m
文件众实现两个代理方法,这两个代理方法将会被自动调用。
2、基本数据类型的归档
a、归档数组
- (void)archiveArray
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
NSString *archivePath = [documentPath stringByAppendingPathComponent:@"Array.archive"];
NSLog(@"归档路径为:%@",archivePath);
NSArray *arrayArchive = @[@"1997",@"2008",@"2020"];
NSError *error;
NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:arrayArchive requiringSecureCoding:YES error:&error];
[archiveData writeToFile:archivePath atomically:YES];
}
b、解档数组
- (void)decodeArray
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
NSString *archivePath = [documentPath stringByAppendingPathComponent:@"Array.archive"];
NSData *decodeData = [NSData dataWithContentsOfFile:archivePath];
NSError *error;
NSArray *arrayDecode = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:decodeData error:&error];
NSLog(@"解档后数据为:%@",arrayDecode);
}
c、输出结果
2020-10-16 17:53:49.316433+0800 LocalCacheDemo[96957:3461934] 归档路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/E58B7018-F68F-46B1-8907-BC227A11A4CE/Documents/Array.archive
2020-10-16 17:53:49.318272+0800 LocalCacheDemo[96957:3461934] 解档后数据为:(
1997,
2008,
2020
)
3、自定义对象的归档
a、NSSecureCoding 协议
@interface ArchivePerson : NSObject<NSSecureCoding>
@property (nonatomic,copy) NSString* name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) float height;
@end
b、encodeWithCoder 归档
encodeWithCoder
用来说明如何将对象编码到归档中。如果是类就用encodeObject: forKey:
,如果是普通的数据类型就用这种方式,如encodeInt: forKey:
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeInt:self.age forKey:@"age"];
[encoder encodeFloat:self.height forKey:@"height"];
}
c、initWithCoder 解档
说明如何进行解档来获取一个新对象。如果是类就用decodeObjectForKey:
,如果是普通的数据类型就用这种方式,如decodeIntForKey:
- (id)initWithCoder:(NSCoder *)decoder
{
self.name = [decoder decodeObjectForKey:@"name"];
self.age = [decoder decodeIntForKey:@"age"];
self.height = [decoder decodeFloatForKey:@"height"];
return self;
}
d、supportsSecureCoding 权限
跟requiringSecureCoding
属性有关,这个需要返回YES
,否则归档是失败的,返回的data
为空。
+ (BOOL)supportsSecureCoding
{
return YES;
}
e、归档对象
把该自定义的类对象编码到 NSData
中。
- (void)archivePerson
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
NSString *archivePath = [documentPath stringByAppendingPathComponent:@"Person.archive"];
NSLog(@"归档路径为:%@",archivePath);
ArchivePerson *person = [[ArchivePerson alloc] init];
person.name = @"谢佳培";
person.age = 22;
person.height = 1.71f;
NSError *error;
NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:&error];
[archiveData writeToFile:archivePath atomically:YES];
}
f、解档对象
再从文件中进行读取归档数据进行解档。
- (void)decodePerson
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
NSString *archivePath = [documentPath stringByAppendingPathComponent:@"Person.archive"];
NSData *personArchive = [NSData dataWithContentsOfFile:archivePath];
NSError *error;
ArchivePerson *personDecode = (ArchivePerson *)[NSKeyedUnarchiver unarchivedObjectOfClass:[ArchivePerson class] fromData:personArchive error:&error];
NSLog(@"解档后人物名称为:%@",personDecode.name);
}
g、输出结果
2020-10-16 17:34:24.166689+0800 LocalCacheDemo[96759:3449134] 归档路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/6F0786C9-C7AD-4778-B831-95F42F0519DF/Documents/Person.archive
2020-10-16 17:34:31.264288+0800 LocalCacheDemo[96759:3449134] 解档后人物名称为:谢佳培
七、SQLite
1、简介
a、定义
SQLite
是中小型数据库,比较原始,操作比较复杂,使用的是C的函数对数据库进行操作,但是SQLite
可控性更强,并且能够跨平台。 在MacOS
中一般使用sqliteManager
软件可以很清晰的看到数据库的内容和进行增删改查操作。也可以执行各种的sql
语句。
b、步骤
-
sqlite_open
函数打开数据库 -
prepare_v2
函数预处理SQL
语句 -
bind_text
函数绑定参数 -
step
函数执行 SQL语句,遍历结果集 -
column_text
等函数提取字段数据 -
finalize
和close
函数释放资源,关闭数据库
1、引入import sqlite3
模块,声明C指针类型变量db COpaquePointer
类型 映射到C指针类型,将NSString
类型转换为C接受的char*
类型数据。
2、sqlite_open
函数打开数据库(数据库文件的完整路径char*
,sqlite3
指针变量db
地址),返回值等于常量SQLITE_OK
打开数据库成功。
3、sqlite_exec
执行create table
创建数据库表。在表不存在时候创建表 ,建立表SQL
语句,再将语句转换为char*
,执行sql
语句(sqlite3
指针变量db
地址,要执行的sql
语句,要回调的函数,执行出错的字符串)。
4、sqlite_close
释放资源,数据操作执行完成。多次使用了该函数 ,数据库打开失败时候,SQL语句执行失败的时候 和执行成功的时候。
2、SQLite的增删改查
导入SQLite
#import <sqlite3.h>
@interface SQLiteManager()
@property (nonatomic,assign) sqlite3 *dataBase;
@end
a、打开数据库
- (BOOL)openDB
{
// app内数据库文件存放路径为沙盒
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dataBasePath = [documentPath stringByAppendingPathComponent:@"sqliteDemo.sqlite"];
if (sqlite3_open(dataBasePath.UTF8String, &_dataBase) != SQLITE_OK)// 数据库打开失败
{
return NO;
}
else// 数据库打开成功则创建表
{
// 用户表
NSString *creatUserTable = @"CREATE TABLE IF NOT EXISTS 't_User' ( 'ID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,'name' TEXT,'age' INTEGER,'icon' TEXT);";
// 车表
NSString *creatCarTable = @"CREATE TABLE IF NOT EXISTS 't_Car' ('ID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,'type' TEXT,'output' REAL,'master' TEXT)";
// 项目中一般不会只有一个表
NSArray *SQL_ARR = [NSArray arrayWithObjects:creatUserTable,creatCarTable, nil];
return [self creatTableExecSQL:SQL_ARR];
}
}
b、用sql语句数组创建表
- (BOOL)creatTableExecSQL:(NSArray *)SQL_ARR
{
for (NSString *SQL in SQL_ARR)
{
if (![self execuSQL:SQL])// 创建表失败
{
return NO;
}
}
return YES;
}
c、执行Sql语句
- (BOOL)execuSQL:(NSString *)SQL
{
// 参数一:数据库对象 参数二:需要执行的SQL语句 其余参数不需要处理
char *error;
if (sqlite3_exec(self.dataBase, SQL.UTF8String, nil, nil, &error) == SQLITE_OK)
{
return YES;
}
else
{
NSLog(@"SQLiteManager执行SQL语句出错:%s",error);
return NO;
}
}
d、查询sql
-(NSArray *)querySQL:(NSString *)SQL
{
/**1、准备查询
* 参数一:数据库对象
* 参数二:查询语句
* 参数三:查询语句的长度:-1
* 参数四:句柄(游标对象)
*/
sqlite3_stmt *stmt = nil;
if (sqlite3_prepare_v2(self.dataBase, SQL.UTF8String, -1, &stmt, nil) != SQLITE_OK)
{
NSLog(@"准备查询失败!");
return NULL;
}
// 2、准备成功,开始查询数据
// 定义一个存放数据字典的可变数组
NSMutableArray *dictMutableArray = [[NSMutableArray alloc] init];
while (sqlite3_step(stmt) == SQLITE_ROW)
{
// 获取表中所有列数(字段数)
int columnCount = sqlite3_column_count(stmt);
// 定义存放字段数据的字典
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (int i = 0; i < columnCount; i++)
{
// 取出i位置列的字段名,作为字典的键key
const char *cKey = sqlite3_column_name(stmt, i);
NSString *key = [NSString stringWithUTF8String:cKey];
// 取出i位置存储的值,作为字典的值value
const char *cValue = (const char *)sqlite3_column_text(stmt, i);
NSString *value = [NSString stringWithUTF8String:cValue];
// 将此行数据中此字段中key和value包装成字典
[dict setObject:value forKey:key];
}
[dictMutableArray addObject:dict];
}
return dictMutableArray;
}
e、关闭数据库
- (BOOL)closeSqlite
{
if (sqlite3_close(self.dataBase) != SQLITE_OK)
{
return NO;
}
else
{
return YES;
}
}
3、使用SQLite
a、使用方式
- (void)viewDidLoad
{
[super viewDidLoad];
// 打开数据库
[[SQLiteManager shareInstance] openDB];
// 执行插入语句
NSString *insertSql = @"insert into t_User (name,age,icon) values ('XieJiaPei',22,'http://...');";
[[SQLiteManager shareInstance] execuSQL:insertSql];
// 执行查询语句
NSString *querySql = @"select name,age,icon from t_User;";
NSArray *queryResultArray = [[SQLiteManager shareInstance] querySQL:querySql];
NSLog(@"查询结果为:%@",queryResultArray);
// 关闭数据库
[[SQLiteManager shareInstance] closeSqlite];
}
b、输出结果
2020-10-21 11:09:26.885488+0800 LocalCacheDemo[30276:5074285] 查询结果为:( {
age = 22;
icon = "http://...";
name = XieJiaPei;
}
)
八、CoreData
1、简介
a、作用
不需要借助第三方框架。Core Date
实际上是对SQLite
的封装,提供了更高级的持久化方式。在对数据库操作时,不需要使用sql
语句,也就意味着即使不懂sql
语句,也可以操作数据库中的数据。
在各类应用开发中使用数据库操作时通常都会用到 (ORM)
“对象关系映射”,Core Data
就是这样的一种模式。ORM
是将关系数据库中的表,转化为程序中的对象,但实际上是对数据中的数据进行操作。
对象模型更加先进,能够描述继承、实现、关联、聚合和组成等复杂的关系,而关系模型只能描述一对一、一对多和多对多的关系 。 这两种模型之间的不和谐问题称为"阻抗不匹配”问 题,而 ORM可以解决。
在使用Core Data
进⾏行数据库存取并不需要手动创建数据库,创建数据库的过程完全由Core Data
框架自动完成,开发者需要做的就是把模型创建起来,具体数据库的创建不需要管。
b、关键类
NSManagedObject
- 通过
Core Data
从数据库中取出的对象,默认情况下都是NSManagedObject
对象 -
NSManagedObject
的工作模式有点类似于NSDictionary
对象,通过键-值对来存取所有的实体属性 -
setValue:forkey:
存储属性值(属性名为key
) -
valueForKey:
获取属性值(属性名为key
) - 每个
NSManagedObject
都知道自己属于哪个NSManagedObjectContext
NSManagedObjectContext
- 负责数据和应用库之间的交互(增删改查、保存等接口都在这个对象中)
- 所有的
NSManagedObject
都存在于NSManagedObjectContext
中,所以对象和context
是相关联的 - 每个
context
和其他context
都是完全独立的 - 每个
NSManagedObjectContext
都知道自己管理着哪些NSManagedObject
NSPersistentStoreCoordinator:
- 添加持久化存储库,
CoreData
的存储类型(比如SQLite
数据库就是其中一种) - 中间审查者,用来将对象图管理部分和持久化部分捆绑在一起,负责相互之间的交流(中介一样)
NSManagedObjectModel
-
Core Data
的模型文件
NSEntityDescription
-
用来描述实体:相当于数据库表中一组数据描述
-
持久化存储协调器 (PersistentStore Coordinator, PSC ):在持久化对象存储之上提供了一个接口,我们可以把它考虑成数据库的连接 。 对应类是
NSPersistentStoreCoordinator
。 -
被管理对象上下文 (ManagedObjectContext, MOC):在被管理对象上下文中可以查找、删除和插入对象 ,然后通 过栈同步到持久化对象存储 。 对应类是
NSManagedObjectContext
。 -
持久化对象存储 ( Persistent Object Store, POS ):执行所有底层的从对象到数据的转换,并负责打开和关闭数据文件 。 它有 3种持久化实现方式 :
SQLite
、 二进制文件和内存形式 。 -
xcdatamodeld文件:它是模型文件,利用它可视化地设计数据 库 ,生成实体类代码和
SQLite
数据库文件,与工程名相同的数据模型文件 <工程名>.xcdatamodeld
或者从打开的选择文件模板对话框中选择iOS-CoreData- Data Model
-
创建实体(entity):实体属性和实体关系等
2、通过视图方式创建Model
a、需求
假设现在需要创建一个Robot
的类,里面有基本数据类型id
和name
属性,还有另外一个自定义RobotArm
的属性arm
,那么该如何在CoreData
里面使用呢?
Class Robot: NSManagedObject
{
@NSManaged public var id: Int16
@NSManaged public var name: String?
@NSManaged public var arm: RobotArm?
}
b、创建一个Robot Entity

c、侧边栏的Codegen Options选项

Manual/None
选择Manual/None
就需要手动去创建对应的类文件(Robot+CoreDataClass
和 Robot+CoreDataProperties
)
Class Definition
使用了这个选项,不需要创建对应类的file
,编译完就能使用这个类。即选择Class Definition
会在编译时生成这两个文件,不需要自己定义对应的类型。
Category/Extension
使用类别/扩展选项,可以完全控制类文件,同时继续自动生成属性文件以使其与模型编辑器保持同步。即选择Category/Extension
,在编译时只会默认生成Robot+CoreDataProperties
文件,需要手动创建Robot+CoreDataClass
定义该类型
如果不是选中Class Definition
,选中了Manual/None
,怎么手动创建对应Model
类的文件?

d、继承关系

3、Core Data的增删改查操作
a、插入数据
- (void)insert
{
// 1. 获得context
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];// 强制转换
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
// 2. 找到实体结构,并生成一个实体对象
NSManagedObject *robot = [NSEntityDescription insertNewObjectForEntityForName:@"Robot" inManagedObjectContext:context];
[robot setValue:@555 forKey:@"id"];
[robot setValue:@"XieJiaPei" forKey:@"name"];
// NSEntityDescription实体描述,也就是表的结构
// 参数1:表名字,参数2:实例化的对象由谁来管理,就是context
NSManagedObject *robotArm = [NSEntityDescription insertNewObjectForEntityForName:@"RobotArm" inManagedObjectContext:context];
[robotArm setValue:@1010 forKey:@"id"];
[robotArm setValue:@10 forKey:@"numberOfFingers"];
[robot setValue:robotArm forKey:@"robotArm"];
// 3. 调用context保存实体,如果没有成功,返回错误信息
[self saveContext];
// 4. 察看文件的存储路径
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"察看文件的存储路径:%@",documentPath);
}
b、保存数据
- (void)saveContext
{
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];// 强制转换
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error])
{
NSLog(@"数据保存错误:%@", error.localizedDescription);
abort();
}
else
{
NSLog(@"save ok");
}
}
c、查询所有数据
- (NSArray *)findAll
{
// 1. 获得Entity
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];// 强制转换
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Robot" inManagedObjectContext:context];
// 2. 构造查询对象
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// 3. 构造排序对象
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"id" ascending:TRUE];
NSArray *sortDescriptors = @[sortDescriptor];
request.sortDescriptors = sortDescriptors;
// 4. 执行查询,返回结果集
NSError *error = nil;
NSArray *listData = [context executeFetchRequest:request error:&error];
// 5. 遍历结果集
for (NSManagedObject *enity in listData)
{
NSLog(@"id=%i name=%@ arm=%@",[[enity valueForKey:@"id"] intValue],[enity valueForKey:@"name"],[[enity valueForKey:@"robotArm"] valueForKey:@"numberOfFingers"]);
}
return listData;
}
d、通过主键查询并修改
- (void)modifyById:(int)robotID
{
// 1. 获得Entity
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];// 强制转换
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Robot" inManagedObjectContext:context];
// 2. 构造查询对象
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = entityDescription;
// 3. 构造查询条件,相当于where子句
fetchRequest.predicate = [NSPredicate predicateWithFormat: @"id = %@",robotID];
// 4. 执行查询,返回结果集
NSError *error = nil;
NSArray *listData = [context executeFetchRequest:fetchRequest error:&error];
// 5. 更新里面的值(find-replace)后存储
if (error == nil && [listData count] > 0)
{
//
NSManagedObject *obj = listData[0];
[obj setValue:@"FanYiCheng" forKey:@"name"];
NSLog(@"更新后的名称为:%@",[obj valueForKey:@"name"]);
[context save:nil];
}
// 6. 显示
[self findAll];
}
e、通过主键删除
- (void)removeById:(int)robotID
{
// 1. 获得Entity
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];// 强制转换
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Robot" inManagedObjectContext:context];
// 2. 构造查询对象
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// 3. 构造查询条件,相当于where子句
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %@",robotID];
[request setPredicate:predicate];
// 4. 执行查询,返回结果集
NSError *error = nil;
NSArray *listData = [context executeFetchRequest:request error:&error];
// 5. 删除后存储
if (error == nil && listData.count > 0)
{
NSManagedObject *noteManagedObject = [listData lastObject];
[context deleteObject:noteManagedObject];
[self saveContext];
}
// 6. 显示
[self findAll];
}
4、查看数据库
a、在输出的路径下找到数据库文件
2020-10-30 10:24:30.075940+0800 LocalCacheDemo[40514:1495596] save ok
2020-10-30 10:24:30.076053+0800 LocalCacheDemo[40514:1495596] 察看文件的存储路径:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/4E4809CA-E567-4D3A-8ADE-790075200303/data/Containers/Data/Application/9F5A4B22-5BD5-448A-8983-ECB3A2B0F7D3/Library
2020-10-30 10:34:14.038838+0800 LocalCacheDemo[40620:1503336] id=555 name=XieJiaPei arm=10

b、DB Brower for SQLite
打开可以看到数据库里面有几个表,忽略Z
的前缀,可以看到Robot
和 RobotArm
表,打开就可以看到数据是否保存成功。id / arm / name
属性已经保存了数据,ZARM
数据是 1,就是指向另外一张表RobotArm
的对象1。


九、NSCache
1、简介
a、作用
NSCache
时苹果官方提供的缓存类,具体使用和NSDictionary
类似,在AFNetworking
框架和SDWebImage
框架中被用来管理缓存。
-
NSCache
作为一个对象其作用就是为了在内存很低的时候达到自动释放对象的功能。建议接收到内存警告时主动调用removeAllObject
方法释放对象。 -
NSCache
是线程安全的,在多线程操作中,不需要对 Cache 加锁。 -
NSCache
的Key
只是做强引用,不是拷贝,不需要实现NSCopying
协议。
b、属性和方法
NSCache *cache = [[NSCache alloc] init]; // 创建
NSData *cacheData = [cache objectForKey:key]; // 获取值
cache.totalCostLimit = 100;// 缓存空间的最大成本,超出上限会自动回收对象。默认值是0没有限制
[cache setObject:data forKey:key]; // 保存值
[cache setObject:data forKey:key cost:50];// cost用于计算记录在缓冲中所有对象的总成本。当出现内存警告,或者超出缓存的成本上限时,缓存会开启一个回收过程,删除部分元素。
cache.countLimit = 100;// 能够缓存对象的最大数量,默认值是0没有限制
cache.evictsObjectsWithDiscardedContent = YES;// 标示是否回收废弃的内容,默认值是YES(自动回收)
[cache removeObjectForKey:key];// 通过key移除指定缓存对象
[cache removeAllObjects];// 移除所有缓存对象
// 该代理方法的调用会在缓存对象即将被清理的时候调用
// 1. - (void)removeObjectForKey:(id)key; 手动删除对象;
// 2 . 缓存对象超过了NSCache的属性限制;(countLimit 和 totalCostLimit )
// 3. App进入后台会调用;
// 4. 系统发出内存警告;
// 第一个参数是当前缓存(NSCache),不要修改该对象
// 第二个参数是当前将要被清理的对象,如果需要存储该对象,可以在此操作(存入Sqlite or CoreData)
- (void)cache:(NSCache *)cache willEvictObject:(id)obj;
2、缓存操作
a、添加缓存
- (void)addCache
{
for (int i = 1; i<10; i++)
{
// 在缓存中设置指定键名对应的值,并且指定回收成本,以便进行计算存储在缓存中对象的总成本,当出现内存警告或者超出总成本时,缓存就会进行删除部分元素的操作
NSString *str = [NSString stringWithFormat:@"在这里进行了存储数据:%@",@(i)];
[self.cache setObject:str forKey:@(i) cost:1];
}
}
b、检查缓存
- (void)checkCache
{
for (int i = 0; i < 10 ; i++)
{
NSString *str = [self.cache objectForKey:@(i)];
if (str)
{
NSLog(@"取出缓存中存储的数据:%@",@(i));
}
}
}
c、清理缓存
- (void)cleanCache
{
[self.cache removeAllObjects];
NSLog(@"清理缓存");
}
d、NSCacheDelegate协议
// 即将回收对象的时候进行调用,实现代理方法之前要遵守NSCacheDelegate协议
- (void)cache:(NSCache *)cache willEvictObject:(id)obj
{
NSLog(@"即将回收对象的时候进行调用:%@",obj);
}
3、使用方式
a、调用方法
- (void)viewDidLoad
{
[self createSubviews];
self.cache = [[NSCache alloc]init];
self.cache.totalCostLimit = 5;// 缓存大小
self.cache.delegate = self;
NSLog(@"缓存的名称:%@",self.cache.name);
NSLog(@"缓存对象:%@",self.cache);
}
b、输出结果
// 点击添加缓存后缓存1234,到5时达到限度回收1234,完成后继续缓存56789
2020-10-30 11:08:55.138804+0800 LocalCacheDemo[41244:1531714] 缓存的名称:
2020-10-30 11:08:55.138908+0800 LocalCacheDemo[41244:1531714] 缓存对象:<NSCache: 0x6000016aa780>
2020-10-30 11:08:56.124067+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:1
2020-10-30 11:08:56.124161+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:2
2020-10-30 11:08:56.124227+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:3
2020-10-30 11:08:56.124322+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:4
// 点击检查缓存后取出56789这些值
2020-10-30 11:12:15.977223+0800 LocalCacheDemo[41244:1531714] 取出缓存中存储的数据:5
2020-10-30 11:12:15.977368+0800 LocalCacheDemo[41244:1531714] 取出缓存中存储的数据:6
2020-10-30 11:12:15.977510+0800 LocalCacheDemo[41244:1531714] 取出缓存中存储的数据:7
2020-10-30 11:12:15.977617+0800 LocalCacheDemo[41244:1531714] 取出缓存中存储的数据:8
2020-10-30 11:12:15.977727+0800 LocalCacheDemo[41244:1531714] 取出缓存中存储的数据:9
// 点击清理缓存后清除所有值
2020-10-30 11:12:40.266000+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:5
2020-10-30 11:12:40.266143+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:6
2020-10-30 11:12:40.266260+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:7
2020-10-30 11:12:40.266350+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:8
2020-10-30 11:12:40.266467+0800 LocalCacheDemo[41244:1531714] 即将回收对象的时候进行调用:在这里进行了存储数据:9
2020-10-30 11:12:40.266538+0800 LocalCacheDemo[41244:1531714] 清理缓存
// 再点击检查缓存无值输出
十、BookMark
1、简介
BookMark
可以持久化保存文件的路径。所谓bookmark
,是一种 NSData
类型的数据,描述了文件路径的信息。将 bookmark
持久化保存,当应用重新启动后,可通过 bookmark
获取到对应文件路径的URL
,即使期间文件被重新命名或移动到其他路径。
2、使用BookMark
a、创建BookMark
-
NSURLBookmarkCreationSuitableForBookmarkFile
指创建的bookmark
需要包含用于创建Finder alias files
的属性 - 通过传入
key
的数组,指明哪些URL
资源需要保存到bookmark
中 - 参数
relativeURL
指bookmark
相对的URL
- (NSData *)bookmarkForURL:(NSURL *)url
{
NSError *error = nil;
// 创建创建一个 NSURL 对象对应的 bookmark
NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (error || (bookmark == nil))
{
// 处理错误
NSLog(@"发生了错误");
return nil;
}
return bookmark;
}
URLSForNSDocumentDirectory的bookMark输出结果
2020-10-19 11:12:48.180285+0800 LocalCacheDemo[14688:4284625] 路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/D8D2D473-93D4-48EF-84A5-DDA0CF81ED0C/Documents
2020-10-19 11:12:48.254197+0800 LocalCacheDemo[14688:4284625] 书签为:{length = 1156, bytes = 0x626f6f6b 84040000 00000410 30000000 ... 18010000 00000000 }
b、保存BookMark
- (void)writeBookMark:(NSData *)bookmark
{
NSError *error = nil;
// 指定一个 alias file 的路径,将 NSData 类型的 bookmark 持久化保存在其中,可以将这个 alias file 理解为一个 symbolic link
BOOL isSuccess = [NSURL writeBookmarkData:bookmark toURL:[NSURL URLWithString:@""] options:NSURLBookmarkCreationSuitableForBookmarkFile error:&error];
if (isSuccess)
{
NSLog(@"bookmark 成功持久化保存在alias file");
}
}
c、BookMark转为URL
-
relativeURL
:bookmark
相对的URL
-
isStale
:指定转换后,bookmark
是否失效
- (NSURL *)urlForBookmark:(NSData *)bookmark
{
BOOL bookmarkIsStale = NO;
NSError *error = nil;
// 通过解析 bookmark 转换成一个 NSURL 对象
NSURL* bookmarkURL = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:&bookmarkIsStale error:&error];
if (bookmarkIsStale || (error != nil))
{
NSLog(@"发生了错误");
return nil;
}
return bookmarkURL;
}
d、从 alias file 获取 bookmark
从创建的 alias file
获取bookmark
。
+ bookmarkDataWithContentsOfURL:error:
直接通过alias file
的 URL 获取保存在其中的 bookmark
对应的真正 URL
,options
参数指明了如何解析 bookmark
。
+ URLByResolvingAliasFileAtURL:options:error:
Demo
Demo在我的Github上,欢迎下载。
LocalCacheDemo
网友评论