简体   繁体   中英

Callbacks in ObjC+Cocoa

I'm relatively new to Cocoa/ObjC. Could someone please help me change my code to use asynchronous network calls? Currently it looks like this (fictional example):

// Networker.m
-(AttackResult*)attack:(Charactor*)target {
    // prepare attack information to be sent to server
    ServerData *data = ...;
    id resultData = [self sendToServer:data];
    // extract and return the result of the attack as an AttackResult
}

-(MoveResult*)moveTo:(NSPoint*)point {
    // prepare move information to be sent to server
    ServerData *data = ...;
    id resultData = [self sendToServer:data];
    // extract and return the result of the movement as a MoveResult
}


-(ServerData*)sendToServer:(ServerData*)data {
    // json encoding, etc
    [NSURLConnection sendSynchronousRequest:request ...]; // (A)
    // json decoding
    // extract and return result of the action or request
}

Notice that for each action (attack, move, etc), the Networker class has logic to convert to and from ServerData. It is unacceptable to expect the other classes in my code to deal with this ServerData.

I need to make line A an asynchronous call. It seems that the correct way to do this is to use [NSURLConnection connectionWithRequest:...delegate:...] implementing a callback to do the post-processing. This is the only way I can think to do it:

//Networker.m
-(void)attack:(Charactor*)target delegate:(id)delegate {
    // prepare attack information to be sent to server
    ServerData *data = ...;
    self._currRequestType = @"ATTACK";
    self._currRequestDelegate = delegate;
    [self sendToServer:data];
    // extract and return the result of the attack
}

-(void)moveTo:(NSPoint*)point delegate:(id)delegate {
    // prepare move information to be sent to server
    ServerData *data = ...;
    self._currRequestType = @"MOVE";
    self._currRequestDelegate = delegate;
    [self sendToServer:data];
    // extract and return the result of the movement
}


-(void)sendToServer:(ServerData*)data {
    // json encoding, etc
    [NSURLConnection connectionWithRequest:...delegate:self] // (A)
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    //json decoding, etc
    switch( self._currRequestType ) {
        case @"ATTACK": {...} // extract and return the result of the attack in a callback
        case @"MOVE": {...} // extract and return the result of the move in a callback
    }
}

However, this is very ugly and not thread safe. What is the proper way to do this?

Thanks,

One option is to have one object instance per command/request; as an added bonus you can then use subclasses so that the handling of the type is polymorphic, rather than based on a big switch statement. Make a base command object with an initWithDelegate: method (then have specialized inits in the subclasses corresponding to commands that need arguments) and methods for the basic sending/receiving plumbing. Each subclass can implement a handleResponse: or similar that's called from your baseclass connectionDidFinishLoading:.

If you want to hide that from the clients of this service, your attack:, moveTo:, etc. methods can hide the instantiation of these objects, so the clients would be interacting with the same API.

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