objective-c – 以用户的XML保存文件结尾的奇怪数据

前端之家收集整理的这篇文章主要介绍了objective-c – 以用户的XML保存文件结尾的奇怪数据前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的应用程序使用 XML用户数据保存到文件中.我刚刚收到了来自用户的2份报告,他们在文件中看到了完全意外的数据.而不是XML,它看起来像这样:

({"windows":[{"tabs":[{"entries":[{"url":"https://mail.google.com/a/cast...

还有一些来自文件中间的内容,重量接近30KB:

{\"n\":\"bc\",\"a\":[null]},{\"n\":\"p\",\"a\":[\"ghead\",\"\",0]},{\"n\":\"ph\",\"a\":[{\"gb_1\":\"http://www.google.com/

任何人都可以告诉我这是什么类型的数据,或者它是如何在我的用户的数据文件中结束的?两位用户都报告说必须按住电源按钮才能关闭他们的机器.一个案例中的关闭是Firefox冻结,另一个案例是鼠标问题.其中一个用户实际上遇到了内核恐慌.

我还不相信这是一个内存管理问题,因为我的用户群超过10万人,而且我只收到了2份报告.我认为它更窄/更罕见.

这是我用来将数据写入文件代码片段:

NSString *xmlString = [[self convertContextToXmlString:context] retain];

NSError *e = nil;

[[xmlString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:location options:NSDataWritingAtomic error:&e];
[xmlString release];
if (e) {
    NSLog(@"An error occurred saving: %@",[e description]);
}
return e;

永远不会在后台线程上发生数据保存,总是在UI线程上.此外,我正在使用NSDataWritingAtomic选项将数据写入文件.

编辑:第二个用户文件具有几乎相同的数据.所以两个错误内容来自同一个地方,但在哪里?一旦我能够,我就会在这个问题上加上200分的赏金.

AV/////wEAAAAAAAAAAAABAAA="}]}]},{"url":"http://googleads.g.doubleclick.net/pagead/ads?client=ca-pu

编辑2:在按住电源按钮关闭机器后,收到来自也遇到数据损坏的用户的第三个报告.他的数据在开始时有很多随机垃圾,然后在最后有适当的数据:

(garbage)rred="1"><rest of it was normal xml...>

解决方法

从Apple开发者那里得到了一个很好的答案.将在未来几周内将我现有的模型移植到Core Data. (StackOverflow混淆了一些列表/格式,但它在很大程度上仍然非常易读.)

我将开始回答一个关于HFS Plus日记的问题.自从在Mac OS X 10.2.x中引入日志记录以来,Mac OS X的文件系统正确性保证一直是 – 无论内核恐慌,电源故障等等 – 文件系统操作将导致以下两种结果之一:

o操作将由期刊前滚,在这种情况下,操作就好像操作已成功完成

o或者操作将被回滚,就好像操作从未发生过一样

此保证有两个关键限制:

o它适用于单个文件系统操作(创建,删除,移动等),而不适用于操作组.

o它仅适用于逻辑文件系统结构,不适用于文件中的数据.

简而言之,该期刊的目的是防止整个文件系统损坏,而不是特定文件的损坏.

记住这一点,让我们来看看 – [NSData writeToFile:options:error:]的行为.它的行为可能非常复杂,但在典型情况下非常简单.探索此问题的一种方法是编写一些代码并使用它来查看其文件系统行为.例如,这里有一些测试代码

- (IBAction)testAction:(id)sender
{

BOOL success;
NSData * d;
struct stat sb;

d = [@"Hello Cruel World!" dataUsingEncoding:NSUTF8StringEncoding];
assert(d != nil);

(void) stat("/foo",&sb);
success = [d writeToFile:@"/tmp/WriteTest.txt" options:NSDataWritingAtomic error:NULL];
(void) stat("/foo",&sb);
assert(success);
}

这两个电话只是标记;他们可以很容易地看到-writeToFile生成哪些文件系统操作:options:error:.

您可以使用以下命令查看文件系统行为:

$sudo fs_usage -f filesys -w WriteTest

其中“WriteTest”是我的测试程序的名称.

以下是生成的fs_usage输出的摘录:

14:33:10.317 stat [2] / foo
14:33:10.317 lstat64 private / tmp / WriteTest.txt
14:33:10.317打开F = 5(RWC__E)private / tmp / .dat2f56.000
14:33:10.317写F = 5 B = 0x12
14:33:10.317 fsync F = 5
14:33:10.317关闭F = 5
14:33:10.318重命名private / tmp / .dat2f56.000
14:33:10.318 chmod private / tmp / WriteTest.txt
14:33:10.318 stat [2] / foo

你可以清楚地看到围绕-writeToFile的“stat”调用:options:error:call,这意味着这些调用之间的所有东西都是由-writeToFile生成的:options:error:.

它有什么作用?嗯,它实际上非常简单:

>它创建,写入,fsyncs并关闭包含数据的临时文件.
>它将临时文件重命名为您要写入的文件.
>它重置最终文件的权限.

总而言之,这是一个非常标准的UNIX风格安全保存.但问题是,这对数据完整性有何影响?需要注意的关键是fsync不保证在返回之前将所有数据都推送到磁盘.这个问题有一个漫长而复杂的历史,但总结是fsync被称为方式太多次,对于太多性能敏感的位置,它可以做出保证.这意味着您可能会遇到所有文件损坏问题,如下所述:

o“iProcrastinate_Bad_2.ipr”和“iProcrastinate_Bad_3.ipr”只包含错误的数据.这可能发生如下:

> App创建临时文件.
> App写入临时文件.响应这个内核:

一个.在磁盘上分配一组块
湾将它们添加文件
C.扩展文件的长度
d.复制写入缓冲区缓存的数据

> App fsyncs并关闭文件.内核通过调度要尽快写入的数据块来响应.
> App将临时文件重命名为真实文件.
>系统内核恐慌.
>当系统重新启动时,将从日志中恢复步骤1,2a..2c,3和4中的更改,这意味着您具有包含无效数据的有效文件.

o“iProcrastinate_Bad_1.ipr”只是上述内容的略微变化.如果你用十六进制编辑器打开文件,你会发现它看起来很好,除了偏移量为0x6000..0x61ff的数据范围,它似乎包含与你的应用程序完全无关的数据.值得注意的是,这个数据的长度为0x200字节,恰好是一个磁盘块.因此,似乎内核设法将所有用户数据写入磁盘,除了这一块.

那么这会让你离开?它不太可能 – [NSData writeToFile:options:error:]将比现有的更强大;正如我之前提到的,这样的变化往往会对整体系统性能产生负面影响.这意味着您的应用必须解决此问题.

在这方面,有三种常用方法可以强化您的应用:

A. F_FULLFSYNC – 您可以通过使用F_FULLFSYNC选择器调用文件提交到永久存储器.你可以在你的应用程序中使用这个来代替 – [NSData writeToFile:options:error:]用您自己的代码调用F_FULLFSYNC而不是fsync.

这种方法最明显的缺点是F_FULLFSYNC非常慢.

B. journalling – 采用更强大的文件格式的另一种选择,也许支持日志记录.一个很好的例子是sqlite,您可以直接使用或通过Core Data使用.

C.更安全的保存 – 最后,您可以通过备份文件实现更安全的保存机制.在调用 – [NSData writeToFile:options:error:]来编写文件之前,您可以将以前的文件重命名为其他名称,并保留该文件以防万一.如果在打开主文件时发现它已损坏,则会自动恢复为备份.

在这些方法中,我更倾向于B,特别是使用Core Data接近B,因为Core Data提供了超出数据完整性的许多好处.但是,对于快速修复选项,C可能是您最好的选择.

如果您对此事有任何疑问,请与我们联系.

猜你在找的cocoa相关文章