みなさん、こんにちは。
obj-c と cocoa で作成したスクリーンセーバーがあります。以下を除いて、OsX 10.6.2 ではすべて正常に動作します。私のスクリーンセーバー内には、いくつかのアプリケーションが実行されている WebView があります。Objective-C アプリ (スクリーンセーバー) を JavaScript 経由で呼び出そうとすると、エラーが発生し、スクリーンセーバーとシステム設定パネルがクラッシュします。
システム環境設定 [86666] *** キャッチされない例外 'NSInvalidArgumentException' によりアプリを終了します
理由: '-[NSCFArray ドレイン]: 認識されないセレクターがインスタンス 0x20049b1e0 に送信されました'
*** Call stack at first throw:(
0 CoreFoundation 0x00007fff8123a444 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff81f130f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff812931c0 +[NSObject(NSObject) doesNotRecognizeSelector:] + 0
3 CoreFoundation 0x00007fff8120d08f forwarding + 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_9ExecStateERKNSS_12Proty10 あたりの識別子 6
)
これはメモリ リークのように見えますが、コードでわかるように、実際にはほとんどオブジェクトが割り当てられていません。
これは、スクリーンセーバーのシステム設定から「テスト」ボタンを使用してスクリーンセーバーを起動した場合にのみ発生します。ターミナル経由でスクリーンセーバーを起動するか、自動的に起動する場合、同じアクション (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
いくつかの問題/解決策を確認するのに十分なコードであることを願っています. どんな答えでも本当に感謝しています。