iOS 利用归档(解档)保存(获取)用户信息

9 篇文章 0 订阅
6 篇文章 0 订阅

       说起用户信息保存问题,每个开发者应该都有很多不一样的见解。iOS提供了多种本地数据持久化方式,常用的有沙盒机制、本地数据库等。使用最多,也最简单方便的估计就是NSUserDefaults了。NSUserDefaults也是沙盒机制的一种,他的沙盒路径是Library->Preferences,因为大家用的都多,这里就不介绍了。处于安全性考虑,一般我们保存的用户信息并不会包括账号、密码这类敏感数据(这类信息可以保存到钥匙串,或者加密以后再保存),博主这里说的用户信息指的就是类似用户昵称、头像、性别等对安全性要求不高的数据。

一、创建用户信息数据模型

XPYUserModel.h

/// XPYBaseModel是博主之前创建的数据模型的基类,实现了YYModel协议(使用YYModel第三方库)
#import "XPYBaseModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface XPYUserModel : XPYBaseModel <NSSecureCoding> // 归、解档操作必须实现NSCoding协议或者NSSecureCoding协议,我们当然使用保证数据安全性的NSSecureCoding

@property (nonatomic, copy) NSString *userId;
@property (nonatomic, copy) NSString *nickname;
@property (nonatomic, copy) NSString *age;

@end

NS_ASSUME_NONNULL_END

XPYUserModel.m


#import "XPYUserModel.h"

@implementation XPYUserModel

/// 必须实现的两个协议方法
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    [self yy_modelEncodeWithCoder:coder];
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
    return [self yy_modelInitWithCoder:coder];
}

/// 必须实现supportsSecureCoding方法,返回YES
+ (BOOL)supportsSecureCoding {
    return YES;
}

@end

/// 因为博主使用了YYModel,所以两个协议方法中只是调用了YYModel的方法,若是自己写代码,如下
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    [coder encodeObject:self.userId forKey:@"userId"];
    [coder encodeObject:self.nickname forKey:@"nickname"];
    [coder encodeInteger:self.age forKey:@"age"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
    self = [super init];
    if (self) {
        self.userId = [coder decodeObjectForKey:@"userId"];
        self.nickname = [coder decodeObjectForKey:@"nickname"];
        self.age = [coder decodeIntegerForKey:@"age"];
    }
    return self;
}

二、创建归解档工具类       

XPYArchiveManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface XPYArchiveManager : NSObject

/// 归档对象
/// @param object 对象
/// @param fileName 文件名
+ (BOOL)archeveObject:(id)object fileName:(NSString *)fileName;

/// 归档对象
/// @param object 对象
/// @param directoryPath 文件夹路径,默认DocumentDirectory路径
/// @param directoryName 下一级文件夹名称
/// @param fileName 文件名
+ (BOOL)archeveObject:(id)object directoryPath:(NSString * _Nullable)directoryPath directoryName:(NSString *_Nullable)directoryName fileName:(NSString *)fileName;

/// 对象解档
/// @param objectClass 对象类
/// @param fileName 文件名
+ (id)unarcheveObjectWithClass:(id)objectClass fileName:(NSString *)fileName;

/// 对象解档
/// @param objectClass 对象类
/// @param directoryPath 文件夹路径,默认DocumentDirectory路径
/// @param directoryName 下一级文件夹名称
/// @param fileName 文件名
+ (id)unarcheveObjectWithClass:(id)objectClass directoryPath:(NSString * _Nullable)directoryPath directoryName:(NSString *_Nullable)directoryName fileName:(NSString *)fileName;

@end

NS_ASSUME_NONNULL_END

XPYArchiveManager.m

#import "XPYArchiveManager.h"

@implementation XPYArchiveManager

+ (BOOL)archeveObject:(id)object fileName:(NSString *)fileName {
    return [self archeveObject:object directoryPath:nil directoryName:nil fileName:fileName];
}
+ (BOOL)archeveObject:(id)object directoryPath:(NSString *)directoryPath directoryName:(NSString *)directoryName fileName:(NSString *)fileName {
    if (XPYIsEmptyObject(object)) {    // XPYIsEmptyObject是简单判空宏,这里就不贴代码了
        NSLog(@"对象为空");
        return NO;
    }
    BOOL supportSecureCoding = NO;
    if ([object conformsToProtocol:@protocol(NSSecureCoding)]) {    // 安全编码
        supportSecureCoding = YES;
    } else if ([object conformsToProtocol:@protocol(NSCoding)]) {   // 普通编码
        supportSecureCoding = NO;
    }
    NSError *error = nil;
    NSData *resultData = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:supportSecureCoding error:&error];
    if (error || !resultData) {
        NSLog(@"归档数据失败");
        return NO;
    }
    NSString *filePath = [self filePathWithDirectoryPath:directoryPath directoryName:directoryName fileName:fileName];
    if (!filePath) {
        return NO;
    }
    return [resultData writeToFile:filePath atomically:YES];
}

+ (id)unarcheveObjectWithClass:(id)objectClass fileName:(NSString *)fileName {
    return [self unarcheveObjectWithClass:objectClass directoryPath:nil directoryName:nil fileName:fileName];
}
+ (id)unarcheveObjectWithClass:(id)objectClass directoryPath:(NSString *)directoryPath directoryName:(NSString *)directoryName fileName:(NSString *)fileName {
    NSString *filePath = [self filePathWithDirectoryPath:directoryPath directoryName:directoryName fileName:fileName];
    if (!filePath) {
        return nil;
    }
    NSData *resultData = [NSData dataWithContentsOfFile:filePath];
    return [NSKeyedUnarchiver unarchivedObjectOfClass:objectClass fromData:resultData error:nil];
}

/// 获取文件路径
/// @param directoryPath 文件夹路径
/// @param directoryName 下一级文件夹名称
/// @param fileName 文件名称
+ (NSString *)filePathWithDirectoryPath:(NSString * _Nullable)directoryPath directoryName:(NSString * _Nullable)directoryName fileName:(NSString *)fileName {
    NSString *directory = directoryPath ? directoryPath : XPYDocumentDirectory;
    if (directoryName) {
        directory = [directory stringByAppendingPathComponent:directoryName];
    }
    if (!XPYCreateDirectoryAtPath(directory)) {    // XPYCreateDirectoryAtPath是创建文件夹内联方法
        NSLog(@"创建文件夹失败");
        return nil;
    }
    return [directory stringByAppendingPathComponent:fileName];
}

@end

创建文件夹内联方法

/// 创建文件夹
/// @param path 文件夹路径
static inline BOOL XPYCreateDirectoryAtPath(NSString *path) {
    BOOL isDirectory = NO;
    // 路径下是否已存在文件或者文件夹
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
    if (isExist && isDirectory) {
        // 已存在该文件夹
        return YES;
    }
    if (isExist && !isDirectory) {
        // 已存在文件,但不是文件夹,则删除文件
        [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
    }
    // 创建文件夹
    return [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];;
}

三、测试

/// Document文件夹路径
#define XPYDocumentDirectory NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject
/// Document路径下文件夹名称
static NSString * const XPYMomentsDocumentDirectoryName = @"XPYMoments";
/// 保存用户信息文件名
static NSString * const XPYMomentsUserDataFileName = @"xpy_moments_user.data";

XPYUserModel *user = [[XPYUserModel alloc] init];
user.userId = @"123";
user.nickname = @"xxpy";
user.age = 28;
BOOL isArchived = [XPYArchiveManager archeveObject:user directoryPath:XPYDocumentDirectory directoryName:XPYMomentsDocumentDirectoryName fileName:XPYMomentsUserDataFileName];
if (isArchived) {
    NSLog(@"归档成功");
    XPYUserModel *temp = [XPYArchiveManager unarcheveObjectWithClass:[XPYUserModel class] directoryPath:XPYDocumentDirectory directoryName:XPYMomentsDocumentDirectoryName fileName:XPYMomentsUserDataFileName];
    NSLog(@"解档结果:%@ %@ %@", temp.userId, temp.nickname, @(temp.age));
}

测试结果如下:

2020-07-24 11:11:30.507884+0800 XPYMoments[12718:542155] 归档成功

2020-07-24 11:11:30.508347+0800 XPYMoments[12718:542155] 解档结果:123 xxpy 28

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【课程特点】1、231节大容量课程:包含了SwiftUI的大部分知识点,详细讲解SwiftUI的方方面面;2、15个超级精彩的实例:包含美食、理财、健身、教育、电子商务等各行业的App实例;3、创新的教学模式:手把手教您SwiftUI用户界面开发技术,一看就懂,一学就会;4、贴心的操作提示:让您的眼睛始终处于操作的焦点位置,不用再满屏找光标;5、语言简洁精练:瞄准问题的核心所在,减少对思维的干扰,并节省您宝贵的时间;6、视频短小精悍:即方便于您的学习和记忆,也方便日后对功能的检索;7、齐全的学习资料:提供所有课程的源码,在Xcode 11 + iOS 13环境下测试通过; 更好的应用,更少的代码!SwiftUI是苹果主推的下一代用户界面搭建技术,具有声明式语法、实时生成界面预览等特性,可以为苹果手机、苹果平板、苹果电脑、苹果电视、苹果手表五个平台搭建统一的用户界面。SwiftUI是一种创新、简单的iOS开发中的界面布局方案,可以通过Swift语言的强大功能,在所有的Apple平台上快速构建用户界面。 仅使用一组工具和API为任何Apple设备构建用户界面。SwiftUI具有易于阅读和自然编写的声明式Swift语法,可与新的Xcode设计工具无缝协作,使您的代码和设计**同步。自动支持动态类型、暗黑模式、本地化和可访问性,意味着您的**行SwiftUI代码已经是您编写过的非常强大的UI代码了。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值