任何 Core MIDI 调用都会导致应用程序无响应

Any Core MIDI call causes app to become unresponsive

我在使用的几个 iOS 应用程序以及从 App Store 下载的另一个应用程序中遇到了 Core MIDI 问题。

似乎 正在发生的是核心 MIDI 服务器崩溃。发生这种情况后,与核心 MIDI 函数或属性的任何交互都会使调用应用程序无响应。以下行在 AppDelegate 的 didFinishLaunchingWithOptions::

[MIDINetworkSession defaultSession].enabled = YES;

执行在此时停止,导致调试时永远挂起,否则 #8badf00d 崩溃。

即使核心 MIDI 功能应该 return OSStatus 也会发生这种情况 - return 值永远不会返回:

OSStatus s = MIDIObjectGetStringProperty(ref, kMIDIPropertyDisplayName, (CFStringRef*)&string);

此调用(在 PGMidi 库中)也挂起,s 未被 returned。解决此问题的唯一方法是重新启动设备。

核心 MIDI 服务器变得无响应似乎与来自已连接 Mac 的 运行 多个网络会话有关,但我无法确定可靠复制的方法。除了我自己的应用程序(使用 MIKMIDI 库),我还遇到过从 App Store 下载的 midimittr 无响应行为。

当然,我无法控制核心 MIDI 服务器是否变得无响应。但是有没有办法在进行可能使我的应用程序无响应的调用之前检查 MIDI 服务器的状态?


更新时间:2019-05-10

我发现我可以通过以下步骤使 iOS 核心 MIDI 服务器进入这种无响应状态:

  1. 在 Audio/MIDI 设置中的 Mac 上设置网络 MIDI 会话。
  2. 在 iOS 应用程序中,连接到该网络 MIDI 会话。
  3. 让 Mac 自然进入睡眠状态(从 Apple 菜单调用睡眠似乎不会触发问题)。
  4. 一旦 Mac 进入睡眠状态,在 iOS 应用程序中执行一些操作,这将调用 Core MIDI。

所以就像 iOS 核心 MIDI 服务器失去了与 Mac 的 MIDI 网络的连接,但继续尝试和执行。


更新时间:2019-05-17

Apple 回复了我的错误报告 (#50657978),称它与另一份报告 (#49583498) 重复,将被关闭。至少他们知道这个错误。


更新时间:2019-07-17

通过音频音乐设置连接 Core MIDI 网络,然后在 iOS 设备上打开飞行模式并打开应用程序后台,也可以更快地进入此 Core MIDI 挂起状态(用于调试目的)。

请参阅下面的最新更新和最终解决方案


我已经想出了一个部分解决这个问题的方法。 (唉,因为根本原因似乎嵌入在 Apple 的代码中,所以不可能有完整的解决方案。)

在我的 AppDelegate 中,我现在 运行 一个可以检测对 Core MIDI 的调用是否超时的例程:

func enableMIDINetworkSession() {
    let midiNetworkGroup = DispatchGroup()
    
    midiNetworkGroup.enter()
    DispatchQueue.global(qos: .background).async {
        MIDINetworkSession.default().isEnabled = true
        midiNetworkGroup.leave()
    }
    
    // If `midiNetworkGroup.leave()` is not reached in the closure above, then the result below will be `.timeOut`
    let midiNetworkTimeoutResult = midiNetworkGroup.wait(timeout: DispatchTime.now() + 10.0)
    
    switch midiNetworkTimeoutResult {
    case .timedOut:
        { ... }  // Calls to Core MIDI will cause app to hang. Handle as required. Note: app will crash anyway at some point in the future.
        
    case .success:
        break    // All good, continue as before
    }
}

这里要注意一件事:如果您打算在 midiNetworkTimeoutResult == .timeOut 的情况下显示警报,请记住在此之后立即发生的任何对 Core MIDI 的调用都可能导致不显示警报(因为主队列将被阻塞)。


更新时间:2019-07-17

重要的是要提到上面的解决方案,如果你最终遇到上面的 .timedOut 情况,应用程序将在未来的某个时候崩溃 无论如何,因为它赶上了 MIDINetworkSession.default().isEnabled 调用。

崩溃前的间隔大约为 5 分钟,因此有足够的时间显示警报并建议用户重启设备。


更新时间:2019-08-14

推荐的解决方案

Apple 在 iOS 12.4 中似乎已经解决了这个问题。解决方案是建议您的用户更新到 iOS 12.4 或更高版本。

(此外,这个问题似乎只影响 iOS 12.2 和 12.3 版本。)