ios - NSOperation remains in an NSOperationQueue after cancellation -
i perform downloading images using nsoperation , nsoperationqueue. every operation retains storethumbrequest object encapsulating request specific data including target view, waiting image. @ same time view retains nsoperation object cancel operation during reusing or deallocations. retain loop broken @ 'cancel' , 'main' methods in appropriate times.
i found nsoperation remain in nsoperationqueue when set limit on maxconcurrentoperationscount. reaching limit prevented 'main' method of new nsoperations being called. nsoperation remains in queue when it's cancelled. if managed finish task, gracefully removed nsoperationqueue.
my nsoperations non-concurrent , have no dependencies.
any suggestions? thanx in advance
#import "storethumbloadoperation.h" #import "storethumbcache.h" @interface storethumbloadoperation () <nsurlconnectiondelegate> { storethumbrequest *_request; nsmutabledata *_downloadeddata; nsurlconnection *_connection; nsport *_port; } @end @implementation storethumbloadoperation -(id)initwithrequest: (storethumbrequest *)request { nsparameterassert(request); self = [super init]; if (self) { _request = request; } return self; } -(void)main { nsurl *url = ...; nsurlrequest *request = [nsurlrequest requestwithurl: url]; _connection = [[nsurlconnection alloc] initwithrequest: request delegate: self startimmediately: no]; nsrunloop *runloop = [nsrunloop currentrunloop]; _port = [nsmachport port]; [runloop addport: _port formode: nsdefaultrunloopmode]; [_connection scheduleinrunloop: runloop formode: nsdefaultrunloopmode]; [_connection start]; [runloop run]; } -(void)cancel { [super cancel]; [_connection unschedulefromrunloop: [nsrunloop currentrunloop] formode: nsdefaultrunloopmode]; [_connection cancel]; _request.thumbview.operation = nil; //break retain loop [[nsrunloop currentrunloop] removeport: _port formode: nsdefaultrunloopmode]; } #pragma mark - nsurlconnectiondelegate - (void)connection:(nsurlconnection *)connection didreceiveresponse:(nsurlresponse *)response { _downloadeddata = [nsmutabledata new]; } - (void)connection:(nsurlconnection *)connection didreceivedata:(nsdata *)data { [_downloadeddata appenddata: data]; } - (void)connectiondidfinishloading:(nsurlconnection *)connection { if (_connection == connection) { nsfilemanager *filemanager = [nsfilemanager new]; nsstring *pathfordownloadedthumb = [[storethumbcache sharedcache] thumbpathonrequest: _request]; nsstring *pathforcontainigdirectory = [pathfordownloadedthumb stringbydeletinglastpathcomponent]; if (! [filemanager fileexistsatpath: pathforcontainigdirectory]) { //create directory if required [filemanager createdirectoryatpath: pathforcontainigdirectory withintermediatedirectories: yes attributes: nil error: nil]; } if (! self.iscancelled) { [_downloadeddata writetofile: pathfordownloadedthumb atomically: yes]; uiimage *image = [uiimage imagewithcontentsoffile: pathfordownloadedthumb]; //an image may empty if (image) { if (! self.iscancelled) { [[storethumbcache sharedcache] setthumbimage: image forkey: _request.cachekey]; if (_request.targettag == _request.thumbview.tag) { dispatch_async(dispatch_get_main_queue(), ^{ _request.thumbview.image = image; }); } } } _request.thumbview.operation = nil; //break retain loop [[nsrunloop currentrunloop] removeport: _port formode: nsdefaultrunloopmode]; } } } - (void)connection:(nsurlconnection *)connection didfailwitherror:(nserror *)error { if (error.code != nsurlerrorcancelled) { #ifdef debug nslog(@"failed downloading thumb name: %@ error %@", _request.thumbname, error.localizeddescription); #endif _request.thumbview.operation = nil; [[nsrunloop currentrunloop] removeport: _port formode: nsdefaultrunloopmode]; } } @end
i understand, nsoperation not expected removed nsoperationqueue. remains there unlimited time period
from nsoperation cancel method documentation
this method not force operation code stop. instead, updates object’s internal flags reflect change in state.
i believe happening because runloop (which didn't remove when intended end operation) keeps operation alive.
replace instances of following line :
[[nsrunloop currentrunloop] removeport: _port formode: nsdefaultrunloopmode];
with 1 :
[[nsrunloop currentrunloop] removeport: _port formode: nsdefaultrunloopmode]; cfrunloopstop(cfrunloopgetcurrent());
Comments
Post a Comment