简体   繁体   中英

Swift - Closure in Dictionary Crash

So I had objc code which uses Singleton instance to make pings(using SimplePing) and holds ping request completion blocks. In ObjC it looks like this:

@interface SimplePingClient()
{
    NSMutableArray* _currentPings;
    NSMutableDictionary* _currentCallbacks;
}

@end

@implementation SimplePingClient

-(id)init
{
    if( self = [super init] )
    {
        _currentPings = [NSMutableArray new];
        _currentCallbacks = [NSMutableDictionary new];
    }

    return self;
}

method to start 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
    ...
}

And when SimplePing delegate method is called:

- (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];
}

So this code work flawlessly on objc. However when I try to port this in Swift I keep get error EXC_BAD_ACCESS. To simplify example I just erased everything and it turns out that storing Closure in Dictionary and immediately call it is not working for me:

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
        }
    }
}

As you can see in code I even try using hostname(which is String) instead of NSValue assuming some memory problems, but it's not the case - still crashing. This error however is surely something with memory management, however I cannot understand what I'm doing wrong. If anyone could point me what I'm missing I'll really apreciate it.

As @Owen Hartnett suggested I'm moving solution from answer itself here:

Okay, after another hour of debugging it turns out reason is pretty silly. As I said I'm porting my code, so previously I was using this class as bridged objective-c code, therefore this function:

-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result

was bridged following way:

SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String!) -> () in

   if nil != latency
   {
       //dummy code
   }

}

therefore after SimplePingClient became swift class, my new closure signature is(note that now latency is not force unwrapped and is not optional, and also no need to check against nil)

SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String) -> () in

       //dummy code

    }

So by simply changing method call everything started working. Strange thing that compiler doesn't notice this problem though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM