29

This question was cleaned up and the important info moved to the answer below.


I have some questions about memory management.

I am building a photo editing app. So keeping memory usage low is important. Also I am not going to post code because I do not have a big memory leak when doing one specific thing. I just lose a couple of KB's/MB's with everything that happens. And going over tens of thousands of lines of code to find kilobytes is no fun ;)

my app uses Core Data, lots of CIFilter stuff, Location, and the basics.

My first view is just a tableview which costs me about 5mb of memory. Then you take some photos, apply some filters, this gets saved to core data and then you go back to that first view.

Is it possible to truly get rid of everything in memory except for the data needed to drive that first view. (that very save and awesome 5mb)

Or will there always be something left behind, even if you set everything to nil?


Bonus Question: is there a difference in file size / cpu load between UIImageJPEGRepresentation and UIImagePNGRepresentation? I know you can set a compression quality with the JPEG method (harder on the cpu/gpu?).

Just trying to reduce memory pressure by all means possible.


Update:

It was pointed out to me that the question might be too vague.

The problems I was having at some point or another were the following:

  • At some points peak memory usage is too high
  • Navigating to a second viewcontroller and back causes a leak
  • Editing an image causes a memory leak.
  • Applying a filter to more than 4-5 images causes a crash because of low memory, there were no more memory leaks at this point. (verified in instruments)

P.s this was all tested on an iPhone 4s , not the simulator.

There was a meme here to lighten the mood on this site a bit.

hippietrail
  • 15,848
  • 18
  • 99
  • 158
R Menke
  • 8,183
  • 4
  • 35
  • 63
  • @brian thx for the edit! ACR comes from adobe camera raw and many years of being a photographer. I always saw it as ACR even though I know what it stands for :) – R Menke Jan 15 '15 at 07:05
  • 6
    *"ARC only really steps in when the memory pressure is beyond a certain threshold"* – No. ARC is Automatic Reference Counting and not a garbage collector. – Martin R Jan 15 '15 at 07:08
  • 2
    Have to tried to use *snapshots* in Instruments to locate the "disappearing memory"? – Martin R Jan 15 '15 at 07:10
  • @MartinR I traced big chunks of disappearing memory to image variables that where not optional. I fixed those, now I am working on the small bits. Going at them one by one is an option. Truly understanding the issue is better. ARC is a type of garbage collection, if the interwebs do not lie to me... – R Menke Jan 15 '15 at 07:42
  • @MartinR But I get what you mean. I might have described behaviour that has nothing to do with ARC, however something releases stuff from memory when there is little left and now something also frees up memory when there is still tons of memory but the app is idle. (sweet but I want to know how/why) – R Menke Jan 15 '15 at 07:50
  • 1
    You said you use Core Data. You might try to investigate around the use of _faulting_ core data managed objects when they are no longer needed. – Matteo Piombo Jan 15 '15 at 13:25
  • @perlfly That is the next step. Optimising all core data stuff. I'll be testing different ways of setting up the moc and stuff like reset and faulting. I already use objects that are almost the same as my core data entities and store strings and bools in them. Keeps my code clean and is a lot lighter than keeping managed objects alive. – R Menke Jan 15 '15 at 18:12
  • 1
    I may be wrong but I don't believe it is ARC that is stepping in when there is memory pressure; rather, I believe it is the iOS itself. Your view controllers will receive the didReceiveMemoryWarning method, and you should respond accordingly there. ARC manages the number of references to objects allocated by your app, and sets the objects to nil and deallocated them when their reference count is zero. – BJ Miller Jan 17 '15 at 00:34

2 Answers2

36

This question has been open long enough and I now feel confident enough to answer it.


Different Levels of MM:

Hardware Memory

In Swift with ARC we have no way to clean up the actual hardware ram. We can only make it possible for the OS to do that for us. One part is using the right code (optionals and weak) the other part is creating time for the OS to do it's job.

Imagine we have a function that runs on all threads indefinitely. It does one thing, load an image, convert to black/white and save. All images max at a couple of mb’s and the function creates no Software Memory Leaks. Because images don’t have a set size and might have different compression they don’t have the same footprint. This function will always crash your app.

This “Hardware” Memory Leak is caused by the function always taking the next available slot of memory.

The OS does not step in to “actually clean the memory” because there is no idle time. Putting a delay between each pass completely fixes this.


Language specific MM

Casting

Some operations don’t have an impact on memory, others do:

let myInt : Int = 1
Float(myInt) // this creates a new instance

Try casting instead:

(myInt as Float) // this will not create a new instance.

Reference Types vs Value Types | Classes vs Structs

Both have their advantages and their dangers.

Structs are memory intensive because they are Value Types. This means they copy their values when assigned to another instance, effectively doubling memory usage. There is no fix / work around for this. It is what makes Structs Structs.

Classes don’t have this behaviour because they are Reference Types. They don’t copy when assigned. Instead they create another reference to the same object. ARC or Automatic Reference Counting is what keeps track of these references. Every Object has a reference counter. Each time you assign it, it goes up by one. Each time you set a reference to nil, the enclosing function ends, or the enclosing Object deinits, the counter goes down.

When the counter hits 0 the object is deinitialised.

There is a way to prevent an instance from deinitialising, and thus creating a leak. This is called a Strong Reference Cycle.

Good explanation of Weak

class MyClass {

    var otherClass : MyOtherClass?

    deinit {
        print("deinit") // never gets called
    }
}

class MyOtherClass {

    var myclass : MyClass?

    deinit {
        print("deinit") // never gets called
    }
}

var classA : MyClass? = MyClass()

// sorry about the force unwrapping, don't do it like this
classA!.otherClass = MyOtherClass()
classA!.otherClass!.myclass = classA // this looks silly but in some form this happens a lot

classA = nil
// neither the MyClass nor the MyOtherClass deinitialised and we no longer have a reference we can acces. Immortalitiy reached they have.

set one reference to weak

class MyOtherClass {

    weak var myclass : MyClass?

    deinit {
        print("deinit") // gets called
    }
}

inout

Functions capture the values passed to them. But it is also possible to mark those values as inout. This allows you to change a Struct passed to a function without copying the Struct. This might save memory, depending on what you pass and what you do in the function.

It is also a nice way of having multiple return values without using tuples.

var myInt : Int = 0

// return with inout
func inoutTest(inout number: Int) {

    number += 5

}

inoutTest(&myInt)
print(myInt) // prints 5

// basic function with return creates a new instance which takes up it's own memory space
func addTest(number:Int) -> Int {

    return number + 5

}

Functional Programming

State is Value over Time

Functional programming is the counter part of Object Oriented programming. Functional programming uses Immutable state.

More on this here

Object Oriented programming uses Objects that have changing/mutating states. Instead of creating a new value, the old values are updated.

Functional Programming can use more memory.

example on FP


Optionals

Optionals allow you to set thing to nil. This will lower the reference count of Classes or deinitialise Structs. Setting things to nil is the easiest way to clean up memory. This goes hand in hand with ARC. Once you have set all references of a Class to nil it will deinit and free up memory.

If you don’t create an instance as an optional, the data will remain in memory until the enclosing function ends or the enclosing class deinits. You might not know when this will happen. Optionals give you control over what stays alive for how long.


API MM

Many "memory leaks" are cause by Frameworks that have a “clean up” function that you might not have called. A good example is UIGraphicsEndImageContext() The Context will stay in memory until this function is called. It does not clean up when the function that created the context ends, or when the image involved is set to nil.

Another good example is dismissing ViewControllers. It might make sense to segue to one VC and then segue back, but the segue actually creates a VC. A segue back does not destroy a VC. Call dismissViewControllerAnimated() to remove it from memory.

Read the Class References and double check there are no “clean up” functions.


If you do need Instruments to find a leak, check out the other answer on this question.

Community
  • 1
  • 1
R Menke
  • 8,183
  • 4
  • 35
  • 63
  • This answer is misleading on many levels, some sentences are just wrong. Some parts don't even have anything to do with the topic (e.g. that paragraph about functional programming which is also completely wrong). – Sulthan Nov 03 '15 at 20:42
  • 4
    @Sulthan checked it all again. I shortened the part about functional programming to the basic idea and added a link to more info. Functional programming does tend to use more memory. I asked this question when I started programming and it was very broad: "How to free memory in swift and how to prevent using too much memory". This answer covers most it. If you downvote and say it is wrong, please provide sources. A big part of the answer was first included in the question as updates. The 7 upvotes might be a result of people finding it useful and correct. Recently I moved it to this answer. – R Menke Nov 03 '15 at 21:00
  • 2
    @Sulthan all I want is to improve the answer if it is needed, so I was seriously asking about what parts are wrong/misleading. I am not stubborn and can accept it when I made a mistake. Can you provide more feedback instead of just downvoting and leaving a negative comment? – R Menke Nov 03 '15 at 21:31
  • Functional programming doesn't have to rely on immutable state. There are languages that are functional and rely on immutable state but that doesn't mean the two concepts are necessarily connected. "hardware memory leak" is a really strange term... The operating system (OS) doesn't have anything to do with the memory management on application level. The whole answer is a bit large for detailed review but trust me, the review is needed. The biggest problem is that the actual question is unclear and that's probably the only reason why it has not been closed as a duplicate. – Sulthan Nov 03 '15 at 21:48
  • 1
    @Sulthan hardware memory leak is maybe not a perfect term but it is what happens. Certain operations leave memory unusable until there has been idle time at which point it is cleaned up. Which is comparable to a memory leak. It was one of the biggest issues I had when first asking the question. Functional Programming might not rely on immutable state but the concept is generally associated with it. I could remove the term Functional Programming and describe it purely as mutable vs immutable? Isn't it a bit drastic to close a relatively well received question after so much time? – R Menke Nov 03 '15 at 22:00
6

enter image description here

click on your apps name in the top-right corner of Xcode.

enter image description here

click on 'edit scheme' in the menu that pops up.

enter image description here

make sure 'RUN' is selected on the left side, then click the diagnostics tab near the top of the window.

under the 'memory management' header check the 'enable Guard Malloc'

you may also want to try checking 'distributed objects' and 'malloc stack' under the 'logging' header

more info on guard malloc, guard edges and scribble can be found here.



hope this helps!

Community
  • 1
  • 1
MoralCode
  • 1,954
  • 1
  • 20
  • 42
  • 1
    sorry i have a confusion. in this way xcode disable the memory usage. what's benefit for it? is it just hide showing memory usage or slow down memory usage? – Mubashar Nov 23 '17 at 09:47