Although iOS 5/Mac OS X 10.7 starts to import arc, xcode4.2 can be used. ARC is an automatic reference counting function that provides automatic memory management for objective-C Programs during compilation. Arc allows you to focus on the code, object graphs, and the relationships between objects in your application, so that you no longer have to focus on retain and release operations.
However, in some old projects, you still need to use manual memory management to prevent memory leaks. It is also good for you to learn manual management. I was planning to write an article by myself. I accidentally found that there was already an article clearly written to help beginners learn about memory.
Preface
When I was a beginner at objectice-C, I always felt confused about the memory management mechanism of objective-C. The program often experienced memory leaks or inexplicable crashes. I have summarized my research achievements and experience on the objective-C memory management mechanism, and wrote such a simple tutorial. I hope it will be helpful to you and you are welcome to discuss it together.
The memory management involved in this article is for Classes inherited from nsobject.
Basic Principles
Objective-C's memory management mechanism is different from the fully automatic garbage collection mechanism of. Net/Java. It is essentially a manual management method in C language, but it only adds some automatic methods.
1. The objective-C object is generated on the heap. After being generated, a pointer is required to point to it.
Classa * obj1 = [[classa alloc] init];
2. The objective-C object will not be automatically destroyed after use. You need to execute dealloc to release space (destroy); otherwise, the memory will be leaked.
[Obj1 dealloc];
This brings about a problem. In the following code, does obj2 need to call dealloc?
Classa * obj1 = [[classa alloc] init];
Classa * obj2 = obj1;
[Obj1 Hello]; // output hello
[Obj1 dealloc];
[Obj2 Hello]; // can this row and the next row be executed?
[Obj2 dealloc];
No, because obj1 and obj2 are just pointers. They point to the same object. [obj1 dealloc] has destroyed this object and cannot call [obj2 Hello] or [obj2 dealloc]. Obj2 is actually an invalid pointer.
How to Avoid invalid pointers? See the next one.
3 objective-C uses the reference count (ref count or retain count ). The number of times the object is referenced. For example, if an object is pointed to (referenced) by two pointers, its retain count is 2. To destroy an object, call release instead of dealloc. Release will reduce retain count by 1. Only when retain count is equal to 0 will the system call dealloc to actually destroy this object.
Classa * obj1 = [[classa alloc] init]; // when an object is generated, retain COUNT = 1
[Obj1 release]; // release: reduce retain count by 1, retain COUNT = 0, dealloc is automatically called, and the object is destroyed
Let's look back at the problem with the Invalid Pointer and change dealloc to release?
Classa * obj1 = [[classa alloc] init]; // retain COUNT = 1
Classa * obj2 = obj1; // retain COUNT = 1
[Obj1 Hello]; // output hello
[Obj1 release]; // retain COUNT = 0, the object is destroyed
[Obj2 Hello];
[Obj2 release];
After [obj1 release], obj2 is still an invalid pointer. The problem persists. For the solution, see the next one.
4 when the objective-C pointer is assigned a value, retain count will not be automatically added and manual retain is required.
Classa * obj1 = [[classa alloc] init]; // retain COUNT = 1
Classa * obj2 = obj1; // retain COUNT = 1
[Obj2 retain]; // retain COUNT = 2
[Obj1 Hello]; // output hello
[Obj1 release]; // retain COUNT = 2-1 = 1
[Obj2 Hello]; // output hello
[Obj2 release]; // retain COUNT = 0, object destroyed
Solve the problem! NOTE: If [obj2 release] is not called, The retain count of this object is always 1 and will not be destroyed, causing memory leakage. (1-4 can refer to the example program memman-no-pool.m in the attachment)
It does not leak memory, but it seems a little troublesome. Is there a simple way? See the next one.
5. The autorelease pool (automatically released Object pool) is introduced in objective-C. objects can be automatically released when some rules are followed. (Autorelease pool is still not the fully automated garbage collection mechanism of. Net/Java)
5.1 for newly generated objects, you only need to call autorelease. You do not need to call release any more!
Classa * obj1 = [[classa alloc] init] autorelease]; // retain COUNT = 1 but no need to call release
5.2 For pointer assignment, the code is similar to the previous one.
Classa * obj1 = [[classa alloc] init] autorelease]; // retain COUNT = 1
Classa * obj2 = obj1; // retain COUNT = 1
[Obj2 retain]; // retain COUNT = 2
[Obj1 Hello]; // output hello
// For obj1, you do not need to call (actually cannot call) Release
[Obj2 Hello]; // output hello
[Obj2 release]; // retain COUNT = 2-1 = 1
Careful readers will surely find that this object has not been destroyed. When will it be destroyed? Who will destroy it? (Refer to the example program memman-with-pool.m in the attachment) see the next one.
6 autorelease pool principle analysis. (In fact, it is very simple. You must stick to it. Otherwise, you still cannot understand the objective-C memory management mechanism .)
6.1 autorelease pool is not born and needs to be created manually. Only when you create an iPhone project, xcode will automatically help you write it. The real name of autoreleasepool is NSAID utoreleasepool.
NSAID utoreleasepool * Pool = [[NSAID utoreleasepool alloc] init];
6.2 The nsmutablearray array is contained in the nsmutoreleasepool to save all objects declared as autorelease. If an object is declared as autorelease, the system will add this object to this array.
Classa * obj1 = [[classa alloc] init] autorelease]; // retain COUNT = 1, add this object to autorelease pool
6.3 when the NSAID utoreleasepool itself is destroyed, it traverses this array and each member in the release array. If the retain count of the members in the array is 1 at this time, after release, the retain count is 0, and the object is destroyed. If the retain count of the members in the array is greater than 1 at this time, the retain count is greater than 0 after release, and the object is still not destroyed, causing memory leakage.
6.4 by default, there is only one autorelease pool, which is usually similar to the following example.
Int main (INT argc, const char * argv [])
{
NSAID utoreleasepool * pool;
Pool = [[NSAID utoreleasepool alloc] init];
// Do something
[Pool release];
Return (0 );
} // Main
All objects marked as autorelease are destroyed only when the pool is destroyed. If you have a large number of objects marked as autorelease, this obviously cannot make good use of the memory, it is very easy to cause insufficient memory in iPhone memory-constrained programs. For example:
Int main (INT argc, const char * argv [])
{
NSAID utoreleasepool * Pool = [[NSAID utoreleasepool alloc] init];
Int I, J;
For (I = 0; I <100; I ++)
{
For (j = 0; j <100000; j ++)
[Nsstring stringwithformat: @ "1234567890"]; // The generated object is autorelease.
}
[Pool release];
Return (0 );
} // Main
(You can refer to the example program memman-many-objs-one-pool.m in the attachment, through the monitoring tool at runtime you can find that there is a sharp increase in the use of the internal, until the pool is released upon destruction) You need to consider the next one.
7. Multiple autorelease pools can be nested in the objective-C program. When you need to create a large number of local variables, you can create an embedded autorelease pool to release the memory in time. (Thanks to hhyytt and neogui, in some cases, the system will automatically create the autorelease pool. See chapter 4)
Int main (INT argc, const char * argv [])
{
NSAID utoreleasepool * Pool = [[NSAID utoreleasepool alloc] init];
Int I, J;
For (I = 0; I <100; I ++)
{
NSAID utoreleasepool * looppool = [[NSAID utoreleasepool alloc] init];
For (j = 0; j <100000; j ++)
[Nsstring stringwithformat: @ "1234567890"]; // The generated object is autorelease.
[Looppool release];
}
[Pool release];
Return (0 );
} // Main
(You can refer to the example program memman-many-objs-many-pools.m in the attachment for minimal memory usage changes)
Tips and Paradigm
1 tip.
1.1 who created and who released (similar to "Who polluted and who managed "). If you create an object through alloc, new, or copy, you must call release or autorelease. In other words, if it is not created by you, you do not need to release it.
For example, if alloc generates an object in a function and the object is only used in this function, you must call release or autorelease in this function. If you alloc a member object in a class method and do not call autorelease, you need to call release in the dealloc method of this class; If autorelease is called, you do not need to do anything in the dealloc method.
1.2 all objects created by methods except alloc, new, or copy are declared as autorelease.
1.3 who retain and who release. If you call retain, you must call release no matter how the object is generated. Sometimes there is no retain in your code, but the system will add it to the default implementation. I don't know why Apple's documents didn't emphasize this very important point. Please refer to Chapter 2.7 and chapter 3 of paradigm.
2 paradigm.
The paradigm is the template. Because different people have different understandings and habits, the paradigm I have summarized may not be suitable for everyone, but I can ensure that there will be no problems in doing so.
2.1 create an object.
Classa * obj1 = [[classa alloc] init];
2.2 create an autorelease object.
Classa * obj1 = [[classa alloc] init] autorelease];
2.3 release an object and immediately clear the pointer. (By The Way, release is legal as a null pointer, but nothing will happen)
[Obj1 release];
Obj1 = nil;
2.4 assign a pointer to another pointer.
Classa * obj2 = obj1;
[Obj2 retain];
// Do something
[Obj2 release];
Obj2 = nil;
2.5 create and return an object in a function. You need to set this object to autorelease
Classa * func1 ()
{
Classa * OBJ = [[classa alloc] init] autorelease];
Return OBJ;
}
2.6 call the dealloc method of the base class in the dealloc method of the subclass
-(Void) dealloc
{
...
[Super dealloc];
}
2.7 create and use property in a class.
2.7.1 declare a member variable.
Classb * objb;
2.7.2 declare property and add the retain parameter.
@ Property (retain) classb * objb;
2.7.3 define property. (For the default implementation of property, see Chapter 3)
@ Synthesize objb;
2.7.4 apart from the dealloc method, property is always called using the. Operator.
Self. objb or obja. objb
2.7.5 release the member variable in the dealloc method.
[Objb release];
The sample code is as follows (For details, refer to the memman-property.m in the attachment, and you need to pay special attention to when the object is destroyed .) :
@ Interface classa: nsobject
{
Classb * objb;
}
@ Property (retain) classb * objb;
@ End
@ Implementation classa
@ Synthesize objb;
-(Void) dealloc
{
[Objb release];
[Super dealloc];
}
@ End
2.7.6 when assigning values to this property, there are two methods: manual release and autorelease.
Void funcnoautorelease ()
{
Classb * objb1 = [[classb alloc] init];
Classa * obja = [[classa alloc] init];
Obja. objb = objb1;
[Objb1 release];
[Obja release];
}
Void funcautorelease ()
{
Classb * objb1 = [[classb alloc] init] autorelease];
Classa * obja = [[classa alloc] init] autorelease];
Obja. objb = objb1;
}
3. Default Implementation of @ property (retain) and @ synthesize
Here we will explain what happened to @ property (retain) classb * objb; and @ synthesize objb; (default Implementation of retain property ). Property is actually getter and setter, for the property with the retain parameter, the implementation behind it is as follows (see the memman-getter-setter.m in the attachment, you will find that the result is the same as the memman-property.m ):
@ Interface classa: nsobject
{
Classb * objb;
}
-(Classb *) getobjb;
-(Void) setobjb :( classb *) value;
@ End
@ Implementation classa
-(Classb *) getobjb
{
Return objb;
}
-(Void) setobjb :( classb *) Value
{
If (objb! = Value)
{
[Objb release];
Objb = [value retain];
}
}
In setobjb, if the new value is different from the original value, the original value object must be release once so that it is correct to ensure ***/span> retain count.
Because we retain the class once (though implemented by default), we need to release the member variable in the dealloc method.
-(Void) dealloc
{
[Objb release];
[Super dealloc];
}
Four systems automatically create a new autorelease pool
When a new run loop is generated, the system will automatically create a new autorelease pool (Thank you very much for the reminder from the netizens hhyytt and neogui ). Note: Unlike the autorelease pool added to the Code automatically generated when xcode creates a project, the code generated by xcode can be deleted, however, the new autorelease pool automatically created by the system cannot be deleted (for environments without garbage collection ). Objective-C does not provide the implementation code, and the official documentation does not explain it, but we can prove it through small programs.
In this small program, we become an autorelease pool, then generate an autorelease classa instance, and then generate an autorelease classb object in a new run loop (note, we did not manually generate autorelease pool in the new run loop ). The streamlined sample code is as follows. For detailed code, see the memman-run-loop-with-pool.m in the attachment.
Int main (INT argc, char ** argv)
{
Nslog (@ "Create an autorelasepool \ n ");
NSAID utoreleasepool * Pool = [[NSAID utoreleasepool alloc] init];
Nslog (@ "Create an instance of classa and autorelease \ n ");
Classa * obj1 = [[classa alloc] init] autorelease];
Nsdate * Now = [[nsdate alloc] init];
Nstimer * timer = [[nstimer alloc] initwithfiredate: Now
Interval: 0.0
Target: obj1
Selector: @ selector (createclassb)
Userinfo: Nil
Repeats: No];
Nsunloop * runloop = [nsunloop currentrunloop];
[Runloop addtimer: timer formode: nsdefaultrunloopmode];
[Timer release];
[Now release];
[Runloop run]; // call a function in the new loop to generate the autorelease instance of classb.
Nslog (@ "releasing autorelasepool \ n ");
[Pool release];
Nslog (@ "autorelasepool is released \ n ");
Return 0;
}
The output is as follows:
Create an autorelasepool
Create an instance of classa and autorelease
Create an instance of classb and autorelease
Classb destroyed
Releasing autorelasepool
Classa destroyed
Autorelasepool is released
Note that before we destroy the autorelease pool, the autorelease instance of classb has been destroyed.
Some may say that this does not mean that the new run loop will automatically generate a new autorelease pool. Maybe it will only use the old autorelease pool, but it will only be drain once. We can not generate autorelease pool in the main function. The streamlined sample code is as follows. For detailed code, see the memman-run-loop-without-pool.m in the attachment.
Int main (INT argc, char ** argv)
{
Nslog (@ "No autorelasepool created \ n ");
Nslog (@ "Create an instance of classa \ n ");
Classa * obj1 = [[classa alloc] init];
Nsdate * Now = [[nsdate alloc] init];
Nstimer * timer = [[nstimer alloc] initwithfiredate: Now
Interval: 0.0
Target: obj1
Selector: @ selector (createclassb)
Userinfo: Nil
Repeats: No];
Nsunloop * runloop = [nsunloop currentrunloop];
[Runloop addtimer: timer formode: nsdefaultrunloopmode];
[Timer release];
[Now release];
[Runloop run]; // call a function in the new loop to generate the autorelease instance of classb.
Nslog (@ "manually release the instance of classa \ n ");
[Obj1 release];
Return 0;
}
The output is as follows:
No autorelasepool created
Create an instance of classa
Create an instance of classb and autorelease
Classb destroyed
Manually release the instance of classa
Classa destroyed
We can see that we have not created any autorelease pool, but the classb instance is still automatically destroyed. This indicates that the new run loop automatically creates an autorelease pool, this pool will destroy itself when the new run loop ends (and automatically release the objects contained ).
Additional instructions
When studying retain count, I do not recommend using nsstring. Because in the following statement,
Nsstring * str1 = @ "constant string ";
The retain count of str1 is a large number. Objective-C performs special processing on constant strings.
Of course, if you create an nsstring like this, the retain count is still 1.
Nsstring * str2 = [nsstring stringwithformat: @ "123"];
Source code download: http://files.cnblogs.com/VinceYuan/objective-c-memman.zip
Address: http://vinceyuan.cnblogs.com