【问题标题】:Swift MultipeerConnectivity crash DateComponents unconditionallyBridgeFromObjectiveCSwift MultipeerConnectivity 崩溃 DateComponents unconditionallyBridgeFromObjectiveC
【发布时间】:2017-07-17 13:13:55
【问题描述】:

我有一个以 EXC_BREAKPOINT 结尾的可重复崩溃,如下图所示:

重现崩溃的步骤:

  1. 连接两台设备
  2. 使用 func sendResource(at resourceURL: URL, withName 资源名称:字符串, toPeer peerID: MCPeerID, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil) -> 进度?
  3. 通过调用func disconnect()断开发起传输的设备

编辑:另一种重现崩溃的方法,通过调用 Progress.cancel() 步骤:

  1. 连接两台设备
  2. 开始传输并存储 Progress 对象 let progress: Progress = session.sendResource(...)
  3. 对进度对象调用取消,导致另一台设备崩溃progress.cancel()

我在 didFinishReceivingResourceWithName 行的代码:

func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL, withError error: Error?) {
    // transfer to local URL
    MusicDownloadRequestor.sharedInstance.finishReceivingSongUploadAtLocalURL(tempUrl: localURL)
}

看起来我的代码没有在堆栈跟踪中调用...

* thread #25: tid = 0x806ec, 0x0000000100944af4 libswiftFoundation.dylib`static Foundation.DateComponents._unconditionallyBridgeFromObjectiveC (Swift.Optional<__ObjC.NSDateComponents>) -> Foundation.DateComponents with unmangled suffix "_merged" + 96, queue = 'com.apple.MCSession.callbackQueue', stop reason = EXC_BREAKPOINT (code=1, subcode=0x100944af4)
frame #0: 0x0000000100944af4 libswiftFoundation.dylib`static Foundation.DateComponents._unconditionallyBridgeFromObjectiveC (Swift.Optional<__ObjC.NSDateComponents>) -> Foundation.DateComponents with unmangled suffix "_merged" + 96
frame #1: 0x0000000100114c60 MyAppSwift`@objc NetworkManager.session(MCSession, didFinishReceivingResourceWithName : String, fromPeer : MCPeerID, at : URL, withError : Error?) -> () + 168 at NetworkManager.swift:0
frame #2: 0x00000001a1dda028 MultipeerConnectivity`__79-[MCSession syncCloseIncomingStream:forPeer:state:error:reason:removeObserver:]_block_invoke + 208
frame #3: 0x0000000100c05258 libdispatch.dylib`_dispatch_call_block_and_release + 24
frame #4: 0x0000000100c05218 libdispatch.dylib`_dispatch_client_callout + 16
frame #5: 0x0000000100c12aec libdispatch.dylib`_dispatch_queue_serial_drain + 1136
frame #6: 0x0000000100c08ce0 libdispatch.dylib`_dispatch_queue_invoke + 672
frame #7: 0x0000000100c14e2c libdispatch.dylib`_dispatch_root_queue_drain + 584
frame #8: 0x0000000100c14b78 libdispatch.dylib`_dispatch_worker_thread3 + 140
frame #9: 0x000000018c2a32a0 libsystem_pthread.dylib`_pthread_wqthread + 1288
frame #10: 0x000000018c2a2d8c libsystem_pthread.dylib`start_wqthread + 4

更新 #1:将堆栈跟踪添加为文本

更新 #2:找到可能导致崩溃的原因,here's another crash with unconditionallyBridgeFromObjectiveC

我认为问题在于 URL 被作为 nil 传递给 didFinishReceivingResourceWithName 但参数是非可选的。这是有道理的,因为如果文件传输失败,URL 将不会有最终的归宿。有什么办法可以解决这个问题或拦截错误?

我认为这是一个 Apple 错误。有没有人有解决方法的建议?

【问题讨论】:

  • 请将堆栈跟踪添加为文本。
  • @AminNegm-Awad 已更新,谢谢!

标签: ios swift multipeer-connectivity


【解决方案1】:

在 iOS 7 和 xcode 8 及更高版本中,将您的函数更改为可选的 localURL

使用此功能

func session(_ session: MCSession, didFinishReceivingResourceWithName         
resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, 
withError error: Error?) {

    if (localURL != nil) {
       //enter code here
   }
}

代替

func session(_ session: MCSession, didFinishReceivingResourceWithName         
resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL, 
withError error: Error?) {

}

所以,这个功能解决了我的崩溃问题。 如果在 xcode 8 中显示任何警告,请忽略它。

【讨论】:

  • 请解释一下?
【解决方案2】:

只是让人们知道这个错误已经随着 xcode 9 beta 1 的发布而得到修复。根据文档和我的测试,at localurl 参数现在是可选的:https://developer.apple.com/documentation/multipeerconnectivity/mcsessiondelegate/1406984-session

只需更新到最新版本的 xcode9 即可解决此问题。

【讨论】:

    【解决方案3】:

    使用混合和匹配功能,我能够将我的 MCSessionDelegate 转换为 Objective-C,然后将参数传递给 Swift。在检查了传入的值之后,我确认 URL 类型的变量“localURL”实际上是 nil,这意味着它应该被声明为可选的。我向 Apple 提交了错误报告。

    作为一种解决方法,使用 Objective-C 编写您的 MCSessionDelegate。有几个步骤可以让 Objective-C 和 Swift 在同一个项目中协同工作,this page explains it very well

    这是我的代码。

    我的 MCSessionDelegate 标头:

    #import <Foundation/Foundation.h>
    #import <MultipeerConnectivity/MultipeerConnectivity.h>
    @class NetworkMCSessionTranslator;
    
    @interface NetworkMCSessionDelegate : NSObject <MCSessionDelegate>
    -(instancetype)initWithTranslator:(NetworkMCSessionTranslator*) trans;
    @end
    

    我的 MCSessionDelegate 实现文件:

    #import "NetworkMCSessionDelegate.h"
    #import "MusicAppSwift-Swift.h"
    
    @interface NetworkMCSessionDelegate()
    @property (nonatomic) NetworkMCSessionTranslator *translator;
    @end
    
    @implementation NetworkMCSessionDelegate
    
    /*
     create a NetworkMCSessionDelegate and pass in a swift translator
     */
    -(instancetype)initWithTranslator:(NetworkMCSessionTranslator*) trans{
        if(self = [super init]){
            self.translator = trans;
        }
        return self;
    }
    
    /*
     Indicates that an NSData object has been received from a nearby peer.
     */
    - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{
        [self.translator networkSession:session didReceive:data fromPeer:peerID];
    }
    
    /*
     Indicates that the local peer began receiving a resource from a nearby peer.
     */
    - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progres{
        [self.translator networkSession:session didStartReceivingResourceWithName:resourceName fromPeer:peerID with:progres];
    }
    
    /*
     Indicates that the local peer finished receiving a resource from a nearby peer.
     */
    - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{
        [self.translator networkSession:session didFinishReceivingResourceWithName:resourceName fromPeer:peerID at:localURL withError:error];
    }
    
    /*
     Called when the state of a nearby peer changes.
     */
    - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{
        [self.translator networkSession:session peer:peerID didChange:state];
    }
    
    /*
     Called when a nearby peer opens a byte stream connection to the local peer.
     */
    - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{
        // not expecting to see any of this
    }
    
    @end
    

    还有我从代理收集流量的 Swift 文件:

    import Foundation
    import MultipeerConnectivity
    
    @objc class NetworkMCSessionTranslator: NSObject{
        public func networkSession(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState){
            ...
        }
    
        public func networkSession(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID){
            ...
        }
    
        public func networkSession(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?){
            // !!! Notice localURL is now an OPTIONAL !!!
        }
    
        public func networkSession(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress){
            ...
        }
    
    }
    

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-01
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-14
      • 2016-06-20
      • 2018-03-11
      相关资源
      最近更新 更多