11

An app that we developed several years ago has started crashing since iOS 10 became available. The app repeatedly presents UIImagePickerControllers to allow a user to capture several photos of a subject. After doing this 40+ times, the app crashes. This is reproducible on all of the devices we've tested on, but did not happen prior to the introduction of iOS 10. We do have the NSCameraUsageDescription and NSPhotoLibraryUsageDescription keys in the Info.plist file.

I've created a minimal sample app that demonstrates the problem. Here's the code that eventually results in the crash:

- (IBAction) cameraPressed:(id) sender {
    self.picker = [[UIImagePickerController alloc] init];
    self.picker.sourceType =  UIImagePickerControllerSourceTypeCamera;
    self.picker.delegate = self;
    self.picker.allowsEditing = NO;
    [self presentViewController:self.picker animated:YES completion:nil];
}

When the user repeatedly uses the camera button that is connected to this IBAction, it eventually starts to slow down (presentation of the picker begins taking longer), and eventually crashes (usually after 50+ uses of the camera). No crash log is produced, although when connected to Xcode, output like this appears in the console leading up to the crash:

2016-11-04 20:30:11.884984 WLPBeta[2747:275474] [MC] Invalidating cache
2016-11-04 20:30:11.890776 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:30:13.017608 WLPBeta[2747:275091] [MC] Invalidating cache
2016-11-04 20:30:13.018312 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:30:19.271720 WLPBeta[2747:276311] [MC] Invalidating cache
2016-11-04 20:30:19.279462 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:08.229294 WLPBeta[2747:278515] [MC] Invalidating cache
2016-11-04 20:32:08.273941 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:09.335711 WLPBeta[2747:278514] [MC] Invalidating cache
2016-11-04 20:32:09.342161 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:14.193376 WLPBeta[2747:278515] [MC] Invalidating cache
2016-11-04 20:32:14.213902 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:47.944657 WLPBeta[2747:275091] [MC] Invalidating cache
2016-11-04 20:32:47.972053 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:48.550934 WLPBeta[2747:279485] [MC] Invalidating cache
2016-11-04 20:32:48.575065 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:50.855308 WLPBeta[2747:279485] [MC] Invalidating cache
2016-11-04 20:32:50.856329 WLPBeta[2747:273019] [MC] Reading from public effective user settings.
2016-11-04 20:32:52.201535 WLPBeta[2747:275091] [GatekeeperXPC] Connection to assetsd was interrupted or assetsd died

I've also posted a bug report to Apple, and added it to open radar: http://www.openradar.me/radar?id=4941109843197952

Has anyone encountered this issue? Is there a fix that would allow us to move past this without recompiling the app using Xcode 8? Unfortunately we are using third party libraries that are not yet ready for Xcode 8 and the iOS 10 SDK, so we are still building the app using Xcode 7.3.1.

EDIT: here's a link to a Github repo containing an example app that can be used to demonstrate the problem.

https://github.com/lolay/ImagePickerTest

EDIT 2: If I change the code so that I allocate and initialize the picker in viewDidLoad, it still crashes after about the same number of uses of the camera button.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.picker = [[UIImagePickerController alloc] init];
    self.picker.sourceType =  UIImagePickerControllerSourceTypeCamera;
    self.picker.allowsEditing = NO;
}

- (IBAction) cameraPressed:(id) sender {
    [self presentViewController:self.picker animated:YES completion:nil];
}
Greg
  • 33,450
  • 15
  • 93
  • 100
  • Have you used Instruments? Perhaps you have a memory leak or a reference cycle. – rmaddy Nov 09 '16 at 17:44
  • We have, and did not detect any leaks or reference cycles. As noted in the question, I boiled down the problem to a minimal sample app with just a few lines of code added to the skeleton project created by Xcode. The code I added is shown in the question. – Greg Nov 09 '16 at 17:45
  • is there any particular reason you have to do the initialization of imagePicker every time the button is pressed? Why not move all the lines of code to viewDidLoad and when button presses, just fire the presentViewController method? – Maciej Chrzastek Nov 09 '16 at 19:46
  • No particular reason, but if I move the initialization of the picker into viewDidLoad, I still experience the crash after about the same number of pictures taken. – Greg Nov 09 '16 at 23:54
  • You are likely running into a ref count loop, set the self.picker.delegate object to some other object (not self from the view controller) to avoid a ref count loop problem. – MoDJ Nov 10 '16 at 00:46
  • @MoDJ I actually removed the setting of the delegate entirely (because it's not needed in this simple example app), and the app still crashes. Also, the delegate of UIImagePickerController is a weak reference. I've updated the second code example above, as well as the example on GitHub. – Greg Nov 23 '16 at 19:51
  • Hi, have run into a very similar problem using Cordova / Phonegap. Have you had any joy finding a resolution ... if so, any pointers would be much appreciated. Thanks – Temple Nov 29 '16 at 15:03
  • @Greg: Is the "camera" button the only button connected to this IBAction? –  Nov 30 '16 at 01:10
  • Most likely a memory leak in UIImagePickerController somewhere. Not much you can do about it, except reporting it to Apple. Possibly related: https://stackoverflow.com/questions/6554225/uiimagepickercontroller-memory-leak – fishinear Jul 28 '17 at 10:36

4 Answers4

1

Your UIImagePickerController is defined as STRONG.

Please change it to weak or make sure that the object is nil before the view is destroyed.

doxsi
  • 1,002
  • 19
  • 42
0

Try this code.

- (IBAction) cameraPressed:(id) sender {
   if (self.picker == nil){
      self.picker = [[UIImagePickerController alloc] init];
   }

    self.picker.sourceType =  UIImagePickerControllerSourceTypeCamera;
    self.picker.delegate = self;
    self.picker.allowsEditing = NO;
    [self presentViewController:self.picker animated:YES completion:nil];
}
handiansom
  • 783
  • 11
  • 27
0

Have you tried declaring the Imagepicker variable inside the cameraPressed() function

- (IBAction) cameraPressed:(id) sender {
   UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.sourceType =  UIImagePickerControllerSourceTypeCamera;
    picker.delegate = self;
    picker.allowsEditing = NO;
    [self presentViewController:self.picker animated:YES completion:nil];
}
Aravind A R
  • 2,674
  • 1
  • 15
  • 25
0

You need to explicitly dismiss the UIImagePickerController, even though it appears to do that automatically. It probably retains the view controller if you don't, leading to memory problems.

From the UIImagePickerControllerDelegate documentation:

If you set the image picker’s showsCameraControls property to NO and provide your own custom controls, you can take multiple pictures before dismissing the image picker interface. However, if you set that property to YES, your delegate must dismiss the image picker interface after the user takes one picture or cancels the operation.

By default, that property is YES, so you need to dismiss.

Use something similar to the following:

- (IBAction) cameraPressed:(id) sender {
    UIImagePickerController* picker = [[UIImagePickerController alloc] init];
    picker.sourceType =  UIImagePickerControllerSourceTypeCamera;
    picker.allowsEditing = NO;
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}

- (void) imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
fishinear
  • 6,101
  • 3
  • 36
  • 84