如何使用数据和Codable优化Swift中NSAttributedString的存储?

jmd*_*mbe 9 swift codable

我正在尝试在保存a的内容时优化存储空间NSTextView,即其NSTextStorage属性本身a NSAttributedString.

将其保存为Data,例如使用该rtfd(from:documentAttributes:)方法,并作为Codable结构的一部分,会产生非常大的字符串,比内容本身大得多,尤其是在将图像插入到内容中时NSTextView.例如,插入200K图像将生成5MB的JSON文件.

旁注:当Data对象直接编码而不是编码对象的属性时更糟糕,因为它是以小整数数组的形式编码而不是任意字符串.我不知道为什么,虽然我能够通过插入Data一个简单的包装结构来防止这种情况.

奇怪的是,使用ZIP压缩实际的JSON文件仍然会产生4MB的文件,只有20%的增益,因此我不清楚200K图像如何变成如此庞大,难以压缩的编码字符串.

我想弄清楚NSAttributedString使用Codable协议有效存储的正确方法是什么.非常感谢任何提示或建议.

我也想知道是否有一个有效的二进制编码选项Codable.

Mat*_*man 7

TL; DR:RTFD将图像编码为PNG,但您可以将其编码为JPG而不是节省空间.如果您有时间创建自定义格式,则自定义格式可能会更好更容易.

NSAttributedString 可以编码为HTML,rtf,rtfd,纯文本,各种Office/Word格式等.鉴于这些都是官方规格必须遵循的官方格式,所以没有太多可以做到的节省空间除了:

  1. 选择最适合您的用例并且占用空间最小的支持格式.

要么

  1. 编写自己的格式.

方法1:RTFD

在支持的格式中,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)")
}
Run Code Online (Sandbox Code Playgroud)

当你打电话时rtfd(from:documentAttributes:),你会变得平坦Data.然后可以在某处对此平坦数据进行编码并将其读回NSAttributedString.但不要搞错:RTFD是一种包格式("D"代表目录).因此,通过调用rtfdFileWrapper(from:documentAttributes:)并将其写入URL带有rtfd扩展名的a,我们可以看到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文件.

例如,只需将生成的PNG图像替换为原始JPG图像,我就可以从超过12 MB(大图像)获得低至2.8 MB的RTFD文件.这最初对TextEdit不敏感,但我随后将图像的文件扩展名更改为.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
Run Code Online (Sandbox Code Playgroud)

然后当你NSAttributedString用它创建一个新的NSTextAttachment并附加它时NSTextStorage,编写RTFD数据将显着变小.

当然,如果您依赖Cocoa UI/API来附加图像,则可能无法控制此过程.这可能会使过程变得更加困难,您可能需要通过交换图像来修改生成的数据.

方法2:自定义格式

由于没有控制附件添加过程并且需要平面数据,上面刚刚描述的方法可能是不方便的.在这种情况下,自定义格式可能会更好.

没有什么可以阻止你设计自己的格式(二进制,文本,包,等等),然后为它编写一个编码器.您可以指定特定的图像格式或支持多种图像格式.由你决定.除非你是一个奇特的文字处理器,否则你可能不需要像字体一样存储所有属性.

我也想知道Codable是否有一个有效的二进制编码选项.

首先,请注意,这NSAttributedString是一个Objective-C类(在Apple平台上使用时)并且符合NSSecureCoding而不是Codable.

请注意,您不能扩展NSAttributedString以符合Codable,因为只有通过保证初始化程序也包含在所有子类中才能满足init(from:)要求Decodable.由于这个类是非类的final,这意味着它只能被a满足required init.必需的初始化程序只能在原始声明中指定,而不能在扩展名中指定.

因此,如果要符合它Codable,则需要使用包装器对象.enumerateAttributes(in:options:using:)应该有助于获取需要编码的属性和原始字符,但您还需要确保注意图像.

对于二进制编码,Codable格式化是完全不可知的,因此您可以编写自己的对象,符合Coder您的要求,包括使用原始字节存储所有内容.

旁白:其他格式

以下是其他支持格式的快速概述(按大小排序).在这些测试中,我使用"Hello World! There's so much to see!"了系统字体中的非常小的字符串.在每个格式描述(括号中)之后是存储该字符串的字节数.

  • 纯文本可以将上述格式存储为36个字节(每个字符1个),但不保留属性或附件.(36个字节)
  • 如果您需要保留属性而不是附件,RTF似乎最轻量级.(331个字节)
  • HTML是下一个最轻的,但并不是真正的存储格式.根据我的经验,当转换为HTML时,某些属性(如行间距)会丢失NSAttributedString.(536字节)
  • NSKeyedArchiver如果您只需要与Apple平台兼容并且不喜欢上述格式,那么在您使用时制作的二进制Plist是一个很好的选择.此格式也支持图像,但通常仍然大于上面(和RTFD).(648字节)
  • Web存档是下一个尺寸,但我不建议使用它,因为WebKit已弃用它.Safari仍然使用它来做某些事情.(784字节)
  • Word ML可能仅对已经知道需要它的人有用.这种格式以及它下面的所有格式通常会有一堆样板,在添加文本时它将成为文件的较小百分比.(~1.2 MB)
  • Open Document(OASIS)比大多数Word格式都要小,但是如果没有充分的理由,你可能不会使用它.(~2.4 MB)
  • Office Open XML是您只有在需要该格式时才使用的另一种格式.(~3.5 MB)
  • Doc(Microsoft Word)与少量文本相比,此文件非常大.虽然我希望这种格式允许图像,但在我的测试中,当我添加一个时,文件大小实际上并没有增加.(~19.4 MB)
  • Mac Simple Text似乎总是会产生错误.(N/A)

最后的说明

最后,NSAttributedString随着Foundation继续适应Swift而不是Objective-C ,编码体验应该会变得更好.你可以想象一天NSAttributedString或某些类似的Swifty类型符合Codable开箱即用,然后可以与任何文件格式配对Coder.