This is less a question and more a record of what I've found around the AVCam sample code provided by Apple for iOS4 and 5 camera manipulation. The symptoms of the problem for me were that my app would crash on launching the AVCamViewController after taking around 5-10 photos.
I ran the app through the memory leak profiler and there were no apparent leaks but on inspection with the Activity Monitor I discovered that something called mediaserverd was increasing by 17Mb every time the camera was launched and when it reached ~100Mb the app crashed with multiple low memory warnings.
First thing I did was put logging into the dealloc methods of all the AVCam files. I quickly discovered that the AVCamCaptureManager and AVCamRecorder weren't being deallocated when the AVCamViewController was. I checked out the retain and release calls and they appeared to balance so I put a breakpoint on the [captureManager release] and discovered that it had a retainCount of 2 AFTER the release (and hence the AVCamCaptureManager dealloc wasn't being called).
Next I stepped through the creation process for the capture manager and discovered that it had a retain count of 3 immediately after the init method was called.
Stepping through the init method and checking the retain count on every line I discovered the following two lines were both incrementing the retain count:
[self setDeviceConnectedObserver=[notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification object:nil queue:nil usingBlock:deviceConnectedBlock]];
[self setDeviceDisconnectedObserver=[notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification object:nil queue:nil usingBlock:deviceDisconnectedBlock]];
Looking through I found that the removeObserver counterparts were INSIDE the dealloc method of the AVCamCaptureManager (which wasn't being called) and so the retain count never dropped to 0.
To fix it I created a new public removeObservers method:
-(void)removeObservers {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:[self deviceConnectedObserver]];
[notificationCenter removeObserver:[self deviceConnectedObserver]];
}
and taking the same lines OUT of the AVCamCaptureManager dealloc method.
Calling [captureManager removeObservers]; and THEN calling [captureManager release]; in the AVCamViewController dealloc method successfully drops the retain count to 0.
Testing with the Activity Monitor now has the mediaserverd process humming at only 5-17Mb and the crashing stops!
Hope this helps anyone else having this problem!
Apple revised the sample code on Oct 17 2013, fixing the retain cycle. The issue is due to a improper usage of self
within the blocks defined in the init
.
Here's the revision description
Fixed retain cycles in
AVCaptureManager
that result in leaks. NOTE - if you've adaptedAVCam
code in your app, you should adopt the fixes made here inAVCaptureManager.m
'sinit
method. Without these fixes, you may be leakingAVCaptureManager
instances and leaving the camera running constantly while your app is in the foreground.
However, the fix they introduced only works in case of Manual Retain Count. If you are using ARC on the project, apart from getting rid of release
/retain
calls and other obvious things, the storage qualifier for weakSelf
has to be changed from __block
to __weak
, like follows.
__weak AVCamCaptureManager *weakSelf = self;
In fact the semantic of __block
changed with ARC. In MRC it caused the variable to be weakly referenced, wheres in ARC it does not and __weak
must be used for this purpose.
More information about this topic can be found here: How do I avoid capturing self in blocks when implementing an API?
Using the new init
implementation from the last revision and using __weak
instead of __block
, finally caused the dealloc
method to be properly called.
Finally, for those who hate to carry around old legacy code, here's a modernized version of the AVCam
project: https://github.com/Gabro/AVCam
Features:
Ran into this problem recently. I found that the real root problem was that deviceConnectedBlock and deviceDisconnectedBlock were implicitly referring to self, leading to retain cycles. To fix it, change all the ivar references in those blocks to use weakSelf.
This way, you won't need to remember to call a teardown method explicitly.
Hope this helps someone else.
REF: View controller dealloc not called when using NSNotificationCenter code block method with ARC
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With