Watch kit App : Terminated due to memory error - ios

Hi I am developing an app in which I require to chache 50 images (size of all images is 2.5 mb) ,It is chaching the images but also increases the memory by 10 mb in Apple Watch App due to which app crashes .
Xcode gives error in xCode “Message from debugger: Terminated due to memory error”
The code i am using is below :
for (var i : Int = 1; i<26; i++) {
let filenameHuman = NSString(format: "human_%d", i )
let filenameZombie = NSString(format: "zombie_%d", i )
var imageHuman : UIImage! = UIImage(named: filenameHuman as String)
var imageZombie : UIImage! = UIImage(named: filenameZombie as String)
WKInterfaceDevice.currentDevice().addCachedImage(imageZombie, name: filenameZombie as String)
WKInterfaceDevice.currentDevice().addCachedImage(imageHuman, name: filenameHuman as String)
}
NSLog("Currently cached images: %#",WKInterfaceDevice.currentDevice().cachedImages)
Also the screenshot of Memory allocation and memory leak is :
Please Help, Thanks in advance .

Are any of your images actually animations (that would use up more space)?
Collect the return value of each call to addCachedImage(). False means it could not be added -- you need to check to that and it might give clues as to a particular problem image.
Before calling anything, try to empty the cache, removeAllCachedImages. This means you will be clean from previous cache interactions using up the pool of memory.
I think your problem is not a leak, I think your problem is over-retained allocations. So use the Allocations tool (with retain count tracking) to see how much memory was allocated (VM allocations) and how many entities are holding on to such memory (retain counts).

Inside your loop try to autorelease the memory that is used by the images since you don't want to wait for autorelease to happen later when the method returns.
for (var i : Int = 1; i<26; i++) {
autoreleasepool {
/* code to cache images */
}
}

Related

How to free memory from VNGeneratePersonSegmentationRequest

I’m playing with the new VNGeneratePersonSegmentationRequest Vision API to make a simple background removal filter
I made a small project to test it, works great, but I’m running into issues with memory. After executing the request the app’s memory consumption adds 300MBs that are never freed.
I’ve cycled through a bunch of images and requests in a test run and thankfully memory consumption remains constant even when filtering more images, but I worry about that initial memory that is never freed, even when inducing a memory warning. I suspect the Vision Framework needs that memory after being called, but my app doesn’t handle video frames or anything, so it’s memory going to waste
//Autorelease pool doesn't helps
autoreleasepool {
// Create request
let request = VNGeneratePersonSegmentationRequest()
request.revision = VNGeneratePersonSegmentationRequestRevision1
request.qualityLevel = .accurate
request.outputPixelFormat = kCVPixelFormatType_OneComponent8
let handler:VNImageRequestHandler = VNImageRequestHandler(ciImage: inputs.ciImage,
options: [:])
//A jump in memory usage after running the handler
//Subsequent calls don't add to memory usage
do{
try handler.perform([request])
}
catch{
return
}
//Even if I delete this chunk of code, memory consumption remains high
let mask = request.results?.first!
if let maskBuffer = mask?.pixelBuffer{
self.personMask = CIImage(cvPixelBuffer: maskBuffer)
let maskScaleX = inputs.ciImage.extent.width / personMask!.extent.width
let maskScaleY = inputs.ciImage.extent.height / personMask!.extent.height
self.personMask = personMask!.transformed(by: __CGAffineTransformMake(
maskScaleX, 0, 0, maskScaleY, 0, 0))
}
}

Does NSCache gets automatically emptied when the app is force quitted? [duplicate]

This question already has an answer here:
NSCache emptied when app enters background
(1 answer)
Closed 2 years ago.
I am downloading some images and save them in my cache. So far so good, but when quit my app and relaunch it, the cache seems to be empty. I do not know how to check if the cache is actually empty which is why I am asking if the cache gets automatically emptied when the app was force quitted.
let cache = NSCache<NSString, UIImage>() // cache for the downloaded images
Yes it does that, for some reason it instantly removes data from cache when app enters background even if there is no memory pressure. To fix this you have to tell NSCache that your data should not be discarded.
What you could do is something like:
class ImageCache: NSObject , NSDiscardableContent {
public var image: UIImage!
func beginContentAccess() -> Bool {
return true
}
func endContentAccess() {
}
func discardContentIfPossible() {
}
func isContentDiscarded() -> Bool {
return false
}
}
and then use this class in NSCache like the following:
let cache = NSCache<NSString, ImageCache>()
After that you have to set the data which you cached earlier:
let cacheImage = ImageCache()
cacheImage.image = imageDownloaded
self.cache.setObject(cacheImage, forKey: "yourCustomKey" as NSString)
And finally retrieve the data:
if let cachedVersion = cache.object(forKey: "yourCustomKey") {
youImageView.image = cachedVersion.image
}
UPDATE
This was already answered by Sharjeel Ahmad. See this link for reference.
NSCache does not persist its elements to the disk. It only keeps them in memory. When an app is force quit then all its RAM is destroyed and obviously cannot be reused at the next launch

What's the most efficient way / strategy for persistent (not in memory) in app logging of text?

I have a navigation and location tracking app. While a user is tracking his trip, coordinates, speed, timestamp (and a few more) are logged with each coordinate that comes in. I don't want to store this in memory as that would make the app memory grow as the user moves along, eventually leading to a didReceiveMemoryWarning and even an app crash. (At least that's been my experience so far)
What would be the most efficient way to do this? Thinking of battery consumption, CPU usage and also memory usage if that comes into play.
I can think of two options to do this:
Log in a file (what I'm currently doing, using this code snipped):
let newLine: String = "bla bla bla"
let url: URL = URL(string: "someLocalPath")
newLine.appendToURL(url)
extension String {
func appendToURL(_ fileURL: URL) throws {
let data = self.data(using: String.Encoding.utf8)!
try data.appendToURL(fileURL)
}
}
extension Data {
func appendToURL(_ fileURL: URL) throws {
if let fileHandle = try? FileHandle(forWritingTo: fileURL) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
fileHandle.closeFile()
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
Using Core Data
I have not tried this yet, but it would be relatively easy I believe. Just creating an entity with all the fields required, and then adding a managedObject for each received coordinate.
So, to repeat my question: which of these two options would be more efficient from a battery, CPU and memory perspective?
Additional question: Is there perhaps another way to do this. One that I didn't think of?
I found another option that works best in my case. Should have thought of that in the first place 🤦🏻‍♂️.
So, I did not want to log to memory because it could make iOS kill my app due to excessive memory usage. But, iOS sends a didReceiveMemoryWarning first, upon which you can reduce the memory footprint of the app, preventing it from being killed.
So, what I do now:
I added a instance variable:
fileprivate var logString: String = ""
And log to it when a new coordinate is received:
logString += newLine
If / when I get a didReceiveMemoryWarning I write the entire logString to the file on disk and empty it afterwards:
override func didReceiveMemoryWarning() {
logString.appendToURL(url)
logString = ""
}
This way I only write to disk when necessary (and not with each coordinate received), which is a lot more energy efficient. (Writing to disk costs more energy than writing to memory. Source: https://developer.apple.com/videos/play/wwdc2016/719/)
Plus, my app is prevented from being killed by iOS due to high memory usage, because I clear up memory on time.

Can memory in my code be better managed when using UIImageView.animationImages and startAnimating()?

I'm declaring some UIImage arrays:
var animationImages1: [UIImage] = []
var animationImages2: [UIImage] = []
I'm using a background thread to load the images:
DispatchQueue.global(qos: .background).async { () -> Void in
self.animationImages1 = self.createImageArray(total: 57, imagePrefix: "animation1")
self.animationImages2 = self.createImageArray(total: 42, imagePrefix: "animation2")
}
The function called above:
var imageArray: [UIImage] = []
for imageCount in 1..<total {
var imageName = String(format: "\(imagePrefix)\(imageCount)")
let image = UIImage(contentsOfFile: Bundle.main.path(forResource: imageName, ofType: "png")!)!
//let image = UIImage(named: imageName)!
imageArray.append(image)
}
return imageArray
Then when I want to animate, I'm calling this function:
func animate(imageView: UIImageView, images: [UIImage], duration: TimeInterval) {
imageView.animationImages = images
imageView.animationDuration = duration
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
I tried doing both theImageView.stopAnimating() and theImageView.animationImages = nil before calling the animation again but didn't notice any improvement to memory management using either.
With UIImage(named:) the images visible in the app start disappearing either partially or completely as memory runs low. With UIImage(contentsOfFile:) the app promptly crashes once memory runs low.
To note: I tried UIImage(named:) with the images in the Assets catalog, and then switched to UIImage(contentsOfFile:) with the images dragged in to the project outside of Assets.xcassets
Is it possible to use this function of UIImageView for longer animations (2-5 seconds: 40-150 pngs) with a file size of about 450k each, or is it too much a memory strain regardless of how you go about it?
It currently runs without an issue on a newer iPad Pro, and using Xcode's simulator it runs well (but eats a lot of memory) on all device sizes. On an iPhone X and an iPhone 8 Plus, it runs out of memory pretty early on - after playing through 5 to 10 animations or so.
Am I missing something, is it not possible, or do I need to do further research on ways to keep memory in check while running these large UIImage arrays through startAnimating()?
Memory usage is not going down. I must be caching this somewhere...
Thanks for any help!
I created a new Xcode project simplified to focus just on this issue, and was given a correct answer by #Stephan Schlecht here.
Although the memory hit doesn't occur when assigning the images to an array, and the memory hit only happens once the animation is played, still the only way to reclaim the memory seems to be removing all references to the image array by setting both the variable array and the animationImages property on the UIImageView to a blank array.
So in the particular example given in this question:
animationImages1 = []
imageView.animationImages = []

ios swift - unreleased memory when appending uiimage to array

I'm appending uiimages from core data to an array in order to create a gif of them.
I'm creating the gif and then emptying the array; however, I still have an indefinite 50 mb memory allocation from the moment I create the gif.
I tried looking at instruments and this is what I'm getting in the call tree:
Call tree image
This is the code:
do{
let objects = try managedObjectContext.executeFetchRequest(request)
let results = objects
if results.count > 0 {
for var i = 0; i < results.count; i += 1{
let match = results[i] as! cFW
date.append(match.date)
let image = match.image
fetchedImage.append(UIImage(data: image)!)
}
} else {
}
}
catch{}
Even after deleting the gif and the array already being deleted, the app stays at 50mb of memory usage.
Thank you
edit: The issue that I'm having has to do with how I'm displaying the gif (in a webview). I will update the question with my solution asap
The issue was happening due to presenting the gif in a webview.
Clearing the cache of web view did not resolve the allocation of memory.
I ended up using a library to FLAnimatedImage to present my GIF.
No more unreleased memory.
Thank you guys

Resources