保存NSTextView
的内容(即其NSTextStorage
属性,本身就是NSAttributedString
)时,我正在尝试优化存储空间。
将其另存为Data
(例如,使用rtfd(from:documentAttributes:)
方法),并作为Codable
结构的一部分,会导致字符串很大,比内容本身大得多,尤其是在将图像插入NSTextView
时。例如,插入200K图像将产生5MB的JSON文件。
旁注:如果直接对Data
对象进行编码而不是将其作为编码对象的属性进行编码,则更糟,因为它以小整数数组而不是任意字符串的形式进行编码。我不知道为什么,尽管我可以通过将Data
插入简单的包装器结构中来防止这种情况。
奇怪的是,使用ZIP压缩实际的JSON文件仍会产生4MB的文件,仅增加20%,因此我不清楚200K图像如何变成如此庞大,难以压缩的编码字符串。
我想弄清楚使用NSAttributedString
协议(protocol)有效存储Codable
的正确方法是什么。任何提示或建议,不胜感激。
我也想知道Codable
是否有有效的二进制编码选项。
最佳答案
TL; DR:RTFD将图像编码为PNG,但您可以改为编码JPG以节省空间。如果您有时间创建自定义格式,则可能会更好,更轻松。 NSAttributedString
可以编码为HTML,rtf,rtfd,纯文本,多种Office/Word格式等。鉴于每种格式都是一种官方格式,必须遵循官方规范,因此在术语方面做不到很多除以下以外节省空间:
要么
方法1:RTFD
在支持的格式中,RTFD确实确实适合您的用例,因为它包括对图像等附件的支持。随意尝试其他包含的格式,下面的“其他格式”中对其进行了描述。
Saving it as Data, for example using the rtfd(from:documentAttributes:) method, and as part of a Codable structure, results in a very large string, much larger than the content itself especially when inserting an image into the NSTextView. For example, inserting a 200K image will result in a 5MB JSON file.
要了解这里发生了什么,请尝试以下代码:
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
扩展名的rtfd
中,我们可以看到rtfd(from:documentAttributes:)
复制的实际包格式,但它是目录而不是原始数据。在Finder中,右键单击生成的文件,然后选择“显示包内容”。RTFD软件包包含一个RTF文件(用于指定文本和属性)以及每个附件的副本。那么,为什么您的榜样那么大?在我的测试中,答案似乎是RTFD希望找到其PNG格式的图像。调用
rtfdFileWrapper(from:documentAttributes:)
或rtfd(from:documentAttributes:)
时,任何图像附件似乎都以PNG文件的形式写出,这会占用更多空间。发生这种情况是因为您的图像先被包裹在NSImage
中,然后才被包裹在NSTextAttachment
中。 NSImage
能够以其他格式写出图像数据,包括更大的格式,如PNG。我假设您尝试的图像采用JPEG之类的压缩格式,并且
NSAttributedString
以PNG格式将其写入RTFD。使用
JPEG
代替假设您对图像进行了压缩并且没有Alpha通道之类的信息,那么您应该能够使用
jpg
图像创建RTFD文件。例如,仅用原始的JPG替换生成的PNG图像,我就可以从超过12 MB的RTFD文件中缩小到2.8 MB(大图像)。最初,这对于TextEdit是 Not Acceptable ,但是后来我将图像的文件扩展名更改为
.png
(即使它仍然是JPG),并且它接受了它。在代码中,它甚至更简单。您可能只需要更改添加图像附件的方式就可以摆脱困境。
// 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
并将其附加到NSTextStorage
时,写入RTFD数据将大大减少。当然,如果您依靠Cocoa UI/API附加图像,则可能无法控制此过程。这可能会使处理过程变得更加困难,您可能需要通过交换图像来修改生成的数据。
方法2:自定义格式
由于无法控制附件添加过程并且需要平面数据,因此上述方法可能会带来不便。在这种情况下,自定义格式可能会更好。
没有什么可以阻止您设计自己的格式(二进制,文本,包等),然后为其编写编码器。您可以指定特定的图像格式或支持多种格式。由你决定。而且,除非您是一个出色的文字处理器,否则您可能不需要一直存储所有属性,例如font。
I am also wondering whether there is a valid binary encoding option for Codable.
首先,请注意
NSAttributedString
是一个Objective-C类(在Apple平台上使用时),并且符合NSSecureCoding
而不是Codable
。请注意,您不能扩展
NSAttributedString
以使其符合Codable
,因为只有通过确保初始化程序也将包括在所有子类中,才能满足init(from:)
的Decodable
要求。由于此类不是final
,所以这只能由required init
满足。只能在原始声明上指定必需的初始化程序,而不能在扩展名上指定。因此,如果要使其符合
Codable
,则需要使用包装器对象。 enumerateAttributes(in:options:using:)
应该有助于获取需要编码的属性和原始字符,但是您还需要确保也要注意图像。 至于二进制编码,
Codable
完全与格式无关,因此您可以编写符合Coder
的自己的对象,该对象可以执行所需的任何操作,包括使用原始字节存储所有内容。旁:其他格式
以下是其他受支持格式的简要说明(按大小顺序)。在这些测试中,我在系统字体中使用了非常小的字符串
"Hello World! There's so much to see!"
。在每种格式说明之后(在括号中)是存储该字符串的字节数。NSAttributedString
转换为HTML时,行间距等某些属性会丢失。 (536字节)NSKeyedArchiver
时创建的最后说明
最后,随着Foundation继续适应Swift而不是Objective-C,对
NSAttributedString
的编码体验应该会更好。您可以想象有一天NSAttributedString
或类似的Swifty类型与Codable
兼容,然后可以与任何文件格式Coder
配对。
关于swift - 如何使用数据和可编码的Swift优化NSAttributedString的存储?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53461875/