问题
I have a repeatable crash that ends in a EXC_BREAKPOINT like in the image below:
Steps to reproduce the crash:
- Connect two devices
- Begin a transfer using func sendResource(at resourceURL: URL, withName resourceName: String, toPeer peerID: MCPeerID, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil) -> Progress?
- Disconnect the device that initiated the transfer by calling func disconnect()
Edit: Another way to reproduce the crash, by calling Progress.cancel() Steps:
- Connect two devices
- Begin a transfer and store the Progress object let progress: Progress = session.sendResource(...)
- call cancel on the progress object, causing a crash on the other device progress.cancel()
My code at the line 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)
}
Doesn't look like my code is called in the stack trace...
* 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
Update #1: added stack trace as text
Update #2: Found a possible lead on the crash, here's another crash with unconditionallyBridgeFromObjectiveC
I think that the problem is the URL is being passed to didFinishReceivingResourceWithName as nil but the parameter is non-optional. This makes sense, since if the file fails to transfer there will not be a final resting place for the URL. Is there any way I can fix this or intercept the error?
I think this is an Apple bug. Does anyone have a suggestion for doing a work-around?
回答1:
Using the mix and match feature, I was able to convert my MCSessionDelegate to Objective-C and then pass the parameters to Swift. After examining the incoming values I confirmed that the variable "localURL" of type URL is actually nil, meaning that it should have been declared as optional. I submitted a bug report to Apple.
As a workaround, write your MCSessionDelegate in Objective-C. There are a couple of steps to get Objective-C and Swift to work together in the same project, this page explains it very well.
Here is my code.
My MCSessionDelegate header:
#import <Foundation/Foundation.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>
@class NetworkMCSessionTranslator;
@interface NetworkMCSessionDelegate : NSObject <MCSessionDelegate>
-(instancetype)initWithTranslator:(NetworkMCSessionTranslator*) trans;
@end
My MCSessionDelegate implementation file:
#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
And my Swift file that collects the traffic from the delegate:
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){
...
}
}
Good luck!
回答2:
Just letting people know that this bug has been fixed with the release of xcode 9 beta 1. According to documentation and my tests, the at localurl parameter is now optional as expected : https://developer.apple.com/documentation/multipeerconnectivity/mcsessiondelegate/1406984-session
Just update to the last version of xcode9 to resolve this issue.
回答3:
In iOS 7 and xcode 8 and above change your function with localURL as optional
Use this function
func session(_ session: MCSession, didFinishReceivingResourceWithName
resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?,
withError error: Error?) {
if (localURL != nil) {
//enter code here
}
}
Instead of
func session(_ session: MCSession, didFinishReceivingResourceWithName
resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL,
withError error: Error?) {
}
so, this function resolve my crashing issue. If there are showing any warning in xcode 8, ignore it.
来源:https://stackoverflow.com/questions/42477025/swift-multipeerconnectivity-crash-datecomponents-unconditionallybridgefromobject