与WebView崩溃Mac屏幕保护程序

问题描述:

Hy everyone,与WebView崩溃Mac屏幕保护程序

我有一个用obj-c和可可做的屏幕保护程序。除了以下内容,在OsX 10.6.2下一切正常。 在我的屏幕保护程序中,我有一个运行某个应用程序的WebView。当我尝试通过javascript调用我的objective-c应用程序(屏幕保护程序)时,出现错误,屏幕保护程序和系统偏好设置面板崩溃。

系统偏好[86666] ***终止应用程序由于未捕获的异常 'NSInvalidArgumentException'

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

** *在第一次投掷:(
0的CoreFoundation 0x00007fff8123a444 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff81f130f3 objc_exception_throw + 45
2 CoreFound调用堆栈通货膨胀0x00007fff812931c0 + [NSObject的(NSObject的)doesNotRecognizeSelector:] + 0
3的CoreFoundation 0x00007fff8120d08f 转发 + 751
4的CoreFoundation 0x00007fff812091d8 _CF_forwarding_prep_0 + 232 5的WebCore 0x00007fff847adee0 _ZN3JSC8Bindings12ObjcInstance10virtualEndEv + 48
6的WebCore 0x00007fff8470d71d _ZN3JSC16RuntimeObjectImp18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 397
7 JavaScriptCore的0x00007fff80862b66 NK3JSC7JSValue3getEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 486

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

这种情况只发生在我用屏幕保护程序系统偏好设置中的“测试”按钮启动屏幕保护程序时。 当我通过终端启动屏幕保护程序或者它自动启动时,同样的操作(从javascript调用obj-c)可以正常工作。

也许有人有任何想法,错误可能来自哪里。这里是一些代码从执行:

@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 

我希望这是足够的代码,让你看到一些问题/解决方案。 我真的很感谢任何答案。

+0

你打开GC还是不打开? – kennytm 2010-02-09 15:27:00

+0

GC已打开。否则屏幕保护程序将无法使用OSX 10.6。 – dalind 2010-02-09 20:51:24

不知怎的,一个NSCFArray(NSMutableArray)正在发送一个“漏”消息,这意味着一个NSAutoreleasePool。

您可以通过实现NSMutableArray的drain方法获得更多关于数组的信息,因此您可以捕获现在已识别的选择器并打印出数组对象的内容。试着在你的代码中加入这个地方:

@interface NSMutableArray (drain) 

- (void) drain; 

@end 

@implementation NSMutableArray (drain) 

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

@end 

如果你没有看到任何消息在控制台中显示出来,尝试在上面的代码为“NSObject的”改变“的NSMutableArray”。

+0

重写NSObject时,系统偏好面板不再崩溃。流失消息来自一些奇怪的预览图像,大部分时间是不同的预览图像。现在一切都很好。 谢谢! – dalind 2010-02-12 11:58:42

+0

投票,因为这是一个答案,我认为如果一些奇怪的对象杀死你的应用程序,总是需要一个答案,特别是如果应用程序被另一个执行。 – dalind 2010-02-12 12:00:04

只是为了排除故障,您是否尝试不释放WebView?

此外,也许设置WebView的代表之前零它释放它?

+0

刚刚尝试过。系统首选项面板仍然崩溃。 我认为WebView没有代表。 – dalind 2010-02-10 08:52:51

+0

在您发布的示例代码中,您在web视图上设置了两个不同的代表。 – 2010-02-10 14:11:23

+0

你是对的。我删除了这些但仍然发生崩溃。 – dalind 2010-02-10 16:04:26

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

我没有看到任何明显的问题,只需快速浏览一下您发布的代码,但它可能在其他地方。你是否在任何地方使用通知?

我在github上放了一个类似的webview-in-a-screensaver项目,在http://github.com/kelan/WikiWalker,我最初有一些类似的问题(虽然我没有使用任何JavaScript的东西)。这不是完美的代码,但可能有帮助。我也做了一些技巧,将通知转发给主线程(用于绘制)。请参阅WWScreenSaverView的“Threaded Notification Support”部分。{h,m}。

+0

我不在任何地方使用通知。 我检查了我的不同对象(WebView,ScriptObject,self)的实例,但无法识别的选择器发送到的实例是完全不同的。有没有办法找出对象的哪些实例正在运行并获取名称(例如:0x200044820)? scriptobject是为JavaScript正确提供的,但之后,sysPrefs崩溃。 – dalind 2010-02-10 10:28:07

一些尝试:

  • 打开一个终端窗口,输入以下行与NSZombieEnabled运行系统预置:

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

  • 执行导致的步骤到崩溃。

  • 运行控制台应用程序,将右上角的过滤器设置为“系统偏好设置”,然后查找NSZombie消息。

希望这有助于!

+0

我试过2次,但在我的控制台中没有NSZombie消息。该错误消息仍然是“***由于未捕获异常'终止应用程序NSInvalidArgumentException' - 无法识别的选择器发送到实例0x200472000 ***”。该实例编号不是我的对象,我认为,至少不是我的主视图,webview或scriptobject。 – dalind 2010-02-12 08:52:58