Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
iOS从Javascript调用Objective-C:忽略某些调用?_Javascript_Objective C_Ios_Timing - Fatal编程技术网

iOS从Javascript调用Objective-C:忽略某些调用?

iOS从Javascript调用Objective-C:忽略某些调用?,javascript,objective-c,ios,timing,Javascript,Objective C,Ios,Timing,我已经使用UIWebView委托shouldStartLoadWithRequest()方法实现了在iOS上从javascript到objective-c的唯一通信方式 起初它似乎工作得很好,但现在我注意到,如果我在短时间内多次从javascript调用objective-c,第二次调用通常会被忽略(该应用程序是一个钢琴键盘,每个按键都会触发对本机代码的调用,当处理多次触摸时,不会对每个手指调用本机代码) 这是我的objective-c代码,用于响应javascript调用。我知道这很糟糕,但我

我已经使用UIWebView委托shouldStartLoadWithRequest()方法实现了在iOS上从javascript到objective-c的唯一通信方式

起初它似乎工作得很好,但现在我注意到,如果我在短时间内多次从javascript调用objective-c,第二次调用通常会被忽略(该应用程序是一个钢琴键盘,每个按键都会触发对本机代码的调用,当处理多次触摸时,不会对每个手指调用本机代码)

这是我的objective-c代码,用于响应javascript调用。我知道这很糟糕,但我只是想要一些暂时有效的东西

- (BOOL)webView:(UIWebView *)webView2 shouldStartLoadWithRequest:(NSURLRequest *)request 
        navigationType:(UIWebViewNavigationType)navigationType 
{  
  // Intercept custom location change, URL begins with "js-call:"
  NSString * requestString = [[request URL] absoluteString];
  if ([requestString hasPrefix:@"js-call:"]) 
  {
    // Extract the selector name from the URL
    NSArray * components = [requestString componentsSeparatedByString:@":"];
    NSString * functionCall = [components objectAtIndex:1];
    NSArray * params = [functionCall componentsSeparatedByString:@"%20"];
    NSString * functionName = [params objectAtIndex:0];

    // Parse playnote event
    if ([functionName isEqualToString:@"playNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [m_audioController playNote:[param intValue]];
      NSString * jscall = [NSString stringWithFormat:@"document.PlayNoteCallback(%i);", note_id];
      NSLog(@"playNote: %i", (int)note_id);
      [m_webView stringByEvaluatingJavaScriptFromString:jscall];
    }

    // Parse stopnote event
    if ([functionName isEqualToString:@"stopNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [param intValue];
      NSLog(@"stopNote: %i", (int)note_id); 
      [m_audioController stopNote:note_id];
    }

    // Parse log event
    if ([functionName isEqualToString:@"debugLog"])
    {
      NSString * str = [requestString stringByReplacingOccurrencesOfString:@"%20" withString:@" "];
      NSLog(@"%s", [str cStringUsingEncoding:NSStringEncodingConversionAllowLossy]);
    }

    // Cancel the location change
    return NO;
  }

  // Accept this location change
  return YES;

}
在javascript中,我通过设置单个隐藏iframe的src属性调用objective-c方法。这将触发objective-c中的委托方法,然后调用所需的本机代码

$("#app_handle").attr("src", "js-call:playNote " + key.data("pitch"));
app_handle是相关iframe的id

总而言之,我的方法的基本原理是可行的,但在短时间内多次调用都不起作用。这只是我们被迫从javascript到objective-c进行通信的糟糕方法的产物吗?还是我做错了什么?我知道PhoneGap做了类似的事情来实现相同的目标。我宁愿不使用PhoneGap,因此,如果他们没有这个问题,那么我很想弄清楚他们在做什么,使这项工作

更新:

我刚刚发现:
这证实了我对快速连续呼叫丢失的怀疑。显然,我需要将呼叫集中在一起或手动延迟呼叫,以便在我再次呼叫时url请求已返回。

移动复杂的url可能比在JavaScript端排队更简单、更快使用GCD将逻辑(如对音频处理程序的调用)从主线程中移除。您可以使用
dispatch\u async()
对事件进行排队。如果将事件放入串行队列,则它们肯定会按顺序运行,但您会更快地返回javascript。例如:

  • 在初始化期间为对象创建队列:

    self.queue=dispatch\u queue\u create(“player”,NULL)

  • 在您的回拨中:

    if ([functionName isEqualToString:@"stopNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [param intValue];
      NSLog(@"stopNote: %i", (int)note_id); 
      dispatch_async(self.queue, ^{[m_audioController stopNote:note_id]});
    }
    

    • 接受的答案无法解决问题,因为在处理第一个答案之前到达的位置更改仍然会被忽略。请参阅第一条注释

      我建议采取以下办法:

      function execute(url) 
      {
        var iframe = document.createElement("IFRAME");
        iframe.setAttribute("src", url);
        document.documentElement.appendChild(iframe);
        iframe.parentNode.removeChild(iframe);
        iframe = null;
      }
      
      重复调用
      execute
      函数,由于每个调用都在其自己的iframe中执行,因此在快速调用时不应忽略它们


      问题在于,第一个window.src集必须在发送另一个window.src集之前完成,这最终是因为runloop是单线程和同步的。在第一个window.src集完成之前,除了排队和/或批处理调用之外,没有其他方法可以解决这一问题。实现这一完美实际上相当棘手,尤其是当您在Mobile Safari上发送第一个onClick之前,请考虑延迟,因此您最好将源代码查找到一个开放源代码桥接器,或者更好的是,只使用一个。这会使问题变得更小,但不会使问题消失。处理程序仍然需要非零的时间,并且第二次更改为.src/.location在这段时间内,仍然不会触发。因此,最终您确实需要在JS端排队/批处理。如果您将所有内容发送到后台线程并立即返回,您是否发现实际上这太慢了?(尚未测试此功能;我自己也很好奇。)我个人?没有。但我从来没有开发过这样的应用程序,用户可以在非常近的距离内进行两次点击,并注意到第二次点击是否丢失。但对于他尝试模拟(复调)钢琴键盘的用例,人们可能会有这样的期望。这是有问题的,因为之前我使用的是[m_audioController播放说明]做一些事情。不过这是一个有趣的建议。当它完成时,你会将它发送回javascript。当前的javascript代码阻止了此方法,但请注意你实际上是如何将信息发送回javascript的。你发送信息时没有返回
      值,而是通过评估一个完全不同的JavaScri来发送PT。我是说,在后台线程完成之后,您应该能够评估完全不同的JavaScript,并且可以在 BudDistAtgult的中间进行它(可能更好,因为您不想重新进入JS)。。这就是我最后所做的,我认为这是最好的解决方案。谢谢你的回答!这是一个非常好的方法。我动态添加了表单和iframe,它也很好地工作。