保存NSTextView
的内容(即其NSTextStorage
属性,本身就是NSAttributedString
时,我正在尝试优化存储空间。
将其另存为Data
(例如,使用rtfd(from:documentAttributes:)
方法,并作为Codable
结构的一部分)会导致字符串很大,比内容本身大得多,尤其是当将图像插入NSTextView
中。例如,插入200K图像将生成5MB的JSON文件。
旁注:Data
对象被直接编码而不是作为编码对象的属性,甚至更糟,因为它以小整数数组而不是任意字符串的形式编码。我不确定为什么,尽管可以通过将Data
插入简单的包装器结构中来防止这种情况。
奇怪的是,使用ZIP压缩实际的JSON文件仍会产生4MB的文件,仅增加20%,因此我不清楚200K图像如何变成如此庞大,难以压缩的编码字符串。
我想弄清楚使用NSAttributedString
协议有效存储Codable
的正确方法是什么。任何提示或建议都非常感谢。
我还想知道Codable
是否有有效的二进制编码选项。
答案 0 :(得分:7)
TL; DR:RTFD将图像编码为PNG,但是您可以使其编码为JPG,以节省空间。如果您有时间创建自定义格式,则可能会更好,更轻松。
NSAttributedString
可以编码为HTML,rtf,rtfd,纯文本,多种Office / Word格式等。鉴于每种格式都是一种官方格式,必须遵循官方规范,因此没有除了节省空间外,还有很多事情可以做:
OR
在受支持的格式中,RTFD确实确实适合您的用例,因为它包括对图像等附件的支持。随时尝试其他包含的格式,其描述在下面的“其他格式”中。
将其另存为数据(例如,使用rtfd(from:documentAttributes :)方法),并作为Codable结构的一部分,导致字符串很大,比内容本身大得多,尤其是在将图像插入NSTextView时。例如,插入200K图像将生成5MB的JSON文件。
要了解此处发生的情况,请尝试以下代码:
do {
let rtfd = try someAttributedString.rtfdFileWrapper(from: NSRange(location: 0, length: someAttributedString.length), documentAttributes: [:])
rtfd?.write(to: URL(fileURLWithPath: "/Users/yourname/someFolder/RTFD.rtfd"), options: .atomic, originalContentsURL: nil)
} catch {
print("\(error)")
}
致电rtfd(from:documentAttributes:)
时,您的电话Data
越来越平坦。然后可以将此平面数据编码到某个位置,然后读回到NSAttributedString
中。但是请不要误会:RTFD是一种软件包格式(“ D”代表目录)。因此,通过调用rtfdFileWrapper(from:documentAttributes:)
并将其写入扩展名为URL
的{{1}},我们可以看到rtfd
复制的实际包格式,但它是目录而不是目录原始数据。在Finder中,右键单击生成的文件,然后选择“显示包内容”。
RTFD软件包包含一个RTF文件(用于指定文本和属性)以及每个附件的副本。那么,为什么您的榜样那么大?在我的测试中,答案似乎是RTFD希望以PNG格式找到其图像。调用rtfd(from:documentAttributes:)
或rtfdFileWrapper(from:documentAttributes:)
时,任何图像附件似乎都被写为PNG文件,这会占用更多空间。发生这种情况是因为您的图像先被包裹在rtfd(from:documentAttributes:)
中,然后又被包裹在NSImage
中。 NSTextAttachment
能够以其他格式写出图像数据,包括PNG等较大的格式。
我假设您尝试的图像采用的是JPEG之类的压缩格式,并且NSImage
以PNG格式写入RTFD。
改为使用NSAttributedString
假设您对图像进行了压缩并且没有Alpha通道之类的信息,则应该能够创建包含JPEG
张图像的RTFD文件。
例如,我只用原始的JPG替换了生成的PNG图像,就可以将RTFD文件从12 MB以上(大图像)减小到2.8 MB。最初,这对于TextEdit是不可接受的,但是后来我将图像的文件扩展名更改为jpg
(即使它仍然是JPG),并且它接受了。
在代码中,它甚至更简单。您可能只需改变添加图像附件的方式就可以摆脱困境。
.png
然后,当您使用该// Don't do this unless you want PNG
let image = NSImage(contentsOf: ...) // NSImage will write to a larger PNG file
let attachment = NSTextAttachment()
attachment.image = image
// Do this if you want smaller files
let image = try? Data(contentsOf: ...) // This will remain in raw JPG format
let attachment = NSTextAttachment(data: image, ofType: kUTTypeJPEG as String) // Explicitly specify JPG
创建一个新的NSAttributedString
并将其附加到NSTextAttachment
时,写入RTFD数据将大大减少。
当然,如果您依靠Cocoa UI / API附加图像,则可能无法控制此过程。这可能会使过程更加困难,您可能需要通过交换图像来修改生成的数据。
由于无法控制附件添加过程并且需要平面数据,因此上述方法可能不方便。在这种情况下,自定义格式可能会更好。
没有什么可以阻止您设计自己的格式(二进制,文本,包等),然后为其编写编码器。您可以指定特定的图像格式或支持多种格式。由你决定。而且,除非您是高级文字处理程序,否则您可能不需要一直存储所有属性,例如font。
我还想知道Codable是否有有效的二进制编码选项。
首先,请注意NSTextStorage
是一个Objective-C类(在Apple平台上使用时),并且符合NSAttributedString
而不是NSSecureCoding
。
请注意,您无法扩展Codable
使其符合NSAttributedString
,因为Codable
的{{1}}要求只能通过确保初始化程序将包含在所有目录中来满足。子类。由于此类不是init(from:)
,这意味着只能由Decodable
来满足。必需的初始化程序只能在原始声明中指定,而不能在扩展名上指定。
因此,如果要使其符合final
,则需要使用包装器对象。 required init
对于获取需要编码的属性和原始字符应该会有所帮助,但是您还必须确保注意图像。
对于二进制编码,Codable
完全与格式无关,因此您可以编写符合enumerateAttributes(in:options:using:)
的对象,该对象可以执行您想要的任何事情,包括使用原始字节存储所有内容。
以下是其他受支持格式的简要介绍(按大小顺序)。在这些测试中,我在系统字体中使用了非常小的字符串Codable
。每个格式说明后(在括号中)是存储该字符串的字节数。
Coder
转换为HTML时,行间距等属性会丢失。 (536个字节)"Hello World! There's so much to see!"
时创建的最后,随着Foundation继续适应Swift而不是Objective-C,NSAttributedString
的编码体验应该会更好。您可以想象有一天NSKeyedArchiver
或类似的Swifty类型符合NSAttributedString
,然后可以与任何文件格式NSAttributedString
配对的情况。