The most visible aspect for any mobile 
application, other than its look and feel, is the speed in terms of user
 interface’s responsiveness. This performance may be impacted not only 
by hardware constraints like processing power or available memory but 
also due to network delays while pulling data. Apple’s iPhone provides 
128MB of RAM, part of which is dedicated for user interface only. Even 
though only one user application is running at a time, still there are a
 bunch of applications (like mail, clock, push, music) that are running 
in the background consuming additional memory. Your application must be 
able to run under the presumption of low memory and the fact that an 
incoming phone call or text message will further decrease the available 
memory, potentially resulting in a memory warning to your application. 
The application will get terminated if you don’t release memory when you
 get these memory warnings. Ideally, your application should stay within
 7-8Mb limit to successfully overcome any memory issues. Apple’s development environment comes with tools that let you gauge the memory footprint as you move through your application. 
 
The Objective-C way
Objective C uses 'reference counting' as its main memory management technique (wikipedia.org/wiki/Reference_counting).
 Every object keeps an internal count of how many times it's 'needed'. 
The system makes sure that objects that are needed are not deleted, and 
when an object is not needed it is deleted. This may sound like 
automatic garbage collection (the way it works in Java, wikipedia.org/wiki/Garbage_collection_(computer_science)),
 but it is not. The main difference is that in automatic GC (Java, .net 
etc.), a seperate chunk of code periodically runs in the background to 
see what objects are being referenced and which ones are not needed. It 
then deletes any unused objects automatically with no special handling 
required by the programmer (apart from making sure all references to 
objects are removed when not needed). In the reference counting method, 
the programmer has the responsibility of declaring when he needs the 
object and when he's done with the object, and object deletion takes 
place immediately when the object is no longer used, i.e. reference 
count drops to zero (See the above wikipedia links for more on the 
matter).
Note: Objective C 2.0 also has an option to enable automatic garbage 
collection. However, garbage collection is not an option when developing
 for iPhone so its still important to understand reference counts, 
object ownership etc.
Object Ownership 
It's important to understand the concept of object ownership. In
 Objective C, an object owner is someone (or piece of code) that has 
explicitly said "Right, I need this object, don't delete it". 
This could be the person (or code) that created the object (e.g. Class A
 in the example above). Or it could be another person (or code) that 
received the object and needs it (e.g. Class B in the example above). Thus an object can have more than one owner. The number of owners an object has, is also the reference count.
Object owners have the responsibility of telling the object when they 
are done with it. When they do, they are no longer an owner and the 
object's reference count is decreased by one. When an object has no 
owners left (i.e. no one needs it, reference count is zero), it is 
deleted.
You can still use an object without being its owner (using it 
temporarily), but bear in mind that the object might (and probably will)
 get deleted in the near future. So if you need an object long-term, you
 should take ownership. If you only need the object there and then (e.g.
 within the function that you received it, or within that event loop - 
more on autorelease pools later), then you don't need to take ownership.
How it works
The main messages you can send an object (regarding memory management) are:
alloc (e.g. [NSString alloc]): This allocates an 
instance of the object (in this case NSString). It also sets the 
reference count to 1. You are the only owner of this object. You must 
release the object when you are done with it. (OFFTOPIC: remember to 
call an init function on the newly allocated object: [[NSString alloc] 
init] or [[NSString alloc] initWithFormat:]  etc.)
new (e.g. [NSString new]): This is basically a shorthand way of writing [[NSString alloc] init]. So same rules apply as alloc.
retain (e.g. [aString retain]): You call this when you 
are passed an existing object (memory has already been allocated for it 
elsewhere), and you want to tell the object that you need it as well. 
This is like saying "Ok, I need this object, don't delete it till I'm done.".
 The reference count is increased by one, and you are the new owner of 
this object (along with any other previous owners). You must release the
 object when you are done with it.
release (e.g. [aString release]): You call this when 
you are done using an object. You are no longer the owner of the object 
so the reference count is decreased by one. If reference count is now 
zero (i.e. no owners left), then the object is automatically deleted and
 the memory is freed, otherwise the object stays in memory for other 
owners to use. It is like saying "Right, I'm done with this object, you can delete it if no one else is using it". If you are not the owner of an object, you should not call this. If you are the owner of an object, you must call this when you are done.
autorelease (e.g. [aString autorelease]). This means that you need the object temporarily, and does not make you an owner. It's like saying "Ok, I need this object for now, keep it in memory while I do a few things with it, then you can delete it". More on this later in the 'autorelease pools' section.
copy (e.g. [aString copy]): This creates a copy of the 
object (depending on how the copy message has been implemented for that 
object) and you become an owner of the new object.
So when dealing with Objective C pointers/objects, it's important to 
remember to send the correct messages. The general rule of thumb is: If
 you own an object (allocate, retain or copy it), you release it. If you
 don't own it (came via convenience method or someone else allocated 
it), you don't release it.
Convenience methods
Many classes in Cocoa have whats known as convenience methods. These are
 static methods used for allocating and initializing objects directly. 
You are not the owner of the returned objects and they are deleted 
automatically when the autorelease pool is popped (generally at the end 
of the event loop, but this depends on you or the app).
E.g. the explicit way of allocating and initializing an NSNumber is:
NSNumber *aNumber = [[NSNumber alloc] initWithFloat:5.0f];
This creates a new instance of NSNumber, initializes it with the 'initWithFloat' method, and parameter 5.0f.
aNumber has a reference count of 1. You are the owner of aNumber and you must release it when you are done.
Using a convenience method would be:
NSNumber *aNumber = [NSNumber numberWithFloat:5.0f];
This also creates a new instance of NSNumber, initializes it with the 'numberWithFloat' method, and parameter 5.0f.
It also has a reference count of 1. But you are not the owner of aNumber and should not release it. The
 owner is the NSNumber class and the object will be deleted 
automatically at the end of the current scope - defined by the 
autorelease pool, more on this later - for now its safe to say you 
should not release the object, but keep in mind the object will not hang around for long.
Convenience methods generally have the same name as the relevant init function, but with the init replaced by the type of object. E.g. for NSNumber: initWithFloat -> numberWithFloat, initWithInt -> numberWithInt.
An example with NSString:
NSString *aString1 = [[NSString alloc] initWithFormat:@"Results are %i and %i",
int1, int2];
// explicit allocation, you are the owner, you must release when you are done NSString *aString2 = [NSString stringWithFormat:@"Results are %i and %i", int1,
int2]; 
// convenience method, you are not the owner, the object will be deleted when 
the autorelease pool is popped.
Autorelease Pools
Autorelease pools (supported by NSAutoReleasePool
 class) are an important aspect of Cocoa’s memory management system that
 helps in lowering the overhead of dealing with retain/release semantic.
 An autorelease pool acts like a simple list containing objects that are
 to be released when the pool itself is deallocated. The AppKit (set
 of classes primarily dealing with user interface elements for 
developing applications) usually ensures that you always have an 
autorelease pool. You may create your own autorelease pools (they are 
stackable) specially when your application creates a lot of temporary 
autoreleased objects within a event loop in order to minimize the peak 
memory footprint as shown in the example below,
-(void) someFunctionDeclaration { for(int i=0; i<100; i++) {
// create your oqn autorelease pool NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // these objects get added to the autorelease pool you created above NSNumber *aNumber = [NSNumber numberWithFloat:1]; NSString *aString = [NSNumber stringWithFormat:@"Temporary String Data"]; ... NSNumber *anotherNumber = [NSNumber numberWithFormat:N]; // ... do some processing with objects created above. ... [pool release]; // all objects added to this pool are released } }
When you send an ‘autorelease’ message to an object, that object will
 be added to the most recently created (on the current thread) pool’s 
list of items to release. Once that pool is itself released (or 
drained), it will send a ‘release’ message to all objects that were 
added to that autorelease pool, decrementing their retain count and the 
object may be deallocated if the retain count becomes zero. Thus, 
sending autorelease instead of release to an object extends the lifetime
 of that object at least until the pool itself is drained (it may be 
longer if the object is subsequently retained).
Autorelease pools are specifically useful when you don’t know how 
long you need an object reference, for instance when you return an 
object. In such scenario, you can’t send a release message to the object
 within the method that created it, but not releasing that object will 
lead to a memory leak. By sending the object an ‘autorelease’ message, 
it gets added to the local autorelease pool and may be further retained 
by the calling function, if needed. In general, all objects returned 
from functions (other than alloc/copy) should be considered to be 
autoreleased.
Arrays, Dictionaries and other Containers
Arrays, dictionaries and other Containers generally retain any objects 
added to them. (When dealing with 3rd party collection type objects, 
always check the documentation to see if they retain or not, but by 
convention they usually will). This means that these collections will 
take ownership of the object, and you do not need to retain before 
adding. Similarly, when the object is removed from the container, or the
 container is destroyed, the object is released (reference count is 
dropped). If the object has no other owners, it is destroyed.
E.g.
The following code will create a leak:
-(void) addNumberToArray:(float)aFloat NSNumber *aNumber = [[NSNumber alloc] initWithFloat:aFloat];
// reference count is now 1, you are the owner [anArray addObject: aNumber];
// reference count is now 2, the array is also an owner as well as you. }
You need to release the number after you've added it if you no longer 
need it elsewhere other than the array. The following code is correct:
-(void) addNumberToArray:(float)aFloat { NSNumber *aNumber = [[NSNumber alloc] initWithFloat:aFloat];
// reference count is now 1, you are the owner [anArray addObject: aNumber];
// reference count is now 2, the array is also an owner as well as you. [aNumber release];
// reference count is now 1, you are not the owner anymore }
Now, when the array is released, or the object is removed from the 
array, the reference count is dropped once more as the array delcares 
itself as no longer owner of the object, so the object is deleted. 
Of course another way of doing the above safely is:
-(void) addNumberToArray:(float)aFloat { NSNumber *aNumber = [NSNumber numberWithFloat:aFloat];
// reference count is now 1, NSNumber is the owner, you are not [anArray addObject: aNumber];
// reference count is now 2, the array is also an owner as well as NSNumber. }
Now when the autorelease pool is popped the NSNumber loses ownership and
 reference count drops to 1 (now only the array owns the number). When 
the array is released, or the object is removed from the array the 
reference count drops to zero and the number is deleted.
You may wonder which is a better way of doing this? Method 1 (explicitly
 using alloc and release), or method 2 (the convenience method). I 
generally preferred method 2 on OSX, because it looks simpler and is 
less code. The functionality looks identical - but it is not. It is 
actually better practise to use method 1 (especially when developing for
 iPhone) or use method 2 with your own autorelease pools, more on this 
below.
Autorelease, Convenience vs Explicit
You may be wondering what exactly the difference and/or benefit is of the following two approaches:
Explicit: 
-(void) doStuff:(float)aFloat { NSNumber *aNumber = [[NSNumber alloc] initWithFloat:aFloat];
// refcount is 1, you are owner /// ... do a bunch of stuff with aNumber... ... [aNumber release]; // release aNumber }
Autoreleased: 
-(void) doStuff:(float)aFloat { NSNumber *aNumber = [NSNumber numberWithFloat:aFloat];
// refcount is 1, you are not ownder, will be automatically release /// ... do a bunch of stuff with aNumber... ... }
With the explicit approach, aNumber is released immediately at the end 
of doStuff and the memory is deallocated there and then. With the 
Autoreleased approach, the aNumber is released when the autorelease pool
 is popped, and generally that happens at the end of the event loop. So 
if you create quite a lot of autorelease objects during an event loop, 
they are all going to add up and you may run out of memory. In the above
 example it isn't that clear but let me give another example:
Explicit: 
-(void) doStuff:(float)aFloat { for(int i=0; i<100; i++) { NSNumber *aNumber = [[NSNumber alloc] initWithFloat:aFloat];
// refcount is 1, you are owner /// ... do a bunch of stuff with aNumber... ... [aNumber release]; // release aNumber } }
Autoreleased: 
-(void) doStuff:(float)aFloat { for(int i=0; i<100; i++) { NSNumber *aNumber = [NSNumber numberWithFloat:aFloat];
// refcount is 1, you are not owner, will be automatically released /// ... do a bunch of stuff with aNumber... ... } }
Now you can see, in the first example we never have more than a single 
NSNumber in memory (the NSNumber is allocated at the beginning of, and 
deallocated at the end of each for loop). Whereas in the second example 
with each for loop, a new NSNumber is created while the old one is still
 hanging around in memory waiting for the autorelease pool to be 
released. On desktop systems with a lot of ram, you may have the luxury 
to decide which method you'd like to go for, but on limited memory 
platforms such as iPhone it's pretty important to make sure objects are 
deleted as soon as they become unnecessary and not hang around.
Of course another option is to create your own autorelease pool, which 
would be especially useful if you are using lots of temporary objects 
and can't be bothered to release them all individually. Consider the 
following code:
Explicit: 
-(void) doStuff { for(int i=0; i<100; i++) { NSNumber *aNumber1 = [[NSNumber alloc] initWithFloat:1];
// refcount is 1, you are owner NSNumber *aNumber2 = [[NSNumber alloc] initWithFloat:2];
// refcount is 1, you are owner NSNumber *aNumber3 = [[NSNumber alloc] initWithFloat:3];
// refcount is 1, you are owner NSNumber *aNumber4 = [[NSNumber alloc] initWithFloat:4];
// refcount is 1, you are owner NSNumber *aNumber5 = [[NSNumber alloc] initWithFloat:5];
// refcount is 1, you are owner NSNumber *aNumber6 = [[NSNumber alloc] initWithFloat:6];
// refcount is 1, you are owner // ... do a bunch of stuff with all objects above. ... // release all objects [aNumber1 release]; [aNumber2 release]; [aNumber3 release]; [aNumber4 release]; [aNumber5 release]; [aNumber6 release]; } }
Autoreleased: 
-(void) doStuff { for(int i=0; i<100; i++) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// create your own little autorelease pool // these objects get added to the autorelease pool you created above NSNumber *aNumber1 = [NSNumber numberWithFloat:1];
// refcount is 1, you are not owner, will be automatically released NSNumber *aNumber2 = [NSNumber numberWithFloat:2];
// refcount is 1, you are not owner, will be automatically released NSNumber *aNumber3 = [NSNumber numberWithFloat:3];
// refcount is 1, you are not owner, will be automatically released NSNumber *aNumber4 = [NSNumber numberWithFloat:4];
// refcount is 1, you are not owner, will be automatically released NSNumber *aNumber5 = [NSNumber numberWithFloat:5];
// refcount is 1, you are not owner, will be automatically released NSNumber *aNumber6 = [NSNumber numberWithFloat:6];
// refcount is 1, you are not owner, will be automatically released // ... do a bunch of stuff with all objects above. ... [pool release];
// all objects added to this pool (the ones above) are released } }
In this case, both chunks of code essentially behave the same. In the 
first example 6 NSNumbers are created at the beginning of every for 
loop, and they are explicitly released at the end of each for loop (you 
own them). There is never more than 6 NSNumbers in memory.
In the second example you don't own any of the NSNumbers, but by 
creating your own autorelease pool, you control their lifespan. Because 
you create and release an autorelease pool in the loop, the NSNumbers 
only live the duration of the for loop, so you never have more than 6 
NSNumbers in memory. Had you not created the autorelease pool, at the 
end of every for loop you'd have 6 NSNumbers waiting to be deleted, and 
by the end of the function there'd be 6x100=600 NSNumbers hanging around
 in memory. Combine that with other autorelease objects allocated in 
other functions and you can have an awful lot of unused objects which 
are going to be released soon (so no memory leak), but potentially you may hit your memory limits if you don't release as you go.
Handling Memory Warnings
Given low available working memory amid background applications and 
unpredictable user behavior, it is ofter better to sacrifice the snappy 
user interface for a low memory footprint and avoid any application 
crashes. It is recommended to delay loading all resources until they are
 needed. If one NIB contains a lot of views, it is better to create multiple NIBs files so as to load the NIB individually when needed.
Before an out-of-memory crash, a ‘didReceiveMemoryWarning’ message is
 sent to your view controllers. The purpose of ‘didReceiveMemoryWarning’
 is to give you a chance to free memory or pop views to avoid a crash. 
It is highly recommended that you override this message in all your view
 controllers and handle the warning properly and free up memory, then 
you can avoid an application crash. Also, when ‘didReceiveMemoryWarning’
 message is called, the setView message will also be called with a nil 
parameter. It will be called to release the view if the view is not the 
active view. This is where you will want to release as much resources as
 you can, such as all your IBOutlet variables. Also, do not forget to 
call the corresponding parent methods or your application will crash.
Static Code Analyzer
In addition to code reviews and careful coding 
practices, a static code analyzer can save many a headaches and help in 
discovering potential vulnerabilities. We used ‘clang’ for
 our project that helped identifying some not so serious and also a few 
potentially serious bugs based on the memory footprint. We found this 
tool to be easy to setup with configurations that allow it to be run 
locally on developer’s machine or along with the build to generate 
reports with filenames and line numbers for easy discovery and 
correction.
Apple’s IDE - XCode
Apple’s IDE tool XCode
 is tightly integrated with Cocoa framework and includes powerful 
optimization and analysis tools called Instruments. Instruments collects
 data such as disk, memory, or CPU usage in real time on your connected 
iPhone that can help you track down performance bottlenecks in your 
applications. You can dynamically view the leaks as well as memory 
object allocation along with exact location in your code.
General Tips
- NSObject provides a method called retainCount that you may use to 
get an object’s current retain count. This can be very handy for 
debugging purposes as, NSLog([NSString stringWithFormat:@"Retain 
Count:%i", [yourObject retainCount]]);
- You should override the ‘dealloc’ method in your classes to release
 any memory that you may have allocated (make sure to super’s dealloc)
- In setters methods, always retain the new object first, and then 
release the old object. This will take care of scenarios when same value
 is being set.
- When you add an object to a collection such as an array, 
dictionary, or set, the collection takes ownership of it. The collection
 will relinquish ownership when the object is removed from the 
collection or when the collection is itself released.
- When your view is freed while handling ‘didReceiveMemoryWarning’, 
the NIB will be read back into memory when it is needed the next time. 
This will cause ‘viewDidLoad’ message to be called again. So make sure 
you have the appropriate initialization code in that message.
- XCode’s Instruments allows you to manually trigger a memory warning
 that can be very helpful in flushing out memory warning issues.
- If you are making Cocoa calls outside of the Application Kit’s main
 thread—for example if you create a Foundation-only application or if 
you detach a thread—you need to create your own autorelease pool.
- You should always drain an autorelease pool in the same context 
(invocation of a method or function, or body of a loop) that it was 
created.
Summary
Owning an object means explicitly declaring that you need it (by using alloc, new, retain or copy).
If you own an object, you need to explicitly declare that you are done using it (by using release).
Do not release an object if you are not
 the owner (i.e. you used autorelease on it, or it came via a 
convenience method, or it was simply passed in as a parameter or return 
value from a function).
Only retain an object if you will be needing it in the long-term. If you
 only need it there and then for the function you are in, you can safely
 use it without owning it.
Arrays, dictionaries etc. will take ownership of objects added to them (they call retain), so you do not
 need to explicitly retain when adding. If you own the object before 
adding to a collection, and you no longer need it outside of the 
collection, you must release it after adding.
If you are going to be using lots of temporary objects (autoreleased / 
from convenience methods) you may want to think about creating your own 
short-term autorelease pools to avoid temporary memory peaks. If you hit
 the memory limits on an iPhone, the app will just quit - it doesn't 
matter if the objects taking up memory are no longer needed and are 
waiting to be released in an autorelease pool, it's upto you to make 
sure you allocate and deallocate efficiently and in a timely fashion.
For more information make sure you read the official memory management docs, lots of important details there.
 
কোন মন্তব্য নেই:
একটি মন্তব্য পোস্ট করুন