Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVCam memory low warning

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.

like image 639
Red Nightingale Avatar asked Oct 17 '11 08:10

Red Nightingale


3 Answers

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!

like image 76
Red Nightingale Avatar answered Nov 16 '22 21:11

Red Nightingale


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 adapted AVCam code in your app, you should adopt the fixes made here in AVCaptureManager.m's init method. Without these fixes, you may be leaking AVCaptureManager 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:

  • memory leaks free
  • uses ARC
  • modern Objective-C syntax
  • minor UI fixes for iOS 7
like image 44
Gabriele Petronella Avatar answered Nov 16 '22 20:11

Gabriele Petronella


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

like image 2
Chen Lim Avatar answered Nov 16 '22 20:11

Chen Lim