> On 2 Jan 2015, at 1:46 pm, Roland King <[email protected]> wrote:
>
> Having a handler block which refers to self is not in and of itself a
> problem, very many blocks implicitly do. The block retains self, however in
> most cases something else retains the block and the self reference goes away
> when the block is released. The problem usually comes when the handler block
> which refers to self is also a property of the object, eg myObj.completion =
> ^{ .. block referring to myobj }. Xcode normally warns you if you even get
> close to doing that however.
Right. Well, my block refers to self, but it's not a property of self. Quickly
trying it with a weak version of self shows no change of behaviour regarding
memory usage, so I guess that was a red herring.
Just to be sure, I have:
<my
object>...[owns]...NSURLSession...[owns]...NSURLSessionDataTask...[owns]...completion
block...[refers to]...<my object>
I don't think this amounts to a retain cycle, because when the session has
finished the task, it will release it, and its block, and those references to
self (my object) that it has.
>
> If that's happening then allocations should show you are amassing whatever
> objects those are and never releasing them, does it? Or you can be old skool
> about it and NSLog() dealloc() to see if it's getting called.
What appears to be amassing are 132KB malloc'd blocks (by the hundreds). These
are created by HTTPNetStreamInfo::_readStreamClientCallBack(__CFReadStream*,
unsigned long), down in CFNetwork. The stack trace is:
0 libsystem_malloc.dylib malloc_zone_malloc
1 libsystem_malloc.dylib malloc
2 CFNetwork HTTPNetStreamInfo::_readStreamClientCallBack(__CFReadStream*,
unsigned long)
3 CFNetwork
CFNetworkReadStream::_readStreamClientCallBackCallBack(__CFReadStream*,
unsigned long, void*)
4 CoreFoundation _signalEventSync
5 CoreFoundation _cfstream_shared_signalEventSync
6 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
7 CoreFoundation __CFRunLoopDoSources0
8 CoreFoundation __CFRunLoopRun
9 CoreFoundation CFRunLoopRunSpecific
10 CFNetwork +[NSURLConnection(Loader) _resourceLoadLoop:]
11 Foundation __NSThread__main__
12 libsystem_pthread.dylib _pthread_body
13 libsystem_pthread.dylib _pthread_start
14 libsystem_pthread.dylib thread_start
Which actually doesn't directly link it to anything in my code, though I think
it's safe to say it's something set up by NSURLSession or NSURLSessionDataTask,
which I do create in my code.
BUT, and this is a big but, I'm not sure if I'm interpreting Allocations
correctly. I have it set to "created and persistent", and graphing these blocks
shows a very close corrlation between them and the overall memory usage
profile, so my interpretation is that these are predominantly responsible for
the memory usage of my app. However, the shape of the graph does show that a
lot of these blocks are freed, but not all, leading to the gradual increase in
baseline allocation.
Here's the rough outline of the code I'm using to run the
NSURLSessionDataTasks. This is a method of <my object>, and it is called once
when there is data to begin downloading, and runs itself as long as there are
items in the queue to be processed.
- (BOOL) dequeueNextChunk
{
// removes next URL from the chunks queue and starts it asynchronously
downloading as a session. Returns YES if a chunk was dequeued, NO if not (queue
empty).
XVMediaChunk* chunk = [queuedChunkURLs firstObject];
if( chunk )
{
// dequeue
[queuedChunkURLs removeObjectAtIndex:0];
// schedule download and start it
[[self.session dataTaskWithURL:chunk.url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse* httpResponse =
(NSHTTPURLResponse*)response;
if( httpResponse.statusCode == 200 )
{
// got a valid response. If it's a valid chunk
add the data to the chunk object and pass it along to the next step.
if([httpResponse.MIMEType
isEqualToString:@"video/mp2t"])
{
chunk.data = data;
[self processVideoChunk:chunk];
// and schedule the next chunk in the
queue
[self
performSelectorOnMainThread:@selector(dequeueNextChunk) withObject:nil
waitUntilDone:NO];
}
}
else
{
// some other status was returned.
// code omitted that handes the error
situation, which is not relevant to the leaks problem, as that occurs for valid
responses only
}
}] resume];
return YES;
}
else
return NO;
}
In this code, the object XVMediaChunk is just a simple container that
associates a URL with a sequence number in the .m3u8 playlist. It also allows
the downloaded data to be attached, as it is here, and further processed. In
fact the remainder of the processing is very simple - the data is written to a
NSFileHandle and the XVMediaChunk is discarded.
The overall function of <my object> is to download a .m3u8 playlist, extract
the media content URLs from the playlist and queue the media chunks in order.
Each chunk is then downloaded by the above method one at a time. If the
download succeeds, the next item in the queue is downloaded. This process
sustains itself until the queue becomes empty or there's an error. Other code
resumes this dequeuing and downloading process by polling for the playlist at
the rate determined by the .m3u8 target schedule time - that all works and is
likely outside the scope of my problem.
--Graham
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]