[更新以实现嵌套文件夹层次结构的低内存串行输出作为嵌套 JSON 对象文件]
现在您已经提供了更多细节,很明显原始问题陈述缺乏足够的细节,任何人都无法为您提供答案。您的问题实际上是一个古老的问题,即如何以内存有效的方式遍历层次结构,再加上 iOS JSON 库非常轻巧并且不容易支持深度层次结构的流式写入)。
最好的方法是使用一种称为访问者模式的技术。对于上面显示的每个 NSManagedObject 类型,实现一个称为访问者的协议,例如,每个对象的接口行应该如下所示:
@interface Folder : NSManagedObject <Visitable>
@interface Word : NSManagedObject <Visitable>
访问者协议应该为所有符合协议的对象定义一个方法调用。
@protocol Visitable <NSObject>
- (void)acceptVisitor:(id<Visitor>)visitor;
@end
您将定义一个访问者对象,该对象本身实现访问者协议。
@protocol Visitor <NSObject>
- (void)visitFolder:(Folder*)folder;
- (void)visitWord:(Word*)word;
@end
@interface JSONVisitor : NSObject <Visitor>
@property (nonatomic, strong) NSURL *streamURL;
- (void)startVisiting:(id<Visitable>)visitableObject;
@end
@implementation JSONVisitor
@property (nonatomic, strong) NSOutputStream *outputStream;
- (void)startVisiting:(id<Visitable>)visitableObject
{
if ([visitableObject respondsToSelector:@selector(acceptVisitor:)]
{
if (_outputStream == nil)
{
// more code required set up your output stream
// specifically as a JSON output stream.
// add code to either set the stream URL here,
// or set it when the visitor object is instantiated.
_outputStream = [NSOutputStream outputStreamWithURL:_streamURL append:YES];
}
[_outputStream open];
// Note 1a Bypass Apple JSON API which doesn't support
// writing of partial objects (doing so is very easy anyway).
// Write opening root object fragment text string to stream
// such as:
// {
// "$schema" : "http://myschema.com/draft-01/schema#Folder1",
// "name" : "Folder export",
// "created" : "2013-07-16T19:20:30.45+01:00",
// "Folders" : [
[visitableObject acceptVisitor:self];
// Note 1b write closing JSON root object
// e.g.
// ]
// }
[_outputStream close];
}
}
- (void)visitFolder:(Folder*)folder
{
// Note 2a Bypass Apple JSON API which doesn't appear to support
// writing of partial objects (Writing JSON is very easy anyway).
// This next step would be best done with a proper templating system,
// but for simplicity of illustration I'm suggesting writing out raw
// JSON object text fragments.
// Write opening JSON Folder object fragment text string to stream
// e.g.
// "Folder" : {
if ([folder.folders count] > 1) {
// Write opening folder array fragment to stream e.g.
// "Folders" : [
// loop through folder member NSManagedObjects here
// (note defensive checks for nulls not included).
NSUInteger count = 0;
for (Folder *nestedFolder in folder.folders)
{
if (count > 0) // print comma to output stream
[nestedFolder acceptVisitor:self];
count++;
}
// write closing folders array to stream
// ]
}
if ([folder.words count] > 1) {
// Write opening words array fragment to stream e.g.
// "Words" : [
// loop through Word member NSManagedObjects here
// (note defensive checks for nulls not included).
NSUInteger count = 0;
for (Word *nestedWord in folder.words)
{
if (count > 0) // print comma to output stream
[nestedFolder acceptVisitor:self];
count++;
}
// write closing Words array to stream
// ]
}
// Print closing Folder object brace to stream (should only be followed
// a comma if there are more members in the folder this object is contained by)
// e.g.
// },
// Note 2b Next object determination code here.
}
- (void)visitWord:(Word*)word
{
// Write to JSON stream
[NSJSONSerialization writeJSONObject:word toStream:_outputStream options: NSJSONWritingPrettyPrinted error:nil];
}
@end
该对象能够“访问”层次结构中的每个对象并对其进行一些工作(在您的情况下将其写入 JSON 流)。请注意,您不需要先提取到字典。您只需直接使用 Core Data 对象,使它们可访问。Core Data 包含它自己的内存管理,有故障,所以你不必担心过多的内存使用。
这就是过程。您实例化访问者对象,然后调用它的开始访问方法,传入上面层次结构的根文件夹对象。在该方法中,访问者对象通过调用要访问的对象来“敲门”第一个要访问- (void)acceptVisitor:(id<Visitor>)visitor
的对象。然后,根文件夹通过调用与它自己的对象类型匹配的访问者对象上的方法来“欢迎访问者”,例如:
- (void)acceptVisitor:(id<Visitor>)visitor
{
if ([visitor respondsToSelector:@selector(visitFolder:)]) {
[visitor visitFolder:self];
}
}
这反过来调用访问者对象上的 visitFolder: 方法,该方法打开流将对象写入 JSON 并关闭流。这是重要的事情。这种模式一开始可能看起来很复杂,但我保证,如果您使用层次结构,一旦实现它,您会发现它功能强大且易于管理。
为了支持深层层次结构的低内存串行输出,我建议您将自己的 JSON 文件夹对象写入输出流。由于 JSON 非常简单,这比它最初看起来要容易得多。另一种方法是寻找一个支持嵌套对象的低内存序列化写入的 JSON 库(我没有使用太多 JSON,所以不知道是否存在并且在 iOS 上易于使用)。访问者模式确保您需要为层次结构的每一级实例化不超过一个 NSManagedObject(当然,当您实现层次结构遍历逻辑时,将不可避免地需要实例化更多对象),因此这对内存使用量很轻。
我已经给出了需要写入输出流的文本字符串的示例。最佳实践是为此使用模板系统,而不是直接编写静态分配的字符串。但就我个人而言,如果你的截止日期很紧,我不会担心采用快速而肮脏的方法。
我假设您的文件夹对象包含一个提供一组附加文件夹的文件夹属性。我还假设您的 Folders NSManagedObject 类包含一个 words 属性,其中包含一组 Words NSManagedObjects。请记住,如果您继续在 Core Data 中工作,它会确保您保持较低的内存占用。
在 visitFolder: 方法结束时,可以使用以下逻辑。
检查文件夹是否包含任何文件夹,如果包含则依次访问每个文件夹。
如果它不再包含文件夹,请检查它是否包含任何单词,如果包含,则依次访问每个文件夹。
请注意,上面的代码是最小化内存占用的最简单结构。您可能希望通过例如仅在超过某个批量大小时执行自动释放来优化它的性能。但是,鉴于您所描述的问题,最好首先实现内存效率最高的方法。
如果你有多态的层次结构——你自己的:)——拿出一本书并做一些研究——管理它们本身就是一个研究生学位。
显然这段代码未经测试!