所以我有objc代码,该代码使用Singleton实例进行ping(使用SimplePing)并保存ping请求完成块。在ObjC中,它看起来像这样:
@interface SimplePingClient()
{
NSMutableArray* _currentPings;
NSMutableDictionary* _currentCallbacks;
}
@end
@implementation SimplePingClient
-(id)init
{
if( self = [super init] )
{
_currentPings = [NSMutableArray new];
_currentCallbacks = [NSMutableDictionary new];
}
return self;
}
开始ping的方法:
-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
{
TSimplePing* pingClient = [TSimplePing simplePingWithHostName:hostName];
[_currentPings addObject:pingClient];
[_currentCallbacks setObject:result forKey:[NSValue valueWithNonretainedObject:pingClient]];
pingClient.delegate = self;
//some other irrelevant code
...
}
当调用SimplePing委托(delegate)方法时:
- (void)simplePing:(TSimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet
{
void(^callback)(NSString* latency) = [_currentCallbacks objectForKey:[NSValue valueWithNonretainedObject:pinger]];
if( callback )
{
//some irrelevant code
...
callback(@"123");//hard coded for test, irrelevant code get this value correctly ;)
[_currentCallbacks removeObjectForKey:[NSValue valueWithNonretainedObject:pinger]];
}
[_currentPings removeObject:pinger];
}
因此,此代码可在objc上完美运行。但是,当我尝试在Swift中移植它时,我总是收到错误EXC_BAD_ACCESS。为了简化示例,我只是擦除了所有内容,结果发现在字典中存储Closure并立即调用它对我不起作用:
typealias callbackClosure = (String) -> ()
class SimplePingClient: NSObject
{
// MARK: variables
var _currentPings = [TSimplePing]()
var _currentCallbacks: Dictionary<String, callbackClosure> = [String: callbackClosure]()
private func ping(hostname: String, resultCallback:callbackClosure)
{
var pingClient = TSimplePing(hostName: hostname)
pingClient.delegate = self
_currentPings.append(pingClient)
_currentCallbacks[pingClient.hostName] = resultCallback
if let callback = _currentCallbacks[pingClient.hostName]
{
callback("123213")//here program CRASHES
}
}
}
如您在代码中所看到的,假设某些内存问题,我什至尝试使用主机名(即String)而不是NSValue,但事实并非如此-仍然崩溃。但是,此错误肯定与内存管理有关,但是我无法理解我在做什么。如果有人能指出我所缺少的内容,我将不胜感激。
最佳答案
正如@Owen Hartnett所建议的那样,我将解决方案从答案本身移到这里:
好的,经过一个小时的调试,事实证明原因很愚蠢。正如我说的那样,我正在移植代码,因此以前我将此类用作桥接的objective-c代码,因此该功能:
-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
通过以下方式桥接:
SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String!) -> () in
if nil != latency
{
//dummy code
}
}
因此,在SimplePingClient成为swift类之后,我新的闭包签名是(请注意,现在延迟不是强制展开的,并且不是可选的,并且也不需要检查nil)
SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String) -> () in
//dummy code
}
因此,只需更改方法调用即可开始一切工作。编译器没有注意到此问题的奇怪之处。