Search code examples
objective-cthread-safetysegmentation-faultnszombie

Segmentation fault - performSelectorOnMainThread on [NSFileManager defaultManager]


This code seems to be causing a segmentation fault from time to time:

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

I want to perform all operations on files in the main thread to avoid conflicts like removing a file while the whole folder is iterated.

It produces this error:

Exception Type:  SIGSEGV
Exception Codes: SEGV_ACCERR at 0x1084

Application Specific Information:
objc_msgSend() selector name: release

This is the stack trace of the crashed main thread:

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

What am I doing wrong?

It looks like the NSFileManager is deallocated too early, but how can it be if it is a singleton? Could it have something to do with the method [NSFileManager defaultManager] which is said to not be thread safe?


Solution

  • Update: new answer

    NSFileManager is threadsafe (as long as you are not using its delegate, which you don't, and which you shouldnt do with the -defaultManager anyways). You can just call [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error] on whatever thread you are currently on. There is no advantage on doing it on the main thread only. In fact, performance will probably better if you update the filesystem in a background thread, because the UI will not block if the operation takes longer than expected.

    Old answer (why the crash did happen)...

    The method -removeItemAtPath:error: wants two objects, but you are providing only one. So the second paramter (NSError **) that the -removeItemAtPath:error: method will see, is just some garbage that lies next to the filePath pointer in memory.

    There is no version of -performSelectorOnMainThread:... that takes two objects. You may use dispatch_sync instead:

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