【问题标题】:Segmentation fault - performSelectorOnMainThread on [NSFileManager defaultManager]分段错误 - [NSFileManager defaultManager] 上的 performSelectorOnMainThread
【发布时间】:2014-07-04 22:42:07
【问题描述】:

这段代码似乎有时会导致分段错误:

[[NSFileManager defaultManager] performSelectorOnMainThread:@selector(removeItemAtPath:error:) withObject:filePath waitUntilDone:YES];

我想在主线程中对文件执行所有操作,以避免在迭代整个文件夹时删除文件等冲突。

它会产生这个错误:

Exception Type:  SIGSEGV
Exception Codes: SEGV_ACCERR at 0x1084

Application Specific Information:
objc_msgSend() selector name: release

这是崩溃的主线程的堆栈跟踪:

Thread 0 Crashed:
0   libobjc.A.dylib                      0x39fc8636 objc_msgSend + 22
1   Foundation                           0x30214c67 __NSThreadPerformPerform + 452
2   CoreFoundation                       0x2f7f6fef __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 12
3   CoreFoundation                       0x2f7f64b7 __CFRunLoopDoSources0 + 204
4   CoreFoundation                       0x2f7f4ca7 __CFRunLoopRun + 628
5   CoreFoundation                       0x2f75f769 CFRunLoopRunSpecific + 522
6   CoreFoundation                       0x2f75f54b CFRunLoopRunInMode + 104
7   GraphicsServices                     0x346bc6d3 GSEventRunModal + 136
8   UIKit                                0x320be891 UIApplicationMain + 1134
9   Pocket3                              0x00050243 main (main.m:4)
10  libdyld.dylib                        0x3a4bcab7 start + 0

我做错了什么?

看起来 NSFileManager 被释放得太早了,但是如果它是单例呢? 会不会和据说不是线程安全的方法 [NSFileManager defaultManager] 有关系?

【问题讨论】:

    标签: objective-c thread-safety segmentation-fault nszombie


    【解决方案1】:

    更新:新答案

    NSFileManager 是线程安全的(只要你不使用它的委托,你不使用它,而且无论如何你都不应该使用 -defaultManager )。您可以在您当前所在的任何线程上调用[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]。仅在主线程上执行此操作没有任何优势。事实上,如果您在后台线程中更新文件系统,性能可能会更好,因为如果操作花费的时间比预期的长,UI 不会阻塞。

    旧答案(为什么会发生崩溃)...

    -removeItemAtPath:error: 方法需要两个对象,但您只提供一个。所以-removeItemAtPath:error: 方法将看到的第二个参数(NSError **)只是内存中filePath 指针旁边的一些垃圾。

    -performSelectorOnMainThread:... 没有接受两个对象的版本。您可以改用dispatch_sync

    dispatch_sync(dispatch_get_main_queue(), ^() {
        NSError *error = nil;
        BOOL ok = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
        if(!ok) {
             NSLog(@"an error happened: %@", error);
        }
    }
    

    【讨论】:

    • 这个解释很有道理。不过,我需要一点时间来确认它解决了问题,因为崩溃很少发生。谢谢!
    • @rollkat 当文件管理器方法产生错误时会发生崩溃。当它返回YES时,该项目刚刚被移除,没有错误,也没有触及错误指针。您可以将filePath 替换为[@"P-" stringByAppendingString:filePath],这将使该方法始终失败。 -performSelector... 的原始方法总是会崩溃。 dispatch_sync 的新方法永远不会崩溃,只会失败。
    • @rollkat 此外,NSFileManager 是线程安全的,只要您不使用它的委托方法。所以你可以从你当前所在的任何线程调用-removeItemAtPath:error: 方法,它就可以工作了!
    • 更正:关于我说“总是会崩溃”:并非总是如此,但大多数时候。在某些情况下,它只会损坏内存,这可能会导致稍后崩溃。
    • @rollkat “我检查文件是否仍然存在于循环中” - 这可能是这里最好的选择,恕我直言......但取决于你如何实现它,可能还有另一种可能的竞争条件:如果你说“if(file exists){data = read_from(file); handle(data)}” 存在竞争(文件可能在“file exists”和“read_from(file)”之间被删除)。如果您执行“data = read_from(file); if(data) { handle(data); }”,则保存。顺便说一句,你可以使用-[NSData dataWithContentsFromFile:]
    猜你喜欢
    • 2012-08-09
    • 2016-03-21
    • 2014-01-08
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 2016-03-31
    • 2014-08-25
    • 1970-01-01
    相关资源
    最近更新 更多