Search code examples
iosobjective-cnsoperationnsoperationqueueonedrive

object of class in NSOperationQueue - with asynchronous method using delegate


I have created a subclass of NSOperation named DownloadQueue. And i'm adding all objects one by one to NSOperationQueue which is appDelegate.queueDownload.

Problem is when object of DownloadOperation is executed and as soon as download method of DownloadOperation executes, my object of DownloadOperation is removed from the queue.

But i have to keep it in memory because when i make an async call [using downloadFromPath] in DownloadOperation.m and server responds with file data, at that time the DownloadOperation object is released and liveOperationSucceeded never called.

May be i think that this question is a bit complex, So before flagging it please read it twice !! It's very important to me. Thanks for understanding.

    // Adding to queue

    DownloadOperation *operation = [[DownloadOperation alloc] init];

    operation.delegate = self;

    [operation operationWithFileOrFolderID:dictDocument[@"id"] withParentPath:self.strParentPath download:NO withFileName:dictDocument[@"name"] withDictionary:dictDocument];
    [operation download];

    [operation setQueuePriority:NSOperationQueuePriorityVeryHigh];

    [appDelegate.queueDownload addOperation:operation];

DownloadOperation.m (which is Subclass of NSOperation)

@implementation DownloadOperation

-(void)main
{
    [self download];
}

- (void)operationWithFileOrFolderID: (NSString *)strID withParentPath: (NSString *)strParentPath download: (BOOL)isDownload withFileName: (NSString *)strFileName withDictionary: (NSMutableDictionary *)dictInfoLocal
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    self.documentsDirectory = paths[0];

    self.strFileName = strFileName;
    self.strID = strID;
    self.strParentPath = strParentPath;
    self.dictInfo = dictInfoLocal;

    if (self)
    {

    }
}

- (void)download
{
    self.liveClient = [[LiveConnectClient alloc] initWithClientId:CLIENT_ID delegate:self];

    [self.liveClient downloadFromPath:[self.strID stringByAppendingString:@"/content"] delegate:self];

    NSLog(@"DownloadFilesVC : Downloading : %@",self.strFileName);    
}

- (void) liveOperationSucceeded:(LiveOperation *)operation
{

}

- (void) liveOperationFailed:(NSError *)error
                   operation:(LiveOperation *)operation
{

}

- (void) liveDownloadOperationProgressed:(LiveOperationProgress *)progress data:(NSData *)receivedData operation:(LiveDownloadOperation *)operation
{

}

@end

Solution

  • You have an async operation inside an operation.

    The operation queue is doing exactly what it is supposed to do and is ejecting the finished DownloadOperation once the method download completes. It doesn't care that the operation has callbacks.

    You need to retain ownership of the DownloadOperation operation by adding it to strongly referenced collection such as NSArray or NSSet and discarding it at a later point when LiveConnectClient completes. You may wish to add a BOOL property to DownloadOperation to say.

    @property BOOL imReallyReallyFinishedAndCanBeDiscardedOK;

    EDIT

    The API is an async / delegate based. One simple way is to iterate over the folder contents and count the return responses. Once you have received the number of responses that matches the number of request you know you have completed.

    As a very simplified version that doesn't care about recursion or errors.

    @interface DownloadManager  : NSObject
    
    @property NSArray *fileRefList;
    @property NSUInteger count;
    @property (strong) void (^completionBlock)();
    
    @end
    
    @implementation DownloadManager
    
    -(void)downloadAllTheFiles {
    
        self.count = 0;
        for(NSString *strID in self.fileRefList) {
    
            LiveConnectClient *liveClient = [[LiveConnectClient alloc] initWithClientId:CLIENT_ID delegate:self];
            [liveClient downloadFromPath:[strID stringByAppendingString:@"/content"] delegate:self];
    
        }
    
    }
    
    - (void) liveOperationSucceeded:(LiveOperation *)operation
    {
        self.count++;
        [self haveIFinished];
    }
    
    - (void) liveOperationFailed:(NSError *)error
                       operation:(LiveOperation *)operation
    {
        self.count++;
        [self haveIFinished];
    }
    
    -(void)haveIFinished {
    
        if (self.count == self.fileRefList.count) {
            if (self.completionBlock) {
                self.completionBlock();
            }
        }
    
    }
    

    Let the API do the queue operations and backgrounding for you. Fire off an arbitrary block when its done.