我有一个iOS应用程序,使用NSCoding持久化数据,更确切地说是NSKeyedArchiver.此应用程序已在App Store上提供.
我正在研究应用程序的第2版,数据模型应该改变.所以我需要处理数据模型迁移.我希望它由单元测试覆盖.
在我的测试中,我想用旧数据模型动态生成持久化数据,启动迁移并查看是否一切顺利.
目前,归档对象如下所示:
MyDataModelObject *object = ....
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:object forKey:key];
[archiver finishEncoding];
Run Code Online (Sandbox Code Playgroud)
问题是,MyDataModelObject可能会在应用程序的版本2中重新计算甚至删除.所以我不能在我的测试中使用这个类来生成"旧版本档案".
有没有办法在encodeWithCoder:不使用此类的情况下模拟类方法中的操作?
我想要实现的目标如下
- testMigrationFrom_v1_to_v2 {
// simulate an archive with v1 data model
// I want this part of the code to be as simple as possible
// I don't want to rely on old classes to generate the archive
NSDictionary *person = ... // { firstName: John, lastName: Doe }
NSDictionary *adress = ... // { …Run Code Online (Sandbox Code Playgroud) 我有一个界面:
#import <Foundation/Foundation.h>
@interface Picture : NSObject;
@property (readonly) NSString *filepath;
- (UIImage *)image;
@end
Run Code Online (Sandbox Code Playgroud)
和实施:
#import "Picture.h"
#define kFilepath @"filepath"
@interface Picture () <NSCoding> {
NSString *filepath;
}
@end
@implementation Picture
@synthesize filepath;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:filepath forKey:kFilepath];
}
- (UIImage *)image {
return [UIImage imageWithContentsOfFile:filepath];
}
@end
Run Code Online (Sandbox Code Playgroud)
我收到错误:ARC问题 - 'NSObject'没有可见的@interface声明选择器'initWithCoder:'
使用ARC时,NSCoding有什么不同吗?谢谢
对于数据恢复程序,我需要能够从NSArchiver编写的文件中提取值+类型,而无需访问Apple的CF/NS框架.
OS X file命令报告以下文件:
NeXT/Apple typedstream data, little endian, version 4, system 1000
Run Code Online (Sandbox Code Playgroud)
是否有关于如何编码这些文件的文档,或者是否有人提出可以解析它们的代码?
以下是此类数据的示例(也可:可下载):
04 0B 73 74 72 65 61 6D 74 79 70 65 64 81 E8 03 ..streamtyped...
84 01 40 84 84 84 12 4E 53 41 74 74 72 69 62 75 ..@....NSAttribu
74 65 64 53 74 72 69 6E 67 00 84 84 08 4E 53 4F tedString....NSO
62 6A 65 63 74 00 85 92 84 84 84 …Run Code Online (Sandbox Code Playgroud) 我正在尝试NSCodingswift中协议的基本实现,但似乎我无法成功地在对象被正确存档后取消归档.
这是我的尝试
import Cocoa
class User: NSObject, NSCoding {
var name: String
init(name: String) {
self.name = name
}
init(coder aDecoder: NSCoder!) {
self.name = aDecoder.decodeObjectForKey("name") as String
}
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeObject(name, forKey: "name")
}
}
let user = User(name: "Gabriele")
let encodedUser = NSKeyedArchiver.archivedDataWithRootObject(user)
let decodedUser = NSKeyedUnarchiver.unarchiveObjectWithData(encodedUser) as User
Run Code Online (Sandbox Code Playgroud)
在操场上运行它,它会在最后一行启动一个例外.这是细节
Execution was interrupted, reason: signal SIGABRT.
The process has been left at the point where it was interrupted, use "thread return -x" to return …Run Code Online (Sandbox Code Playgroud) 我有以下实现的类,NSCoding我已经创建了它的几个实例并将它们保存到文件中.
@interface BiscuitTin ()
@property NSString *biscuitType;
@property int numBiscuits;
@end
@implementation BiscuitTin
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.biscuitType = [coder decodeObjectForKey:@"biscuitType"];
self.numBiscuits = [coder decodeIntForKey:@"numBiscuits"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *coder) {
[coder encodeObject:self.biscuitType forKey:@"biscuitType"];
[coder encodeInt:self.numBiscuits forKey:@"numBiscuits"];
}
@end
Run Code Online (Sandbox Code Playgroud)
我现在决定将其表示numBiscuits为float(因为可能有部分吃过的饼干).更新属性类型并且encodeWithCoder工作正常但是当我尝试从文件加载现有实例时,应用程序崩溃,因为它正在尝试解码a int和a float.
有一个很好的方法来处理这个?理想情况下,我可以加载现有int值并将其转换为a float,但我不介意只使用默认值而不是崩溃.
我已经考虑decode在try-catch中包装适用的行,但在我的实际用例中,大约有50个左右的属性正在被编码/解码,并且不必为每个有变化的每个属性进行显式处理类型.
我的应用程序通过归档具有属性的类的实例将设置保存到iOS设备上的文件.该类使用NSCoding协议,因此,我使用encodeWithCoder对这些属性进行编码.然后我尝试使用诸如的命令将这些文件读回内存tempInt = decoder.decodeIntegerForKey("profileFlags") as Int
到目前为止,这种方法运行良好,但现在我需要能够存储其他属性并检索它们.本质上,这个存档对象的结构正在改变,但用户已经拥有旧结构(具有较少属性)的文件.如果用户有一个具有新结构的文件(附加属性),那么我想阅读它们.如果没有,我希望能够执行代码来处理它并提供默认值.
我尝试使用不存在的密钥tempInt = decoder.decodeIntegerForKey("nonExistentKey") as Int,期望得到一个零值,但它返回零.不幸的是,这是我真正需要一个可选的地方,因为0是一个有效的值.
我能找到的最接近的帮助文章是Swift:如果key不存在则decodeObjectForKey崩溃, 但这似乎不适用于此处.似乎decodeObjectForKey返回一个可选项,而decodeIntegerForKey返回一个Integer.
关于如何做到这一点的任何想法?
我创建了一个可重用的xib文件,其中包含一个表,并将其加载到如下TableView.swift文件中:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Bundle.main.loadNibNamed("TableView", owner: self, options: nil)
}
Run Code Online (Sandbox Code Playgroud)
我只是提到这一点来澄清我对如何加载xib文件并不感到困惑
我可以RootViewController.swift通过添加视图UIViewController并为该视图提供如下自定义类,轻松地在我的文件中加载可重用视图:
然后为这个视图创建一个这样的出口:
所以这是我的问题:
为什么我不能简单地添加这样的视图:
let tableViewView = TableView()
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我得到一个我不完全理解的错误:
在我的iPhone应用程序中,我从远程服务器获取json数据,使用Json Framework解析它并将其呈现在UIview中.还希望能够为用户提供将数据存储在设备上的选项,以便也可以脱机查看.我想知道是否直接存储json数据比制作一个对象更好或更差,然后使用NSCoding + NSKeyedArchiver保存它.我认为存储JSON字符串,因为它是的优点在于,它需要较少的空间比光盘存档对象,同时,在另一方面,通过存储归档的对象,你不必解析存储的数据每一次这样使用更少的内存.
有最好的整体选择吗?在这个问题上有没有最佳实践?json文件大小约为8KB.
我有两个实体,Chain和Step.Chain有一个属性steps,可以是多维Step实体数组,例如:
[
step,
step,
step,
[
step,
step
],
step
]
Run Code Online (Sandbox Code Playgroud)
每个Step对象都有content一个字符串属性.
如果我使用关系数据库,我只是将该数组存储为JSON,每个step都是step_id特定步骤.
我如何在Core Data中实现类似的功能?我认为我需要让这个Step类符合NSCoding协议,但那会是什么样子?我怎样才能使它只id在Chain.steps的最终值中存储它的等价物,即对它自己的引用?
编辑
下面的评论表明我nextStep在Step其他人之间包含了一对多关系(让我们称之为)Steps.然后我会使用这种关系从a中的一步到下一步Chain,因此Chain实体只需要包含Step序列中的第一步.这个问题是a Step可能属于多个Chain,因此它可能并不总是具有相同的nextStep值.
我的自定义对象符合NSCoding协议,具有以下方法
required init(coder decoder: NSCoder) {
super.init()
createdDate = decoder.decodeObject(forKey: "created_date") as? Date
userId = decoder.decodeInteger(forKey: "user_id")
}
func encode(with aCoder: NSCoder) {
aCoder.encode(createdDate, forKey: "created_date")
aCoder.encode(userId, forKey: "user_id")
}
Run Code Online (Sandbox Code Playgroud)
这是Swift 3中nscoding协议的正确方法名称,但应用程序因错误而崩溃 SwiftValue encodeWithCoder - unrecognized selector sent to instance
显然这种方法不可用,为什么不被认可?
参考https://developer.apple.com/reference/foundation/nscoding
这是我制作的归档方法
func encodeObject(_ defaults:UserDefaults, object:NSCoding?, key:String) {
if (object != nil) {
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object)
defaults.set(encodedObject, forKey: key)
} else {
defaults.removeObject(forKey: key)
}
}
Run Code Online (Sandbox Code Playgroud) nscoding ×10
ios ×7
objective-c ×5
swift ×3
iphone ×2
cocoa ×1
cocoa-touch ×1
core-data ×1
json ×1
macos ×1
nsarchiving ×1
storing-data ×1
swift3 ×1
uitableview ×1
unit-testing ×1
xcode8 ×1
xib ×1