代码之家  ›  专栏  ›  技术社区  ›  dalind

带有WebView的Mac屏幕保护程序崩溃

  •  1
  • dalind  · 技术社区  · 16 年前

    大家好,

    我有一个用obj-c和可可制作的屏幕保护程序。在OsX 10.6.2下,除了以下各项之外,其他一切都可以正常工作。 在我的屏幕保护程序中,我有一个运行着一些应用程序的WebView。当我试图通过javascript调用objective-c应用程序(屏幕保护程序)时,出现错误,屏幕保护程序和系统首选项面板崩溃。

    系统首选项[86666] ***由于未捕获异常“NSInvalidArgumentException”而终止应用程序

    原因:'-[NSCFArray drain]:无法识别的选择器已发送到实例0x20049b1e0'

    ***第一次抛出时调用堆栈:(

    1 libobjc.A.dylib 0x00007fff81f130f3 objc_异常_抛出+45
    2 CoreFoundation 0x00007fff812931c0+[NSObject(NSObject)不识别选择器:+0
    转发 + 751
    4 CoreFoundation 0x00007fff812091d8\u CF\u转发\u准备\u 0+232 5网络存储0x00007fff847adee0(u zn3JSC8绑定12对象虚拟现实环境+48

    7 JavaScriptCore 0x00007fff80862b66 NK3JSC7JSValue3getEPNS\u 9执行状态KNS\u 10标识符\u 12 PropertySlote+486
    )

    我知道这看起来像是内存泄漏,但正如您在代码中看到的,我几乎没有分配对象。

    只有当我从屏幕保护程序系统prefs用“测试”按钮启动屏幕保护程序时,才会发生这种情况。

    也许有人知道错误可能来自哪里。下面是实现中的一些代码:

    @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 回复  |  直到 16 年前
        1
  •  4
  •   Josh Freeman    16 年前

    不知何故,一个NSFarray(NSMutableArray)被发送了一条“drain”消息,该消息是针对NSAutoreleasePool的。

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

    如果控制台中没有显示任何消息,请尝试将上述代码中的“NSMutableArray”更改为“NSObject”。

        2
  •  1
  •   Kelan    16 年前

    需要注意的一点是,当您通过System Prefs中的“Test”(测试)按钮启动屏幕保护程序时,实际上有两个屏幕保护程序视图实例在不同线程的同一进程地址空间中运行。一个(isPreview==YES)是SysPrefs窗口中的小预览(即使在全屏版本启动时也会继续运行),另一个是全屏版本。它们都在SysPrefs.app进程中运行。因此,您必须仔细检查所有通知/等,以查看它们是否来自您期望的视图实例。

    http://github.com/kelan/WikiWalker ,我最初遇到了一些类似的问题(尽管我没有使用任何javascript)。这不是完美的代码,但可能会有所帮助。我还做了一些技巧,将通知转发到主线程(用于绘图)。请参阅WWScreenSaverView的“线程通知支持”部分。{h,m}。

        3
  •  1
  •   Josh Freeman    16 年前

    尝试一下:

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

    • 执行导致崩溃的步骤。

    希望这有帮助!

        4
  •  0
  •   Ken Aspeslagh    16 年前

    只是为了解决问题,您是否尝试过不发布WebView?

    另外,在首次发布之前,是否可以将WebView的委托设置为零?

    推荐文章