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

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

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

XPYUserModel.h

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

NS_ASSUME_NONNULL_BEGIN

@interface XPYUserModel : XPYBaseModel  // 归、解档操作必须实现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 

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

你可能感兴趣的:(OC,iOS,技术,objective-c,xcode,swift,ios)