iOS App Crashes on Some Devices Not on Others

OK. I am beside myself here.

I’ve had an iOS app that has been in the store for a couple years now. I recently started working on some updates to it and one of my business partners who’s been testing it says it’s been crashing. It was working fine on my end and also on my iPad Pro and my iPhones. No problem.

His crash reports in TestFlight didn’t tell me a lot. Something in the framework that was a NIL object exception or something like that. One of those vague Apple errors.

So I asked him to send me the copy of his database that I load into the app. So I tried to duplicate his setup. No crashes. Then I decide to try loading things onto my 7th generation iPad. Boom! Crash happens. So now I go running it in the debugger on this iPad. Trying to catch exceptions - nothing. The app just crashes. Searching through the logs, I did fine this one:

3836 error 21:47:27.939944-0500 MediaSwitcher.debug CGSImageDataLock: Cannot allocate memory

Now, my app presents what is basically a grid of TV screens that are Container Controls with ImageView controls in them. You can see my app on the AppStore - MediaSwitcher for iOS. Now his system being loaded has a lot of TV Screens - about 150. The database I load is created with my desktop app and then transferred to the iOS app. He saved some rather large image files in his database to represent the icons to display for the screens. So I thought - maybe I am running out of memory by trying to load all these icons. So I tried scaling the images down to like 250x200 pixels. Nope still crashed. So I thought maybe instead of loading all these images at startup into 150 different objects each which basically is 150 different images that I’d load them only when I display them. Nope - still a crash.

Now I’m taking my images and after scaling them to like 200 x 250 pixels, I am storing every unique picture in a dictionary and pulling the picture when I need it. Nope still crashed and I checked just before crashing using a break point and I have about 5 picture objects in the dictionary.

Same exact crash - cannot allocate memory…

I have no clue. I can’t believe an 12" iPad Pro of the same vintage as my iPad 7th gen would have so much more memory. Same thing with my phones. I really have no clue here. It crashes after just loading a few images. It’s like in my loop, the memory is not being released each time through the loop.

To me this problem seems to have started with iOS 15.7 on the iPad. But if it was just iOS15.7, then I should have seen the crash on my iPad Pro but I don’t.

I am running Xojo 2022r3 now. I though maybe updating from r2 to r3 would fix it. Nope. Running Xcode 14.0.1 MacOS 12.6.1.

I am really clueless here. I hope someone can give me some insight…

And as a sanity check, I just tried my latest code with this same database on my iPhone. Works just fine. I raise the low memory event a couple times but no crashes.

Not sure what is holding all the memory but it doesn’t crash.

Did you implement the LowMemoryWarning event on app? If so, are you cleaning up as much memory as you possibly can?

First of all - that event never fires on the crashing database.

Second - what can you do to clean up memory usage? I close objects when I don’t need them, etc.

I’ve had the same issue where I was loading 20-30 pictures at once and the app was crashing on some devices.

I fixed it by using a Table with a datasource to only load the visible pictures. As soon as a picture isn’t displayed anymore, it is nilled to free up memory.

As you seem to be displaying a grid and not a table, you could try ARiOSMobileCollectionView from https://falcosoftware.com/xojo/
It uses a datasource pattern similar to the table, which helps only load what is necessary.

2 Likes

Problem is I’m not even getting to the point where the app starts to display or load images into any controls on the screen. This is all loading images into a dictionary in the background. Haven’t even started creating the UI controls yet.

I can’t use any predefined table or grid controls. The images may look like a grid by default but there are instances where on the desktop they user may move them around to specific points on the screen.

What looks to me what is happening is as I am going through my loop loading the image data that Xojo is not releasing the memory allocated to objects created in each iteration of the loop. For example, I create a picture object in the loop from a database row. If I determine I’ve already stored that image in the dictionary from a previous row, I don’t store it. I just continue on. Then that picture goes out of scope when starting the next iteration of the loop. Once it goes out of scope, that memory should be released. But that doesn’t look like what is happening.

Just out of curiosity, could you show your picture loading code? In iOS, they should load as an “image” which is just a reference to the picture on disk until the drawing system actually needs it.

1 Like

You might not know, the system will kill your app if it is too greedy in memory.

Loading many pictures in a tight loop does not give enough time to the system to release the memory.

I am also curious to see your picture loading code.

This is what I am wondering - If my loop is too tight and the memory doesn’t get released in time. I think that may be the case because It really doesn’t matter what I do in terms of scaling the loading images, or how I handle them - the app always crashes around 60 to 70 iterations through the loop.

I can post some of the code that I am using. Give me some time today to get snippets of it put together…

The problem with scaling is that:

  1. You open the full size image
  2. You resize the image
  3. You keep a reference to the resized image

This is three steps which all allocate memory and are quite intensive for a device that only has 1-4GB of RAM

Try running your app in the simulator with Instruments open to see how much memory is used during that process.
(If you need help in doing so just PM me, I’m available for a screen share for the next two hours)

Hi Jeremie,

That’s a good idea to run in the simulator with instruments open. I will try that. I’m tied up for the next several hours so I won’t be able to take you up on your offer of a screen share, but thank you. Very generous. I may take you up sometime.

I agree with you about the scaling - it’s certainly a problem if the memory is not getting released quick enough. It makes it worse.

Thanks,

Jon

Just thought about something I do in one of my apps.

The app bundle comes with all 60 images in the highest resolution (for display on iPhone Max/Plus).
As soon as one of the images is needed I check if I have already resized it in the caches folder.
If the resized image wasn’t found I open it, resize it and save it in the caches folder.

Using the caches folder means the system can automatically delete files if it is running low in storage.
Reading the image + resizing is a bit slower the first time, but much faster afterwards when only reading the resized file.

I made some substantial progress today. Not long after reading the posts here, I realized that I was duplicating a lot of effort and was reading in these images multiple times when I didn’t need to. Now I am basically just reading them once. It made a substantial difference. The app now loads and runs!

I did some profiling using instruments and various versions of the app installed on my iPad. When I was loading all the images originally and the app was crashing, I was taking up to 2.5 to 3 GB of memory before the app crashed. With the way I am doing it now, I get up to about 500 MB of memory use but then that releases and I’m at less than 100 MB.

It definitely helps to re-scale the images. If I don’t do that, I still am able to stay running but my memory usage goes up to about 1.2 GB.

Is there a way to not run the loop so “tight” so that the garbage collector happens more easily? If I put the code in a thread and sleep the thread for a few mS each iteration would that help? I’ll try it…

Considering that’s the total amount of RAM in some devices, it’s not surprising that you had crashes.

Yep. I didn’t realize how much memory can be retained even though objects inside a loop have done out of scope. I thought that when the loop boundary is hit and the context switch happens that the memory would get released right away. Obviously that is not the case!

I did some more optimization and now I am seeing a peak memory usage of about 175 MB when loading the images. This could vary from use case to use case, but I think it’s way more efficient than what I had previously.

Going to apply the same concept to my desktop app. Make it better too…