3

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
Akshit Zaveri
  • 4,166
  • 6
  • 30
  • 59

2 Answers2

2

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.

Warren Burton
  • 17,451
  • 3
  • 53
  • 73
  • Your idea sounds right but I've added `DownloadOperation` to `NSMutableArray`, but no luck. Any delegate method is not being called. – Akshit Zaveri Aug 26 '14 at 09:28
  • is LiveConnectClient designed to be used off the main thread? – Warren Burton Aug 26 '14 at 09:55
  • Honestly, I don't know. But i think it does stuff in background and not on main thread. How can i force it to run on main thread? – Akshit Zaveri Aug 26 '14 at 09:57
  • Its an async API. It does its work off the main thread as intended but by wrapping it in NSOperation you push the initial call off the main thread which might not work. Honestly, your whole approach here is a bit odd. Why are you placing an operation inside an operation. – Warren Burton Aug 26 '14 at 10:35
  • Here's my requirement. Perhaps you can suggest me a better approach. I want to download all files from a folder which are on OneDrive. And Retarded Microsoft SDK does not support blocks. – Akshit Zaveri Aug 26 '14 at 10:37
  • Its just a regular delegate based async API. Have a look at the edit. – Warren Burton Aug 26 '14 at 21:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60052/discussion-between-akshit-zaveri-and-warren-burton). – Akshit Zaveri Aug 27 '14 at 06:17
0

Don't allocate * DownloadOperation operation * in the method

Make it a property in the .h file like

@property (nonatomic,strong) DownloadOperation *operation;

Sourav Gupta
  • 762
  • 11
  • 13
  • I'm looping it over for undefined number of times. It will throw an exception when i will try to add same `nsoperation` for the second time. – Akshit Zaveri Aug 26 '14 at 08:42