BaseModel 2.6.4

BaseModel 2.6.4

测试已测试
语言语言 Obj-CObjective C
许可 zlib
发布最后发布2017年10月

Nick Lockwood维护。



BaseModel 2.6.4

  • 作者
  • Nick Lockwood

目的

BaseModel 为您的 iOS 或 Mac OS 项目提供了构建模型对象的基类。它节省了您编写样板代码的麻烦,并通过减少在模型实现中妥协的动力,鼓励良好的实践。

BaseModel 对象使用属性列表和 NSCoding 协议进行序列化。它不是为了与 Core Data 一起使用而设计的,尽管从原则上讲,如果需要,可以通过将 BaseModel 超类改为 NSManagedObject 来将此类扩展到与 Core Data 一起使用。

BaseModel 主要是为那些希望对自己的数据堆栈实现有更多控制的开发者设计的 Core Data 的替代品。BaseModel 允许您精确控制您的数据文件的位置和序列化,同时仍然提供足够的自动行为,以避免您反复编写相同的代码。

BaseModel 设计为与以下序列化库一起工作

  • HRCoder (https://github.com/nicklockwood/HRCoder)。HRCoder 提供了一种在可读/可编辑的格式中加载和保存数据的替代机制。HRCoder 允许您以标准格式指定您的数据文件,并避免了您需要覆盖 setWith... 方法的需求。使用 HRCoder 是完全可选的。关于它是如何工作的示例,请查看 HRTodoList 示例。

  • CryptoCoding 库 (https://github.com/nicklockwood/CryptoCoding)。当与 CryptoCoding 一起使用时,BaseModel 对象支持在磁盘上保存或加载时自动对整个对象进行 AES 加密。使用 CryptoCoding 是完全可选的。关于它是如何工作的示例,请查看 CryptoTodoList 示例。

  • FastCoding (https://github.com/nicklockwood/FastCoding)。FastCoding 提供了一种在快速、紧凑的二进制格式中加载和保存数据的替代机制。FastCoding 是 NSCoding 的替代品,生成的文件大小是普通 NScoded存档的一半,并在一半的时间内加载。使用 FastCoding 是完全可选的。关于它是如何工作的示例,请查看 FCTodoList 示例。

注意: HRCoder 和 CryptoCoding 都利用了 BaseModel 的 NSCoding 实现并调用 setWithCoder:encodeWithCoder: 方法。FastCoding 使用它自己的序列化实现,不使用这些方法。

支持的操作系统和 SDK 版本

  • 支持的构建目标 - iOS 8.0 / Mac OS 10.9 (Xcode 6.0,Apple LLVM 编译器 6.0)
  • 最早支持的部署目标 - iOS 5.0 / Mac OS 10.7
  • 最早兼容的部署目标 - iOS 4.3 / Mac OS 10.6

注意:“支持”表示已使用此版本对该库进行了测试。“兼容”表示库应在此iOS版本上运行(即,它不依赖于任何不可用的SDK功能),但不再对其进行兼容性测试,可能需要微调或修复错误才能正常运行。

ARC兼容性

从版本2.4起,BaseModel需要ARC。如果要在非ARC项目中使用BaseModel,只需将-fobjc-arc编译器标志添加到BaseModel.m类中。为此,请在目标设置中转到“构建阶段”选项卡,打开“编译源”组,双击列表中的BaseModel.m,然后在弹出窗口中输入-fobjc-arc。

如果您想将整个项目转换为ARC,请取消注释BaseModel.m中的#error行,然后在Xcode中运行“编辑”>“重构”>“转换到Objective-C ARC...”工具,并确保您希望使用ARC的所有文件都已勾选(包括BaseModel.m)。

线程安全

可以在多个线程中安全地创建和访问BaseModel实例,但是某些BaseModel操作是基于类进行同步的,因此在多个线程上并发创建BaseModel实例或访问共享实例可能导致意外的性能问题。

安装

要将在项目中使用BaseModel类,只需将BaseModel.h和.m文件拖放到项目中。BaseModel没有必需的依赖,但是您可以还希望包含可选的HRCoder(https://github.com/nicklockwood/HRCoder)、CryptoCoding(https://github.com/nicklockwood/CryptoCoding)和/或FastCoding(https://github.com/nicklockwood/FastCoding)库,以启用BaseModel的其他功能。

有一个单个的类,BaseModel,您应将其用作任何模型类型类的基类。BaseModel提供一组处理模型数据的核心功能,并通过BaseModel协议提供钩子,以最小的编码实现额外的定制行为。

方法

BaseModel类具有以下方法

- (void)setUp;

BaseModel的初始化例程相当复杂,遵循多步骤的过程。为了简化配置,BaseModel提供了一个setUp方法,该方法在调用任何其他方法之前调用来预配置实例。它仅在成功的[super init]之后调用,因此无需验证self是否为nil或调用[super setUp](除非您正在继承自己的BaseModel子类)。与init一样,setUp只在实例生命周期的开始时调用一次,因此可以安全地设置属性而无需释放它们现有的值(仅当您不使用ARC时相关)。您决不应该直接调用setUp,除非在调用[super setUp]时继承自己的BaseModel子类。

- (void)tearDown;

tearDown方法与setUp方法相辅相成。在对象被销毁时调用,但只有在setUp方法先被调用的情况下才会调用。这对于类在初始化之前被销毁的情况很有用,例如通过调用[[Model alloc] initWithObject:nil]创建的情况。通过将销毁逻辑放入tearDown而不是dealloc中,您可以避免调用removeObserver等的未平衡调用。您决不应该直接调用tearDown,除非在调用[super tearDown]的情况下(BaseModel的dealloc方法会自动调用)。除非您继承了自己的BaseModel子类,否则无需调用[super tearDown]

- (void)setWithDictionary:(NSDictionary *)dict;

如果使用字典(通常从JSON或Plist文件中加载)初始化BaseModel实例,则此方法将自动调用。BaseModel提供默认的setWithDictionary:实现,该实现尝试从传入的字典中自动设置类的属性,但您可以通过覆盖它来实现(例如,如果属性的名称或数据类型不匹配)。

- (NSDictionary *)dictionaryRepresentation;

此方法返回一个包含所有(非空)模型属性的字典。将初始化自Plist或JSON文件的模型转换为可以保存为这种文件形式的形式是一种有用方法。注意:无法保证此字典中的对象是Plist安全的,所以如果您打算生成Plist或JSON文件,最好使用HRCoding库,该库可以递归地将子对象转换为Plist/JSON安全的形式。

- (void)setWithXXX:(XXX *)xxx;

可以通过使用instanceWithObject:/initWithObject:方法使用任何类型的对象初始化BaseModel实例。要支持给定的对象类型,只需添加一个名为setWith[ClassName]:的方法,其中ClassName是正在设置的对象的类。例如,要使用NSArray初始化BaseModel,请添加名为setWithArray:setWithNSSet:的方法,它将自动调用。

- (void)setWithCoder:(NSCoder *)aDecoder;

此方法作为NSCoding实现的一部分由initWithCoder:调用。BaseModel通过检查您的类的属性提供自动实现NSCoding,因此除非您需要自定义解码行为,否则不需要实现此方法。此方法在setUp方法之后调用,并且在默认值使用setWith[ClassName]:加载之后可能被调用,因此不要假设类属性和实例变量在调用此方法时没有值。

- (void)encodeWithCoder:(NSCoder *)aCoder;

BaseModel类通过检查您的类的属性自动默认实现此方法,因此除非您需要自定义解码行为,否则不需要自己实现此方法。

+ (instancetype)instance;
- (instancetype)init;

创建类的自动释放实例或初始化保留实例。这些方法首先调用setUp,然后尝试从存在该文件的resourceFile初始化类。

+ (instancetype)instanceWithObject:(NSDictionary *)dict;
- (instancetype)initWithObject:(NSDictionary *)dict;

使用提供的对象创建类的实例并初始化它。当从应用程序包中的嵌入属性列表文件加载数据模型时,或从由Web服务返回的JSON数据中创建模型对象时,此方法很有用。此方法要求您的类中定义了适当的设置方法,其中设置器的名称为

setWith[ClassName]:
。例如,要使用NSArray初始化模型,您的BaseModel子类必须有一个名为setWithArray:的方法。此方法在调用
setWithClassName:
之前尝试从
resourceFile
初始化类(如果文件存在),因此如果您的资源文件包含序列化对象,则此方法将两次被调用(输入不同)。

+ (NSArray *)instancesWithArray:(NSArray *)array;

与使用数组参数调用instanceWithObject:类似,但在此处,它遍历数组中的每个项目,并通过为每个数组元素调用instanceWithObject:从每个对象创建一个实例。然后,作为新数组返回BaseModel实例。

+ (instancetype)instanceWithCoder:(NSCoder *)decoder;
- (instancetype)initWithCoder:(NSCoder *)decoder;

使用NSCoding协议从NSCoded存档初始化对象。此方法要求在您的子类上定义setWithCoder:方法。此方法在调用setWithCoder:之前尝试从resourceFile(如果存在该文件)初始化类,这允许您使用静态数据文件首先初始化对象,然后再从NSCoded存档加载其他属性。

+ (instancetype)instanceWithContentsOfFile:(NSString *)filePath;
- (instancetype)initWithContentsOfFile:(NSString *)filePath;

从文件中创建/初始化模型类的实例。如果文件是属性列表,则模型将使用适当的 setWith[ClassName]: 方法进行初始化,具体取决于 Plist 中根对象类型。如果文件是模型实例的 NSCoded 存档,则对象将使用 setWithCoder: 进行初始化。如果文件格式不被识别,或者未实现适当的 setWith... 函数,则该方法将返回 nil。

+ (instancetype)sharedInstance;

该方法返回模型对象的共享实例,因此任何 BaseModel 子类都可以很容易地通过此方法用作 '单例'。首次调用时,共享实例将延迟创建,因此在进行此操作之前没有开销。首先通过调用 setUp 创建共享实例,然后尝试加载由 resourceFile 方法定义的文件,最后加载由 saveFile 定义的文件,因此您的实例可能从不同的来源初始化了三次。

+ (BOOL)hasSharedInstance;

由于 sharedInstance 是一个懒加载构造器,因此您不能调用它来测试给定的类是否有共享实例。使用 hasSharedInstance 方法可以检查给定的模型是否有共享实例,而不会意外创建一个。

+ (void)setSharedInstance:(BaseModel *)instance;

此方法允许您用模型的其他实例替换当前的共享实例。这对于可能从文件重新加载或从网络服务下载的模型非常有用。当设置新实例时,将自动释放上一个共享实例。请注意,由于共享模型实例被替换,持有模型引用的对象需要更新。当调用此方法时,模型通过 NSNotificationCenter 广播 BaseModelSharedInstanceUpdatedNotification,因此为该事件设置观察者是一种确保任何保留引用都得到更新的好方法。您还可以将 BaseModel 共享实例设置为 nil,以回收 BaseModel 共享实例的内存,如果它们不再需要,或者在内存警告的情况下。

+ (void)reloadSharedInstance;

此方法使用由 saveFile 指定的文件重新加载模型的共享实例。此方法通过调用内部调用 setSharedInstance: 来替换 sharedInstance,因此有关更新引用的同样的注意事项也适用。

- (BOOL)writeToFile:(NSString *)path format:(BMFileFormat)format atomically:(BOOL)atomically;

此方法尝试使用指定的格式将模型对象序列化到指定的文件中。如果选择了除了 BMFileFormatKeyedArchive 之外的其他格式,则需要将相应的库添加到您的项目中,否则使用此方法将引发异常。路径可以是绝对路径或相对路径。相对路径将自动使用 ~/Library/Application Support/ 前缀。成功返回 YES,失败返回 NO。

- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomically;

此方法尝试使用由 +saveFormat 方法指定的格式将模型对象序列化到指定的文件中。路径可以是绝对路径或相对路径。相对路径将自动使用 ~/Library/Application Support/ 前缀。成功返回 YES,失败返回 NO。

+ (NSString *)newUniqueIdentifier;

此方法用于生成一个新的、全局唯一的标识符。每次调用时,它都使用 CFUUID API 创建一个全新的值,该值在任何设备上永远不会重复。您可以将此值存储在模型的一个属性中,以提供适合在加载和保存对象时维护对象引用的唯一标识符。然而,请注意,负责加载、保存和复制此 ID 的责任在于开发者。如果您在保存和加载模型时没有保留此值,则每次重新实例化对象时,它都会不同。

+ (NSString *)resourceFile;

此方法返回用于初始化对象每个实例的文件名。这通常是一个存储在应用程序包中的属性列表文件,是一种在不必在setUp方法中硬编码的情况下快速定义复杂默认数据集的优秀方法。这可以是完整路径或部分路径名。部分路径将被视为相对于应用程序包资源文件夹的相对路径。此方法的默认返回值是ClassName.plist,其中ClassName是模型类的名称,但你可以在自己的子类中重写此方法来更改文件名。

如果你还在怀疑,那么在创建模型实例的第一次加载此文件时会有少量开销,但是尽可能地缓存并重用文件以供随后创建的模型实例使用。对于没有初始化文件的模型,只要确保包中不存在名为ClassName.plist的文件,就不会进行初始化。不需要重写此方法并返回nil,除非你需要一个名为ClassName.plist的文件资源用于其他目的(在这种情况下,你也许应该只是重命名你的文件)。

+ (NSString *)saveFile;

此方法返回用于加载和保存类共享实例的文件路径。默认情况下,此方法返回ClassName.extension,其中ClassName是模型类的名称,'extension'是'plist'、'json'或'fast'之一,具体取决于在+saveFormat方法中指定的格式。你可以在自己的子类中重写此方法来更改文件名或指定一个明确的目录。如果你指定的是相对文件名或路径而不是绝对路径,它将自动填充~/Library/Application Support/

+ (BMFileFormat)saveFormat;

此方法返回用于保存类共享实例的文件格式。默认情况下,此方法返回BMFileFormatKeyedArchive,这意味着模型将使用NSKeyedArchiver/NSKeyedUnarchiver和NSCoding协议进行保存和加载,但你可以在自己的子类中重写此方法以更改文件类型。BaseModel通过利用额外的库(如CryptoCoding、FastCoding和HRCoder)支持多种替代序列化格式。请参阅下面的BMFileFormat选项。

- (BOOL)save;

尝试保存模型,如果成功则返回YES,如果保存失败则返回NO。此方法会检查被保存的实例是否是共享实例。如果是,它将使用NSCoding协议调用encodeWithCoder:将保存到由saveFile方法指定的文件。如果不是共享实例,它将抛出异常。你可以在子类中重写保存方法以提供对保存单个模型的实现。有几种方法(这就是为什么没有默认实现的原因)。

  1. 如果实例是较大对象图(其中根节点是同一类型或不同类型的BaseModel)的一部分,你可以在保存方法中修改以调用根节点的save,这样可以通过对任何节点调用save来保存整个树。

    - (void)save
    {
        [[RootModel sharedInstance] save];
    }
    
  2. 你可以通过使用writeToFile:方法和一个自定义文件路径实现自己的逻辑将类保存到文件中。

    - (void)save
    {
        [self writeToFile:@"some-unique-filename.plist" atomically:YES];
    }
    

    然后你可以使用以下方法稍后重新加载文件:

    MyModel *instance = [MyModel instanceWithContentsOfFile:@"some-unique-filename.plist"];
    

BMFileFormat选项

BaseModel可以以多种不同的格式保存文件。默认是NSKeyedArchiver,但也有其他选择,每种选择都有其优点和缺点。

BMFileFormatKeyedArchive

这是默认的保存格式。它使用NSKeyedArchiver将模型保存为二进制编码的属性列表。BaseModel完全支持NSCoding,因此此选项允许你仅通过少量额外的努力就保存几乎所有类型的对象。唯一的例外是你需要保存是struct或者NSCoding不支持的其他类型的属性,在这种情况下,你可能需要重写encodeWithCoder:/setWithCoder:以处理这些情况。

BMFileFormatXMLPropertyList

这将模型保存为XML属性列表。与BMFileFormatKeyedArchive保存的NSCoded文件不同,因为它具有可读性。非常适合保存希望在文本编辑器中查看或手动编辑的数据。然而,XML属性列表不包括类信息,因此您可能需要覆盖根或子模型类中的setWithDictionary:方法,以便正确加载生成的文件。您可能想考虑使用BMFileFormatHRCodedXML选项代替,该选项使用HRCoder库生成包含类信息的XML属性列表。

BMFileFormatBinaryPropertyList

这与BMFileFormatXMLPropertyList选项(具有相同的限制)相同,但将文件保存为二进制属性列表。它不能在文本编辑器中查看,但在属性列表编辑器中可以打开和编辑,例如Xcode内置的编辑器。

BMFileFormatJSON

这与BMFileFormatXMLPropertyList选项(具有相同的限制)类似,但将数据保存为JSON文件。

BMFileFormatUserDefaults

这与BMFileFormatBinaryPropertyList选项相同,但在调用save方法时,不是保存到文件,而是将模型保存在[NSUserDefaults standardUserDefaults]中。这是为您的项目引入强类型设置模型的一个很好的选项。

BMFileFormatKeychain

这与BMFileFormatBinaryPropertyList选项类似,但在调用save方法时,不是保存到文件,而是将模型安全地保存到密钥链中(这意味着每个属性都将在密钥链中单独编码为密码)。这对于表示用户登录凭据或其他安全数据的模型是一个很好的选项。使用此选项需要您包含FXKeychain库。如果您的对象包含不安全的属性列表的类,则需要启用FXKeychain的FXKEYCHAIN_USE_NSCODING选项,默认情况下此选项在Mac OS中是关闭的(但在iOS中是开启的)。请注意,密钥链存储空间有限,因此不应使用它来存储大量安全数据(对于此目的,请尝试BMFileFormatCryptoCoding选项)。

BMFileFormatCryptoCoding

此选项使用CryptoCoding库使用AES加密保存模型。在加密之前使用NSCoding保存模型,因此应自动工作。

BMFileFormatHRCodedXML

此选项使用HRCoder库将文件保存为可读的XML属性列表。文件包括所有对象类名,因此无需在setWithDictionary:方法中进行任何额外工作即可重新加载模型。

BMFileFormatHRCodedJSON

此选项使用HRCoder库以可读的JSON文件保存文件。文件包括所有对象类名,因此无需在setWithDictionary:方法中进行任何额外工作即可重新加载模型。

BMFileFormatHRCodedBinary

此选项使用HRCoder库将文件保存为二进制属性列表。文件不能在文本编辑器中查看,但如果使用属性列表编辑器(如Xcode中内置的编辑器)打开,可以手动读取和编辑。文件包括所有对象类名,因此无需在setWithDictionary:方法中进行任何额外工作即可重新加载模型。

BMFileFormatFastCoding

此选项使用FastCoding库以二进制文件保存文件。这与二进制属性列表类似,但保存和加载速度更快,并在磁盘上创建更小的文件。FastCoding与大多数属性类型自动工作,唯一的例外是需要保存结构体或KVC不支持的其他类型的属性,在这种情况下,您可能需要做一些额外工作来处理这些情况。

使用方法

BaseModel类是抽象的,不适合直接实例化。要实现模型对象,创建BaseModel类的新子类,并根据您的特定要求实现或覆盖需要自定义的方法。查看项目以获取如何实现此处的示例。

发行说明

版本 2.6.3

  • 升级以改进使用 FastCoding 2.3 时的性能
  • 更新测试以使用 Travis CI

版本 2.6.2

  • 修复使用 FXKeychain 反序列化子对象时的错误

版本 2.6.1

  • 修复在 -initWithObject: 传递 nil 值时出现的无限循环
  • 使用 BMFileFormatHRCodedJSON 格式保存模型现在可以正确工作

版本 2.6

  • 添加了 tearDown 方法以补充 setUp
  • save 方法现在返回 BOOL 来指示成功
  • BaseModel 现在可以自动将对象序列化为 Property List 或 JSON 文件
  • BaseModel 现在可以自动将实例保存到 NSUserDefaults 和密钥链中
  • 添加了 +allPropertyKeys 和 +codablePropertyKeys 公共属性
  • 将 NSNull 传递给 -initWithObject: 现在返回 nil 而不是崩溃
  • 从 BaseModel 构造函数返回 nil 现在不再在 +instancesWithArray 中崩溃
  • BaseModel 现在为 -debugDescription 方法提供了一个很好的默认实现

版本 2.5

  • 现在自动实现 NSCoding (-setWithCoder:, -encodeWithCoder:),因此无需使用 AutoCoding 库
  • 现在提供了一个默认实现 -setWithDictionary:,它使用属性检查自动将字典值映射到属性
  • 添加了对 FastCoding 协议的保存/加载模型的支持
  • 添加了 +saveFormat 方法,作为指定要用于保存的文件格式的更便捷方式
  • 通过消除内部 @synchronized 调用来改进多线程性能
  • 现在符合 -Weverything 警告级别

版本 2.4.4

  • 修复了在 Xcode 5 下的警告

版本 2.4.3

  • 为所有 obc_msgSend 调用添加了显式的函数指针转换。
  • 在调用 -writeToFile:atomically 时现在尊重原子参数
  • 现在符合 -Wextra 警告级别

版本 2.4.2

  • writeToFile:atomically: 现在返回一个布尔值,指示成功或失败。

版本 2.4.1

  • BaseModel 将不再尝试将资源文件作为 JSON 处理,除非文件具有 "json" 或 "js" 扩展名
  • 如有必要,添加了更健壮的警告,用于在支持 JSON 的平台以外的目标平台上使用 JSON

版本 2.4

  • 添加了对 JSON 格式的资源文件加载的支持
  • BaseModel 现在需要 ARC。有关升级的详细信息,请参阅 README 文件
  • 删除了 uniqueID 属性,因为它没有理由复杂化类接口
  • 纠正了文档中的几个错别字和不准确之处

版本 2.3.5

  • 修复了从另一个 BaseModel 实例的 init 方法中用资源文件初始化 BaseModel 实例时的问题

版本 2.3.4

  • 修复了资源文件缓存机制中的错误

版本 2.3.3

  • 添加了对 CryptoCoding 的支持
  • 更新以支持 iOS6 / Xcode 4.5

版本 2.3.2

  • 现在可以将其共享的 BaseModel 实例设置为 nil,这将允许您回收不再需要的 BaseModel 共享实例的内存,或遇到内存警告时

版本 2.3.1

  • 将构造函数返回类型更改为新的类型安全 instancetype 而不是 id,使得在 basemodel 单例上使用点语法属性访问器更容易

版本 2.3

  • 默认情况下删除了 uniqueID 属性,并用更灵活的 newUniqueIdentifier 类方法替换。要重新启用 uniqueID 属性,请向您项目的预处理器宏添加 BASEMODEL_ENABLE_UNIQUE_ID

版本 2.2.2

  • 添加了对 HRCoder 库的支持,该库使用 NSCoding 提供 human-readable 对象序列化和反序列化

版本 2.2.1

  • 修正了设置器名称生成逻辑中的小错误
  • 删除了过时的属性列表序列化方法

版本 2.2

  • 添加了新的 instancesWithArray: 方法,以便一次性加载模型数组。
  • 添加了 setWithString:, setWithNumber: 和 setWithData: 方法。
  • 将 instanceWithDictionary/Array: 和 initWithDictionary/Array: 方法替换为 instanceWithObject: 和 initWithObject

版本 2.1

  • 增加了对ARC编译目标的自动支持
  • 现在BaseModel被设计为与AutoCoding库协同工作,该库可完全自动通过NSCoding进行对象序列化
  • NSObject (BaseModel) 类别已从BaseModel库中删除。您现在可以在AutoCoding库中找到此功能(https://github.com/nicklockwood/AutoCoding)。
  • 修复了可能多次调用setUp方法的问题

版本 2.0

  • 主要API重设计。不推荐将使用BaseModel 1.1或更早版本的项目更新到2.0版

版本 1.1

  • 添加了mergeValuesFromObject方法。
  • 将documentsPath重命名为savePath。
  • 更新了加载和保存方法,将默认使用应用程序支持文件夹,而不是文档文件夹。
  • 修复了加载代码中nil对象的异常。
  • 修复了NSCoded加载逻辑中的缺陷。

版本 1.0

  • 首次发布