ZDCSyncable (objective-c 版本)
为 Objective-C 中的普通对象提供撤销、重做和合并功能。
(还有一个 Swift 版本,请见 此处。)
由: ZeroDark.cloud: 基于区块链和 AWS 构建的用于您的应用的安全同步和消息框架。
撤销与重做
示例 #1
@interface FooBar: ZDCRecord // < Just extend ZDCRecord
// add your properties as usual
@property (nonatomic, copy, readwrite) NSString *someString;
@property (nonatomic, readwrite) NSUInteger someInt;
@end // That's it !
// And now you get undo & redo support (for free!)
FooBar *foobar = [[FooBar alloc] init];
foobar.someString = @"init";
foobar.someInt = 1;
[foobar clearChangeTracking]; // starting point
foobar.someString = @"modified";
foobar.someInt = 2;
NSDictionary *changeset = [foobar changeset]; // changes since starting point
NSDictionary *redo = [foobar undo:changeset error:nil]; // revert to starting point
// Current state:
// foobar.someString == "init"
// foobar.someInt == 1
[foobar undo:redo error:nil]; // redo == (undo an undo)
// Current state:
// foobar.someString == "modified"
// foobar.someInt == 2
通过容器类支持复杂对象
- ZDCDictionary
- ZDCOrderedDictionary
- ZDCSet
- ZDCOrderedSet
- ZDCArray
示例 #2
@interface FooBuzz: ZDCRecord // < Just extend ZDCRecord
// add your properties as usual
@property (nonatomic, readwrite) NSUInteger someInt;
// or use smart containers!
@property (nonatomic, readonly) ZDCDictionary *dict;
@end
FooBuzz *foobuzz = [[FooBuzz alloc] init];
foobuzz.someInt = 1;
foobuzz.dict[@"foo"] = @"buzz";
[foobuzz clearChangeTracking]; // starting point
foobuzz.someInt = 2;
foobuzz.dict[@"foo"] = @"modified";
NSDictionary *undo = [foobuzz changeset]; // changes since starting point
NSDictionary *redo = [foobuzz undo:undo error:nil]; // revert to starting point
// Current state:
// foobuzz.someInt == 1
// foobuzz.dict["foo"] == "buzz"
[foobuzz undo:redo error:nil]; // redo == (undo an undo)
// Current state:
// foobuzz.someInt == 2
// foobuzz.dict["foo"] == "modified"
合并
您还可以合并更改!(例如,从云端)
FooBuzz *local = [[FooBuzz alloc] init];
local.someInt = 1;
local.dict[@"foo"] = @"buzz";
[local clearChangeTracking]; // starting point
FooBuzz *cloud = [local copy];
NSMutableArray<NSDictionary*> *changesets = [NSMutableArray array];
// local modifications
local.someInt = 2;
local.dict[@"foo"] = @"modified";
[changesets addObject:[local changeset]];
// ^ pending local changes (not yet pushed to cloud)
// cloud modifications
cloud.dict[@"duck"] = @"quack";
// Now merge cloud version into local.
// Automatically take into account our pending local changes.
[local mergeCloudVersion:cloud withPendingChangesets:changesets error:nil];
// Merged state:
// local.someInt == 2
// local.dict["foo"] == "modified"
// local.dict["duck"] == "quack"
动机
与云端同步数据需要能够正确合并更改。而正确合并更改则需要了解更改内容。
这在教程中通常被一带而过,因此人们往往会忘记它...直到实际编写代码的时候。然后一切都会变得混乱不堪!
与云端同步对象意味着了解如何从多个设备合并更改。这比预期要难,因为默认情况下,这是您唯一要合并的信息
- 对象当前版本,如您在云端所见
- 当前对象的版本,正如它在您的数据库中一样
但是缺少了一些东西。如果两个版本之间的属性 someInt
不同,那么这可能意味着
- 仅由远程设备更改
- 仅由本地设备更改
- 双方设备都进行了更改
为了正确执行合并,您需要知道这个问题的答案。
缺少的是对本地对象所做的更改列表。也就是说,已经进行的更改,但尚未推送到云端的更改。有了这些信息,我们可以进行适当的合并。因为现在我们知道了
- 对象当前版本,如您在云端所见
- 当前对象的版本,正如它在您的数据库中一样
- 对本地对象所做的更改列表,包括更改的键和它们的原始值
因此,如果您要正确合并更改,您需要跟踪这些信息。您可以通过困难的方式(手动)或简单的方式(使用为您自动提供跟踪的基类)来完成此操作。无论如何,您仍然没有摆脱困境!
跟踪简单记录的更改稍微有些简单。也就是说,只有一个少量键/值对的对象。并且所有值都是原生的(数字、布尔值、字符串)。但是当您的应用程序变得更加高级,您需要更复杂的对象时怎么办呢?
如果您的某个属性是数组呢?或者字典?或者集合?
说实话,编写这些东西并不困难。这不是rocket science。但这确实需要大量的单元测试来正确处理所有的小边界情况。这意味着您可以自己编写这些单元测试,或者您可以使用已经由社区击败测试的开源版本。然后花更多的时间让你的应用程序变得出色。
入门
ZDCSyncableObjcC 通过 CocoaPods 提供。
CocoaPods
请在 Podfile 中添加以下内容
pod 'ZDCSyncableObjC'
然后像平常一样运行 pod install
。然后您可以通过
// using module-style imports:
@import ZDCSyncableObjC;
// or you can use classic-style imports:
#import <ZDCSyncableObjC/ZDCSyncableObjC.h>
额外功能:不可变性
所有 ZDCObject 子类都可以变得不可变。这包括所有容器类(例如 ZDCDictionary),以及您创建的任何 ZDCRecord 的自定义子类。
myCustomSwiftObject.makeImmutable() // Boom! Cannot be modified now!
这一概念类似于拥有独立的 NSDictionary/NSMutableDictionary 类。但这项技术使用起来更为简便。
(如果您想知道它是如何工作的:更改跟踪已经可以监控对象的更改。因此,一旦将对象标记为不可变,修改对象的尝试将抛出异常。如果您想进行修改,只需复制对象,然后修改副本即可。)