objective c - Can't retain block retrieved from within another block -
i trying write unit test method creates , passes block object (so can called later). method using socket.io-objc send request server. passes callback block socketio's sendevent:withdata:andacknowledge invoked when receives response server). here method want test:
typedef void(^myresponsecallback)(nsdictionary * response);
...
-(void) sendcommand:(nsdictionary*)dict withcallback:(myresponsecallback)callback andtimeoutafter:(nstimeinterval)delay { __block bool didtimeout = false; void (^timeoutblock)() = nil; // create block invoke if request times out if (delay > 0) { timeoutblock = ^ { // set flag if response can suppress didtimeout = true; // invoke original callback no response argument (to indicate timeout) callback(nil); }; } // create callback/ack invoked when response socketiocallback cb = ^(id argsdata) { // if callback invoked after ui timed out, ignore response. otherwise, invoke // original callback if (!didtimeout) { if (timeoutblock != nil) { // cancel timeout timer [nsobject cancelpreviousperformrequestswithtarget:self selector:@selector(onrequesttimeout:) object:timeoutblock]; } // invoke original callback nsdictionary * responsedict = argsdata; callback(responsedict); } }; // send event server [_socketio sendevent:@"message" withdata:dict andacknowledge:cb]; if (timeoutblock != nil) { // if timeout value specified, set timeout [self performselector:@selector(onrequesttimeout:) withobject:timeoutblock afterdelay:delay]; } } -(void) onrequesttimeout:(id)arg { if (nil != arg) { // arg block, execute void (^callback)() = (void (^)())arg; callback(); } }
this appears working fine when running real. problem comes in when run unit test (which uses sentestingkit , ocmock):
-(void)testsendrequestwithnotimeout { nsdictionary * cmd = [[nsdictionary alloc] initwithobjectsandkeys: @"thecommand", @"msg", nil]; __block bool callbackinvoked = false; __block socketiocallback socketiocallback = nil; myresponsecallback requestcallback = ^(nsdictionary * response) { stassertnotnil(response, @"response dictionary invalid"); callbackinvoked = true; }; // expect controller emit message [[[_mocksocket expect] anddo:^(nsinvocation * invocation) { socketiocallback temp; [invocation getargument:&temp atindex:4]; // isn't working i'd expect socketiocallback = [temp copy]; stassertnotnil(socketiocallback, @"no callback passed socket.io sendevent method"); }] sendevent:@"message" withdata:cmd andacknowledge:ocmock_any]; // send command dio [_iocontroller sendcommand:cmd withcallback:requestcallback]; // make sure callback not invoked yet stassertfalse(callbackinvoked, @"response callback invoked before receiving response"); // fake response coming socketiocallback([[nsdictionary alloc] initwithobjectsandkeys:@"msg", @"response", nil]); // make sure original callback invoked result stasserttrue(callbackinvoked, @"original requester did not callback when msg recvd"); }
to simulate response, need capture (and retain) block created method i'm testing , passed sendevent. passing 'anddo' argument expectation, able access block i'm looking for. however, seems not being retained. so, when sendevent unwinds , go invoke callback, values should have been captured in block show null. result test crashes when invoke socketiocallback , goes access 'callback' value captured part of block (and nil).
i using arc , expect "__block socketiocallback socketiocallback" retain values. i've tried "-copy" block variable still not seem retain past end of sendcommand. can force block retain long enough me simulate response?
note: i've tried calling [invocation retainarguments] works crashes somewhere in objc_release when cleaning after test complete.
i able reproduce problem , suspect error in code section:
socketiocallback temp; [invocation getargument:&temp atindex:4];
extracting block way not work correctly. i'm not sure why may have of magic arc in background. if change code following should work you'd expect:
void *pointer; [invocation getargument:&pointer atindex:4]; socketiocallback temp = (__brigde socketiocallback)pointer;
Comments
Post a Comment