通过控制台从JavaScript调用Obj-C代码:参数丢失了?

9
我在处理这个问题时遇到了极大的困难。
我有一个超级简单的Cocoa应用程序,其中包含一个WebView、在页面上定义的WebScripting API和一个单独在该API上定义的NSObject。当我打开调试工具(在嵌入的WebView中)时,我可以在JavaScript窗口对象上看到API,并且我可以看到我的“api”属性定义在那个对象上——但当我调用API的“get”方法时,参数没有被序列化——当Obj-C方法被调用时,参数缺失。如下所示,希望能说明问题:
图片
我已经仔细阅读了文档,(显然)已经设置了适当的方法来公开需要公开的所有内容,而且我也能看到正在调用该方法。肯定是有些愚蠢的地方我忽略了,但作为这个环境的新手,我没看到。
非常感谢您的帮助!

有趣的是,当我从 WebView 加载的页面中的脚本调用这些方法时,一切看起来都很正常。只有在使用开发人员工具调用 JavaScript 方法时才会丢失参数。同样,方法被调用,断点被命中(在 Xcode 中),但没有参数。困惑了。 - Christian Nunciato
你试过将get:(NSString *)input更改为get:(id)input并且设置一个断点(不是NSLog),只是为了查看是否有任何传递来的东西吗? - JoeCortopassi
1
我们能否看到一些JavaScript代码呢? - Sam
3个回答

2

当您发送 -[NSUserDefaults registerDefaults:] 时,是否已将 WebKitDeveloperExtras 设置为 YES,以在默认用户默认值中启用该选项?


刚刚试了一下以确保——没有成功。唉!感谢您的建议。 - Christian Nunciato

1

根据您使用的Xcode版本,您可能会遇到已知错误。如果您在除最新版本以外的任何版本上使用LLDB,则调试器可能不会提供正确的变量。解决方案是在Apple修复问题之前使用GDB而不是LLDB。但我认为他们在最新版本中解决了这个问题。我建议将调试器更改为使用GDB,并查看您是否在Xcode中获得了正确的变量(Product-> Edit Scheme...-> Run -> Debugger)。我在iOS中遇到了这个问题,但我不知道它是否适用于OSX。无论如何都值得一试。

我最初在这里遇到了这个问题:https://dev59.com/2mDVa4cB1Zd3GeqPiPTD#9485349


真是奇怪,但似乎那也不起作用。不过还是谢谢你的建议。 - Christian Nunciato

0

我从应用程序目录中的本地文件在我的应用程序的主线程中处理javascript。我检查执行的js函数是否包含变量以及开始和结束标记。

希望这能为您解决问题提供一些好的思路。您还可以在js中进行警报,以查看在运行应用程序时值是否正确提交(我相信您已经考虑过了,但值得一提)。愉快的编码!希望这有所帮助!

在.h文件中定义:

NSMutableString *processedCommand;
NSArray *commandArguments;

在 .m 文件中:
// tokens
#define kOpenToken @"<%%"
#define kCloseToken @"%%>"

// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {

[self performSelectorOnMainThread:@selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}

// this will throw 
-(NSString *)executeCommand:(NSString *)command {

NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:@""] 
                       stringByReplacingOccurrencesOfString:kCloseToken withString:@""] 
                      stringByTrimmingLeadingAndTrailingWhitespaces];

if ([aCommand hasPrefix:@"="])
{
    // variable. get value
    [self getVariableFromCommand:aCommand]; 
}
else {
    [self executeThisCommand:aCommand];
}

NSString *returnValue = [NSString stringWithString:processedCommand];

self.processedCommand = nil;
self.commandArguments = nil;

return returnValue;
}

-(void)executeThisCommand:(NSString *)aCommand {

BOOL hasError = NO;

// clear result
self.processedCommand = nil;
self.commandArguments = nil;

BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;

@try {
    // first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
    commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:@":"]];
    if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:@"js-call"]) {
        isFromJS = YES;
        [commandParts removeObjectAtIndex:0];
    }

    // get our function, arguments
    function = [[commandParts objectAtIndex:0] retain];
    [commandParts removeObjectAtIndex:0];

    if ([commandParts count] > 0){
        if (isFromJS == YES) {
            NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            if ([arguments length] > 0) {
                self.commandArguments = [arguments JSONValue];
            }
        }
        else {
            self.commandArguments = [NSArray arrayWithArray:commandParts];
        }
    }

    // build invoke
    SEL sel = NSSelectorFromString(function);

    if ([self respondsToSelector:sel]) {

        [self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];

        // using invocation causes a SIGABORT because the try/catch block was not catching the exception.
        // using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)

    }
    else {
        [appDelegate buildNewExceptionWithName:@"" andMessage:[NSString stringWithFormat:@"Object does not respond to selector %@", function]];
    }

}
@catch (NSException * e) {
    hasError = YES;
    [self updateErrorMessage:[NSString stringWithFormat:@"Error processing command %@: %@", aCommand,  [e reason]]];
}
@finally {
    [function release];
    [commandParts release];
}

if (hasError == YES) {
    [appDelegate buildNewExceptionWithName:@"executeThisCommand" andMessage:self.errorMessage];
}
}

// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {

NSString *returnValue = nil;
if (queryString != nil) {
    returnValue = [queryString objectForKey:[name lowercaseString]];
}

return returnValue;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接