问题描述:

Here is a tricky question, to which the answer might prove useful for many networking apprentice out there, including me.

Some background information on the context:

  • Let's say you want to download data from an Online Service
  • you want to do this asynchronously
  • you want to do one download at a time, then maybe do another one upon completion of the previous one.

One neat way of doing this is using recursion.

The problem with common implementations you can come up with is Retain cycles, between networking completion blocks and self. This can be solved using weakSelf reference pointers.

But, what about retain cycles for recursive calls ?

We have implemented a recursive stack, self pointing to a download management class, like this:

-(void)startNetworkDownloadForObjectAtIndex:(int) anIndex

{

__typeof__(self) __weak weakSelf = self;

NSURL *urlForObjectAtIndex = [SomeClass URLforIndex:anIndex];

[self.downloadManager getResourceAtURL:urlForObjectAtIndex success:^(AFHTTPRequestOperation *operation, id responseObject) {

if (indexOfObjectToDownload < weakself.totalNumberOfObjectsToDownload) [weakSelf startNetworkDownloadForObjectAtIndex:indexOfObjectToDownload+1];

else [weakSelf startDOwnloadTimer];

}

failure:^(AFHTTPRequestOperation *operation, NSError *error) {

// response code is in operation.response.statusCode

[weakSelf handleNetworkError:error];

}];

}

-(void)handleNetworkError:error

{

// Do some error handling

[self startNetworkDownloadForObjectAtIndex:self.lastUnsentObjectIndex];

}

-(void)startDownloadTimer

{

if (self.syncEngineTimer) [self.syncEngineTimer invalidate];

self.syncEngineTimer = [NSTimer scheduledTimerWithTimeInterval:kSyncTimeInterval

target:self

selector:@selector(restartNetworkDownload)

userInfo:nil

repeats:NO];

}

-(void)restartNetworkDownload

{

// do some fancy calculations / etc to manage your download

int anIndex = theResultOfYourCalculation;

[self startNetworkDownloadForObjectAtIndex:anIndex];

}

OK, this is an example of a possible recursive call of several network downloads (get 100 flicker pictures for instance) and try to get the new ones after 1 hour, for instance.

Excuse any coding typos.

We're running this under ARC for iOS devices above iOS 5.0

We obviously broke the first level of retain cycle by using a weakSelf reference pointer when using self.downloadManager keeping a reference to the success and failure completion block. This is all fine and goes well in instruments.

Now, when looking at allocation in Instruments, we launch a download operation for several downloads.

Instruments shows no leaks.

BUT when regurlarly saving the heap, you can see it slowly growing.

Checking allocations and having a look at the call stack, it definitely looks like the block is keeping a reference to self through the use of startDownloadTimer

Any explanation on a possible cause and solution will be greatly appreciated :)

网友答案:

Your timer retains its target (the self).

Try the solution from this question: Weak Reference to NSTimer Target To Prevent Retain Cycle

Or use dispatch_after instead of timer.

相关阅读:
Top