Mac Screensaver с WebView вылетает - PullRequest
       15

Mac Screensaver с WebView вылетает

1 голос
/ 09 февраля 2010

Привет всем,

У меня есть заставка с obj-c и какао. Под OsX 10.6.2 все работает нормально, кроме следующего. В моей заставке у меня есть WebView с запущенным приложением. Когда я пытаюсь вызвать мое приложение target-c (заставку) через javascript, я получаю сообщение об ошибке, и происходит сбой заставки и панели системных настроек.

Системные настройки [86666] *** Завершение работы приложения из-за необработанного исключения «NSInvalidArgumentException»

причина: '- [сток NSCFArray]: нераспознанный селектор отправлен на экземпляр 0x20049b1e0'

*** Вызов стека при первом броске: (
0 CoreFoundation 0x00007fff8123a444 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff81f130f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff812931c0 + [NSObject (NSObject) doesNotRecognizeSelector:] + 0
3 CoreFoundation 0x00007fff8120d08f пересылка + 751
4 CoreFoundation 0x00007fff812091d8 _CF_forwarding_prep_0 + 232 5 WebCore 0x00007fff847adee0 _ZN3JSC8 Привязки12ObjcInstance10virtualEndEv + 48
6 WebCore 0x00007fff8470d71d _ZN3JSC16RuntimeObjectImp18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 397
7 JavaScriptCore 0x00007fff80862b66 NK3JSC7JSValue3getEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 486
)

Я знаю, что это похоже на некоторую утечку памяти, но, как вы увидите в коде, у меня действительно почти нет выделенных объектов.

Это происходит только тогда, когда я запускаю скринсейвер с помощью кнопки «Тест» из системных настроек заставки. Когда я запускаю заставку через терминал или запускается автоматически, то же действие (вызов obj-c из javascript) работает нормально.

Может быть, у кого-то есть идея, откуда может возникнуть ошибка. Вот некоторый код из реализации:

@implementation ScreensaverView

- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {

    self = [super initWithFrame:frame isPreview:isPreview];

    if (self) {

        [self setAnimationTimeInterval:-1];
        [self setAutoresizesSubviews:YES];

        // ::::::::::::::::::::::: Init stuff ::::::::::::::::::    

        // init 
        quitFlag = false;
        previewMode = isPreview;

        // find out the path the screensaver bundle
        pMainBundle = [NSBundle bundleForClass:[self class]];
        pBundlePath = [pMainBundle bundlePath];

        // read Info.plist
        infoDict = [pMainBundle infoDictionary];
    }

    return self;
}

- (void)startAnimation
{   
    [super startAnimation];

    // combine: bundle path + filename for screensaver file 
    NSString *pathToScreensaver = [NSString stringWithString:pBundlePath];
    NSString *valueScreensaverFile;

    if(!previewMode)
    {
        valueScreensaverFile = [infoDict objectForKey:@"ScreensaverFile"];
    }
    else 
    {
        valueScreensaverFile = [infoDict objectForKey:@"PreviewFile"];
    }

    // add filename to bundle path
    pathToScreensaver = [pathToScreensaver stringByAppendingString:valueScreensaverFile];

    // complete NSURL to the screensaver file
    NSURL *screensaverUrl = [NSURL fileURLWithPath: pathToScreensaver];

    webView = [WebView alloc];
    [webView initWithFrame:[self frame]];
    [webView setDrawsBackground:NO];

    // delegation policy for interactive mode
    [webView setPolicyDelegate: self];
    [webView setUIDelegate:self];

    // load screensaver
    [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:screensaverUrl]];

    scriptObject = [webView windowScriptObject];
    [scriptObject setValue:self forKey:@"screensaver"];

    [self addSubview:webView];
}

- (void)stopAnimation
{   
    [[webView mainFrame] stopLoading];
    [webView removeFromSuperview];
    [webView release];
    [super stopAnimation];
}

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector 
{       
    if (selector == @selector(quitScreenSaver)) {
        return NO;
    }

    if(selector == @selector(gotoUrl:) ){
        return NO;
    }

    return YES;
}

+(NSString *)webScriptNameForSelector:(SEL)selector
{   
    if(selector == @selector(quitScreenSaver))
    {
        return @"quitNoOpen";
    }

    if(selector == @selector(gotoUrl:))
    {
        return @"openAndQuit";
    }

    return nil;
}

- (void) quitScreenSaver
{
    quitFlag = true;
    [super stopAnimation];
}

- (void) gotoUrl:(NSString *) destinationURL 
{   
    if(destinationURL == NULL)
    {
        return;
    }

    NSString * path    = destinationURL;
    NSURL    * fileURL = [NSURL URLWithString:path];
    [[ NSWorkspace sharedWorkspace ] openURL:fileURL];
    [self quitScreenSaver];
}

@end

Надеюсь, вам хватит кода, чтобы увидеть некоторые проблемы / решения. Буду очень признателен за любые ответы.

Ответы [ 4 ]

4 голосов
/ 12 февраля 2010

Каким-то образом NSCFArray (NSMutableArray) отправляется «сливное» сообщение, предназначенное для NSAutoreleasePool.

Возможно, вы сможете получить немного больше информации о том, что представляет собой массив, реализовав метод стока для NSMutableArray, так что вы можете перехватить теперь признанный селектор и распечатать содержимое объекта массива. Попробуйте добавить это где-нибудь в своем коде:

@interface NSMutableArray (drain)

- (void) drain;

@end

@implementation NSMutableArray (drain)

- (void) drain
{
   NSLog(@"drain message received by object: %@", self);
}

@end

Если вы не видите никаких сообщений в консоли, попробуйте изменить "NSMutableArray" в приведенном выше коде на "NSObject".

1 голос
/ 10 февраля 2010

Следует помнить одну вещь: когда вы запускаете экранную заставку с помощью кнопки «Тест» в «Системных настройках», у вас фактически есть 2 экземпляра представления экранной заставки, работающих в адресном пространстве одного процесса в разных потоках. Один (с isPreview == YES) - это небольшой предварительный просмотр в окне SysPrefs (который продолжает работать даже при запуске полноэкранной версии), а другой - полноэкранная версия. Они оба работают в процессе SysPrefs.app. Таким образом, вы должны быть осторожны, чтобы проверить все уведомления / и т.д. чтобы увидеть, исходят ли они из ожидаемого вами экземпляра представления.

Я не вижу каких-либо очевидных проблем при быстром взгляде на код, который вы разместили, но это может быть где-то еще. Используете ли вы где-нибудь уведомления?

Я поместил похожий проект webview-in-a-screensaver на github на http://github.com/kelan/WikiWalker,, где у меня изначально были некоторые подобные проблемы (хотя я не использовал какие-либо JavaScript-материалы). Это не идеальный код, но может помочь. Я также сделал несколько трюков для пересылки уведомлений в основной поток (для рисования) в. См. Раздел «Поддержка потоковых уведомлений» в WWScreenSaverView. {H, m}.

1 голос
/ 12 февраля 2010

Что-то попробовать:

  • Откройте окно терминала и введите следующую строку, чтобы запустить Системные настройки с помощью NSZombieEnabled :

env NSZombieEnabled=YES "/Applications/System Preferences.app/Contents/MacOS/System Preferences"

  • Выполните шаги, которые приведут к аварии.

  • Запустите консольное приложение, установите фильтр в верхнем правом углу на «Системные настройки» и найдите сообщения NSZombie.

Надеюсь, это поможет!

0 голосов
/ 10 февраля 2010

Просто для устранения неполадок, вы пытались не выпускать WebView?

Кроме того, возможно, установите делегатов WebView равным nil перед первым выпуском?

...