你如何使用NSCoder编码和解码自定义类型?
例如,如何将NSCoder与" STATE
" 实例一起使用,其中:
typedef enum { ON, OFF } STATE;
Run Code Online (Sandbox Code Playgroud) 具体来说,我正在尝试找到图像路径.这将是一个非常有用的东西能够得到,并且据我发现没有人知道如何.我已经查看了生成的nib文件中的密钥,我可以在那里看到图像URL(test.jpg),但找不到获取它的密钥.键"UIImage"返回实际已构建的图像(通过调用上述init调用initWithCGImageStored:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
的神秘UIKit函数调用构造GetImageAtPath
),因此没有用.
我也尝试使用NSKeyedArchiver将UIImageView写入磁盘,这些值似乎都没有正确,也没有test.jpg值存在.
如果没人能解决这个问题 - 任何人都知道如何在二进制文件中读取文本?我可以通过nib读取并解析URL,这比什么都没有好,但无论我尝试什么格式,NSString的构造函数都会失败.
以NSObject方法-(id)awakeAfterUsingCoder:(NSCoder *)decoder
为例,文档说:
允许对象在解码后替换另一个对象.例如,表示字体的对象在解码时可以释放自身并返回具有与其自身相同的字体描述的现有对象.通过这种方式,可以消除冗余对象.
通常你会这样做
[self release];
return substitutedObject;
Run Code Online (Sandbox Code Playgroud)
使用ARC,你必须离开这条线.这不会泄漏吗?或者我应该只相信NSCoder对象为我发布原始对象?如果是这样,为什么你必须首先用非ARC代码明确释放自己?
self = nil
根据编译器文档中关于self的内容,我认为不正确:http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.self
memory-management objective-c nscoder automatic-ref-counting
这将加载一个数组
- (id)initWithCoder:(NSCoder*) coder
{
self = [super initWithCoder: coder];
if (self) {
myArray=[coder decodeObjectForKey:@"myArray"];
}
return self;
}
Run Code Online (Sandbox Code Playgroud)
调用此函数的代码是什么,以便可以加载数组?
我有大量的对象需要保存以供离线使用.目前,我使用创建NSCoder兼容类的对象和编码数据到文件离线可用.
所以在.h中我介绍了这些对象:
@interface MyClass : NSObject<NSCoding>{
NSNumber* myObject;}
@property(nonatomic,retain) NSNumber* myObject;
Run Code Online (Sandbox Code Playgroud)
在.m我做的是:
- (id) initWithCoder: (NSCoder *)coder {
if (self = [super init]) {
[self setMyObject: [coder decodeObjectForKey:@"myObject"]];
}
}
- (void) encodeWithCoder: (NSCoder *)coder {
[coder encodeObject: myObject forKey:@"myObject"];
}
Run Code Online (Sandbox Code Playgroud)
所以这个类只是带有getter和setter的虚拟存储.这里有更好的方法来进行解码/编码.我能以某种方式使用@dynamic或键值编码进行编码和解码吗?基本上我希望类中的所有变量都保存到文件中,并在程序启动时返回到对象.这种方法有效,但创建所有类需要花费时间和精力.
我需要跨设备传输单个对象.现在我正在将我的NSManagedObject转换为字典,将其存档并作为NSData发送.收到后,我正在取消归档.但我真的想通过归档和取消归档来转移NSManagedObject本身,而不是创建一个中间数据对象.
@interface Test : NSManagedObject<NSCoding>
@property (nonatomic, retain) NSString * title;
@end
@implementation Test
@dynamic title;
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.title = [coder decodeObjectForKey:@"title"]; //<CRASH
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.title forKey:@"title"];
}
@end
NSData *archivedObjects = [NSKeyedArchiver archivedDataWithRootObject:testObj];
NSData *objectsData = archivedObjects;
if ([objectsData length] > 0) {
NSArray *objects = [NSKeyedUnarchiver unarchiveObjectWithData:objectsData];
}
Run Code Online (Sandbox Code Playgroud)
上面代码的问题是.它在崩溃self.title
的initWithCoder
说法发送到实例无法识别选择.
title
不被识别为选择器.initWithCoder
吗?copyWithZone
吗?NSArchiver
自OS X 10.2起不推荐使用,并且在iOS上不可用AFAIK
在另一方面,NSKeyedArchiver
已知缺乏对速度和简明一部分(一些用户报告之间的100倍以上的性能差异NSKeyedArchiver
和NSArchiver
).我要归档的对象主要是NSObject
含子类NSMutableArray
的NSNumber
包含基本类型(主要是和对象double
).我不相信开销密钥存档意味着值得.
所以我决定NSCoder
在iOS上创建一个串行编码器的子类NSArchiver
.
我知道键控档案可能派上用场:向后兼容和其他细节,它可能是我最终会使用的,但我很想知道串行存档可以获得什么样的表现.坦率地说,我认为通过这样做我可以学到很多东西.所以我对替代解决方案不感兴趣;
我受到了Cocotron的来源的启发,提供了一个开源的NSArchiver
TLDR:我想要子类化NSCoder
来重建NSArchiver
我正在使用ARC,为iOS 6和7编译,现在假设是32位系统.
我现在对引用对象或字符串不感兴趣,我只使用a NSHashTable
(weakObjectsHashTable
)来防止类名被复制:类将在第一次遇到时被描述,然后通过引用引用.
我使用a NSMutableData
来构建存档:
@interface Archiver {
NSMutableData *_data;
void *_bytes;
size_t _position;
NSHashTable *_classes;
}
@end
Run Code Online (Sandbox Code Playgroud)
基本方法是:
-(void)_expandBuffer:(NSUInteger)length
{
[_data increaseLengthBy:length];
_bytes = _data.mutableBytes;
}
-(void)_appendBytes:(const void *)data length:(NSUInteger)length
{
[self _expandBuffer:length];
memcpy(_bytes+_position, …
Run Code Online (Sandbox Code Playgroud) 我认为问号中的public init?(coder aDecoder: NSCoder)
意思不是可选的。另外,当我覆盖它时,我发现根本不需要写问号。
那到底是什么意思呢?
-更新-
下面的评论帮助我弄清楚了它被称为“失败的初始化程序”,另一个使该概念更易于理解的示例是UIFont的自发init,因为UIFont可能不存在。
public /*not inherited*/ init?(name fontName: String, size fontSize: CGFloat)
Run Code Online (Sandbox Code Playgroud) 在iOS 12中,不推荐使用NSKeyedArchiver的初始化程序init(forWritingWith :).Xcode 10建议用新的初始化程序init(需要SecureCoding :)替换它.问题是此初始化程序仅设置NSCoder对象的requiresSecureCoding属性的值,但它不设置将包含编码数据的NSMutableData对象.以下是Apple提出的编码CKRecord元数据(CloudKit记录)的原始代码:
let data = NSMutableData()
let coder = NSKeyedArchiver.init(forWritingWith: data)
coder.requiresSecureCoding = true
record.encodeSystemFields(with: coder)
coder.finishEncoding()
Run Code Online (Sandbox Code Playgroud)
CKRecord类的encodeSystemFields方法需要NSKeyedArchiver对象(NSCoder子类),并且编码数据存储在与此对象关联的NSMutableData对象中.如果我用init(requiresSecureCoding :)初始化程序替换init(forWritingWith :)初始化程序,我得到一个NSKeyedArchiver对象,但是这个对象没有与任何NSMutableData对象关联,因此我没有得到记录的元数据.我不确定如何完成代码以将NSKeyedArchiver对象生成的数据转换为NSMutableData对象.
let lessons = Lessons(definition: "testo", photo: url)
SaveUtil.saveLessons(lessons: lessons!)
let x = SaveUtil.loadLessons()
Run Code Online (Sandbox Code Playgroud)
所以一切都编译并运行,但 x 为零......我试图使这个 ios12/swift 4.2 兼容,但不知道缺少什么。谢谢!
class SaveUtil {
static func saveLessons(lessons: Lessons) {
let data = try! NSKeyedArchiver.archivedData(withRootObject: lessons, requiringSecureCoding: false)
UserDefaults.standard.set(data, forKey: "lessons")
}
static func loadLessons() -> [Lessons]? {
let data = UserDefaults.standard.data(forKey: "lessons")
return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data!) as? [Lessons]
}
}
Run Code Online (Sandbox Code Playgroud) nscoder ×10
ios ×5
objective-c ×4
swift ×2
arrays ×1
black-box ×1
cocoa ×1
cocoa-touch ×1
core-data ×1
key-value ×1
persistence ×1
types ×1
uikit ×1
uiview ×1
xcode ×1