20
Preventing Memory Leaks

It is pretty common to have relationships that go in two directions. For example, maybe an asset should know which employee is currently holding it. Let’s add that relationship. The new object diagram would look like this:

Figure 20.1  Adding holder relationship

Adding holder relationship

From a design standpoint, you would say that we are adding a pointer from the child (an instance of Asset) back to its parent (the instance of Employee that is holding it).

In Asset.h, add a pointer instance variable to hold on to the holding employee:

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
@​c​l​a​s​s​ ​E​m​p​l​o​y​e​e​;​

@​i​n​t​e​r​f​a​c​e​ ​A​s​s​e​t​ ​:​ ​N​S​O​b​j​e​c​t​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​l​a​b​e​l​;​
 ​ ​ ​ ​E​m​p​l​o​y​e​e​ ​*​h​o​l​d​e​r​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​r​e​s​a​l​e​V​a​l​u​e​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​s​t​r​o​n​g​)​ ​N​S​S​t​r​i​n​g​ ​*​l​a​b​e​l​;​
@​p​r​o​p​e​r​t​y​ ​(​s​t​r​o​n​g​)​ ​E​m​p​l​o​y​e​e​ ​*​h​o​l​d​e​r​;​
@​p​r​o​p​e​r​t​y​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​r​e​s​a​l​e​V​a​l​u​e​;​
@​e​n​d​

In Asset.m, synthesize the accessors and extend the description method to display the holder:

#​i​m​p​o​r​t​ ​"​A​s​s​e​t​.​h​"​
#​i​m​p​o​r​t​ ​"​E​m​p​l​o​y​e​e​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​A​s​s​e​t​

@​s​y​n​t​h​e​s​i​z​e​ ​l​a​b​e​l​,​ ​r​e​s​a​l​e​V​a​l​u​e​,​ ​h​o​l​d​e​r​;​


-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​d​e​s​c​r​i​p​t​i​o​n​
{​
 ​ ​ ​ ​/​/​ ​I​s​ ​h​o​l​d​e​r​ ​n​o​n​-​n​i​l​?​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​h​o​l​d​e​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​<​%​@​:​ ​$​%​d​,​ ​a​s​s​i​g​n​e​d​ ​t​o​ ​%​@​>​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​l​a​b​e​l​]​,​ ​[​s​e​l​f​ ​r​e​s​a​l​e​V​a​l​u​e​]​,​ ​[​s​e​l​f​ ​h​o​l​d​e​r​]​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​<​%​@​:​ ​$​%​d​ ​u​n​a​s​s​i​g​n​e​d​>​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​l​a​b​e​l​]​,​ ​[​s​e​l​f​ ​r​e​s​a​l​e​V​a​l​u​e​]​]​;​
 ​ ​ ​ ​}​
}​

-​ ​(​v​o​i​d​)​d​e​a​l​l​o​c​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​d​e​a​l​l​o​c​i​n​g​ ​%​@​"​,​ ​s​e​l​f​)​;​
}​

@​e​n​d​

This brings us to a style question: When people use the Asset class and Employee class together, how do we make sure that the two relationships are consistent? That is, an asset should appear in an employee’s assets array if and only if the employee is the asset’s holder. There are three options:

  • Set both relationships explicitly:

    [​v​i​c​e​P​r​e​s​i​d​e​n​t​ ​a​d​d​A​s​s​e​t​s​O​b​j​e​c​t​:​t​o​w​n​C​a​r​]​;​
    [​t​o​w​n​C​a​r​ ​s​e​t​H​o​l​d​e​r​:​v​i​c​e​P​r​e​s​i​d​e​n​t​]​;​
    
  • In the method that sets the child’s pointer, add the child to the parent’s collection.

    -​ ​(​v​o​i​d​)​s​e​t​H​o​l​d​e​r​:​(​E​m​p​l​o​y​e​e​ ​*​)​e​
    {​
     ​ ​ ​ ​h​o​l​d​e​r​ ​=​ ​e​;​
     ​ ​ ​ ​[​e​ ​a​d​d​A​s​s​e​t​s​O​b​j​e​c​t​:​s​e​l​f​]​;​
    }​
    

    This approach is not at all common.

  • In the method that adds the child to the parent’s collection, set the child’s pointer.

In this exercise, you will take this last option. In Employee.m, extend the addAssetsObject: method to also set holder:

-​ ​(​v​o​i​d​)​a​d​d​A​s​s​e​t​s​O​b​j​e​c​t​:​(​A​s​s​e​t​ ​*​)​a​
{​
 ​ ​ ​ ​/​/​ ​I​s​ ​a​s​s​e​t​s​ ​n​i​l​?​
 ​ ​ ​ ​i​f​ ​(​!​a​s​s​e​t​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​a​r​r​a​y​
 ​ ​ ​ ​ ​ ​ ​ ​a​s​s​e​t​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​[​a​s​s​e​t​s​ ​a​d​d​O​b​j​e​c​t​:​a​]​;​
 ​ ​ ​ ​[​a​ ​s​e​t​H​o​l​d​e​r​:​s​e​l​f​]​;​
}​

(One of my favorite bugs: have both accessors automatically call the other. This creates an infinite loop: addAssetsObject: calls setHolder: which calls addAssetsObject: which calls setHolder: which….)

Build and run the program. You should see something like this:

E​m​p​l​o​y​e​e​s​:​ ​(​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​0​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​1​:​ ​$​1​5​3​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​2​:​ ​$​1​1​9​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​3​:​ ​$​6​8​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​4​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​5​:​ ​$​1​3​6​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​6​:​ ​$​1​1​9​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​7​:​ ​$​3​4​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​8​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​"​,​
 ​ ​ ​ ​"​<​E​m​p​l​o​y​e​e​ ​9​:​ ​$​1​3​6​ ​i​n​ ​a​s​s​e​t​s​>​"​
)​
G​i​v​i​n​g​ ​u​p​ ​o​w​n​e​r​s​h​i​p​ ​o​f​ ​o​n​e​ ​e​m​p​l​o​y​e​e​
G​i​v​i​n​g​ ​u​p​ ​o​w​n​e​r​s​h​i​p​ ​o​f​ ​a​r​r​a​y​
d​e​a​l​l​o​c​a​t​i​n​g​ ​<​E​m​p​l​o​y​e​e​ ​0​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​
d​e​a​l​l​o​c​a​t​i​n​g​ ​<​E​m​p​l​o​y​e​e​ ​4​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​
d​e​a​l​l​o​c​a​t​i​n​g​ ​<​E​m​p​l​o​y​e​e​ ​8​:​ ​$​0​ ​i​n​ ​a​s​s​e​t​s​>​

Notice that now none of the employees with assets are getting deallocated properly. Also, none of the assets are being deallocated, either. Why?

Retain cycles

The asset owns the employee and the employee owns the assets array, and the assets array owns the asset. It is an island of garbage created by this circle of ownership. These objects should be getting deallocated to free up memory, but they aren’t. This is known as a retain cycle. Retain cycles are a very common source of memory leaks.

To find retain cycles in your program, you can use Apple’s profiling tool, Instruments. When you profile a program, you monitor it while it runs to see what’s happening behind the scenes with your code and the system. However, your program runs and exits very, very quickly. To give you time to profile, put in a hundred seconds of sleep() at the end of your main() function:

 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​}​
 ​ ​ ​ ​s​l​e​e​p​(​1​0​0​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​

In Xcode, choose ProductProfile in the menu. Instruments will launch. When the list of possible profiling instruments appears, choose Leaks:

Figure 20.2  Picking a profiler

Picking a profiler

As your program runs, you can browse the state of things. You have two instruments to choose from on the lefthand side of the window (Figure 20.3). Clicking on the Allocations instrument will let you see a bar graph of everything that has been allocated in your heap:

Figure 20.3  Allocations instrument

Allocations instrument

You can see, for example, that 10 instances of Asset are still living on your heap.

To look for retain cycles, change to the Leaks instrument and choose the Cycles view from the menu bar above the table. Select a particular cycle to see an object graph of it:

Figure 20.4  Leaks instrument

Leaks instrument
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset