Search code examples
objective-cmacoscocoascreensaver

Crash in ScreenSaver since OSX 10.8


Since OSX 10.8, I am getting a crash in my screensaver:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libobjc.A.dylib                 0x00007fff94749790 objc_msgSend_vtable13 + 16
1   com.apple.Foundation            0x00007fff9508941f -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 163
2   com.apple.Foundation            0x00007fff950892f8 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 131
3   albertzeyer.PictureSlider       0x000000010f6e64c7 -[PictureSliderView nextFileName] + 71 (PictureSliderView.m:69)
4   albertzeyer.PictureSlider       0x000000010f6e6675 -[PictureSliderView loadNext] + 53 (PictureSliderView.m:86)
5   albertzeyer.PictureSlider       0x000000010f6e695f -[PictureSliderView initWithFrame:isPreview:] + 447 (PictureSliderView.m:116)
6   com.apple.ScreenSaver           0x00007fff968c7cb5 -[ScreenSaverModules loadModule:frame:isPreview:] + 968

The related code:

- (void) queuedFileNamesPop:(NSString**)fn {
    if([queuedFileNames count] > 0) {
        *fn = [queuedFileNames objectAtIndex:0];
        [queuedFileNames removeObjectAtIndex:0];
    }
}

- (NSString*) nextFileName
{
    NSString* fn = nil;
    [self performSelectorOnMainThread:@selector(queuedFileNamesPop:) withObject:(id)&fn waitUntilDone:YES];
    if(!fn) {
        [nextFileNameLock lock];
        fn = [[NSString alloc] initWithUTF8String:FileQueue_getNextFile()];
        [nextFileNameLock unlock];
    }
    return fn;
}

It seems to crash on performSelectorOnMainThread but I don't really see why. Is there something obvious which I am doing wrong?

Also, it only crashes if it is run from the ScreenSaverEngine. I have also a small dummy app which uses the same view to test the screensaver and it doesn't crashes there.


Edit: I asked about something very related (the same code) a while ago here. I wonder why I haven't taken one of those solutions in the end...


Edit: After the code change suggested by Jonathan, this crash does not occur anymore. However, at other events, I am getting other crashes, with this backtrace:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.CoreFoundation        0x00007fff8f9aefe8 CFRelease + 248
1   com.apple.CoreFoundation        0x00007fff8f9d6770 -[__NSArrayM removeObjectAtIndex:] + 400
2   albertzeyer.PictureSlider       0x0000000108c54425 -[PictureSliderView queuedFileNamesPop:] + 197 (PictureSliderView.m:64)
3   com.apple.Foundation            0x00007fff95089450 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 212
4   com.apple.Foundation            0x00007fff950892f8 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 131
5   albertzeyer.PictureSlider       0x0000000108c544b0 -[PictureSliderView nextFileName] + 128 (PictureSliderView.m:71)
6   albertzeyer.PictureSlider       0x0000000108c54665 -[PictureSliderView loadNext] + 53 (PictureSliderView.m:88)
7   albertzeyer.PictureSlider       0x0000000108c54cce -[PictureSliderView keyDown:] + 382 (PictureSliderView.m:178)
8   com.apple.AppKit                0x00007fff924ad8e0 -[NSWindow sendEvent:] + 9687

Or this:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libobjc.A.dylib                 0x00007fff947497d0 objc_msgSend_vtable14 + 16
1   com.apple.CoreFoundation        0x00007fff8f9aef9a CFRelease + 170
2   com.apple.CoreFoundation        0x00007fff8f9d6770 -[__NSArrayM removeObjectAtIndex:] + 400
3   albertzeyer.PictureSlider       0x00000001023fac51 -[PictureSliderView keyDown:] + 257 (PictureSliderView.m:172)
4   com.apple.AppKit                0x00007fff924ad8e0 -[NSWindow sendEvent:] + 9687

I'm not sure yet wether they are related or independent.

Edit: They were not related. They were because of another additional [fn release] somewhere else.


Solution

  • You're casting a non-object to an object. This is probably conflicting with memory management changes in the Objective-C runtime or in Foundation (looks like they may have started building NSObject with ARC enabled.)

    Instead of (id)&fn, try this:

    NSValue *fnHandle = [NSValue valueWithPointer: &fn];
    [self performSelectorOnMainThread: @selector(queuedFileNamesPop:) withObject: fnHandle waitUntilDone: YES];
    

    This passes the address of fn wrapped in an object. Then, in the called method:

    - (void)queuedFileNamesPop: (NSValue *)fnHandle {
        NSString **fn = [fnHandle pointerValue];
        if (fn) {
            // existing code here
        }
    }
    

    This ensures that correct memory management is always followed.