Friday, December 19, 2008

iPhone Application Development: Memory Management - Part 2

Re read the earlier post about Memory Management. Lots of new stuff and new understanding has been added to the earlier post. It should really be an article in itself.

iPhone Application Development: Collections

Apple provides three basic collection classes: NSArray, NSSet, and NSDictionary. All three of these classes are non-mutable. i.e. once created, they cannot be changed. Their mutable equivalents are NSMutableArray, NSMutableSet, and NSMutableDictionary. All collections store objects only - primitive types can be wrapped in NSNumber and stored in collections.

NSArray is used to store lists of objects. An Ordered Collection. Primitive types can be wrapped in NSNumber. Common operations are supported: count, objectAtIndex, containsObject, etc. NSMutableArray contains additional operations: addObject, insertObject... atIndex, removeObject, removeObjectAtIndex, etc.

NSSet is a set of Objects. An Unordered Collection. An object might appear a maximum of one time. Operations supported are similar to NSArray.

NSDictionary: are equivalent to Map in Java. They store key, value pairs. Both keys and values are objects. Common supported operations: count, objectForKey, allKeys. NSMutableDictionary also supports editing operations such as setObject... forKey, removeObjectForKey.

NSDictionary supports saving to (writeToFile) and loading from (initWithContentsOfFile) files. These are usually called property lists or plists. The file created would be an XML file. Similarly, initWithContentsOfURL, writeToUrl are supported for URL operations.

Memory Management Reminder: Every collection retains an object when its added, and releases it when it is removed. Releasing a collection object releases all the objects stored in it as well.

iPhone Application Development: Using gdb

gdb is a command line debugger. In addition to XCode providing a graphical debugger, Apple includes the gdb debugger with XCode. Setting a breakpoint is as easy as double clicking in the left margin of XCode. This is similar to Eclipse. For those new to gdb, here are a few basic gdb commands to get you started.


print-object anObj
print-object [anObj description]
print-object [nsStr stringByAppendingString:@" yes"]
print (int) [@"John Galt" length]
print (char *) [nsStr UTF8String]

other commands:
call [exp]: Calls the function
set [var] = [exp]
whatis [variable]: print details about variable
help

iPhone Application Development: Objective-C / Cocoa: String Operations

Apple has provided a very nice implementation of the String class. Called NSString. Any string in Objective-C, written as @"Status" is represented in the code as an instance of NSString. Keep in mind that NSString objects are non-mutable. Just like Java String objects.

Go through NSString.h for many many more methods

NSString *nsStr = @"iPhone Development";
int len = [nsStr length]; //
Length of a string
NSString *result = [nsStr stringByAppendingString:anotherNsStr]; Combine 2 strings
NSRange range = [nsStr rangeOfString:@"iPhone"]; // range (not a pointer) would contain location and length of found string
NSString *str = [@"iPhone Application Development" substringFromIndex:7]; // -> Application Development
NSString *str = [@"iPhone Application Development" substringToIndex:6]; // -> iPhone
NSString *str = [@"iPhone Application Development" substringWithRange:NSMakeRange(7, 11)]; // -> Application

NSArray *arrayOfStrings = [@"iPhone Application Development" componentsSeparatedByString:@" "]; // split on space

// Converting between C Strings and NSStrings
NSString *nsStr = [[NSString alloc] initWithUTF8String:"iPhone"];
(const char *)cStr = [nsStr UTF8String];


Mutable Strings: If you need to make modifcations to NSString, use NSMutableString. Some of the valid messages to NSMutableString:


[msMuStr appendString:@" needs an iPhone"];
[msMuStr deleteCharactersInRange:NSMakeRange(7, 11)];
[msMuStr insertString:@" is a good application " atIndex:10];
[msMuStr replaceCharactersInRange:NSMakeRange(7, 11) withString:@" is a good application "];

Monday, December 15, 2008

iPhone Application Development: Memory Management with Objective-C / Cocoa

This discussion touches the basics of memory management within Objective-C / Cocoa. Hopefully, you have already ready my post about differences between C++ / Java and Objective-C.

Cocoa library (and thus Cocoa Touch for iPhone Development) defines NSObject, which is at the root of the entire class hierarchy of the Foundation framework. Every other framework, depends on the Foundation Framework. NSObject implements a semi-automated / reference counting version of garbage collection. Memory manager might be a more appropriate name.

Here is some sample code that helps illustrate how to use NSObject's garbage collection mechanism:

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSObject *obj = [[NSObject alloc] init];
NSLog(@"Created object %@", obj);
[obj release];

[pool release];
return 0;
}
NSObject provides the following messages to manage objects via the memory-manager:
  • alloc: Create a new object. Allocates physical memory for the object. This message to be passed to the class. Alloc "retains" the object. i.e. Objects created by alloc have a reference count of 1. Be sure to release it when you are done.
  • init: initializes the object. It is considered best practice to always init the object by sending it the init message. Typical object creation looks like MyClass *obj = [[MyClass alloc] init]; - calls both alloc and init.
    • If you override init, it must always return itself (id). Sample code: if (self = [super init]) { /* my inits */ } return self;
  • dealloc: Free the memory obtained by alloc. Avoid calling dealloc. Always call release - let the memory manager perform dealloc, when appropriate.
  • retain: Signal to the garbage collector that we are intrested in this object. This increases the reference counter for this object by one.
  • release: Signal to the garbage collector that we are no longer interested in this object. This decreases the reference counter by one. When the reference counter reached 0, the object is deallocated. As a general rule, always "release" pointers holding memory. This will let the memory-manager do its job.
NOTE: If you do decide to over-ride any of these methods, be sure to call super's corresponding method at the appropriate time.

Creating an NSAutoReleasePool creates and inits the memory-management system. At the end of the program, the release message to the auto-release pool signals that we are no longer interested in it. The system then disposes off the auto-release pool, and sends release message to all the objects it contains. Any object that gets to a reference count of 0, is deallocated.

General Tips / Rules of thumb related to memory management
  • Classes should override dealloc, if they want to be notified of when the object is put out of its misery. This is an appropriate place for the object to release any memory that it may have allocated. Be sure to call parent's dealloc when you are done.
  • In setters, always retain the new object first, then release the old object. This will take care of cases when the same value is being set over and over again.
  • Maintain atleast one Auto release pool per thread
  • If you have to create an object and return it from a method. Make sure you pass the autorelease method to it. This will add the object to the autorelease pool.
  • This leads to: don't release objects that you receive from other messages. Assume that they have been added to the auto release pool by the creator. release them *only* if you retain them for some reason.
  • Write memory balanced code: total count of alloc, copy, mutableCopy should be equal to the total count of release, autorelease
  • Every collection retains an object when its added, and releases it when it is removed. Releasing a collection object releases all the objects stored in it as well.
  • NSString objects created using @"..." should be considered as constants. retain or release messages have no effect on them.

Objective-C for C++ / Java developers

Here I document the differences between Objective-C and another object oriented language like C++ / Java. The goal is to get someone, who has a fair grasp of object oriented concepts in either C++ or Java or (preferably) both, started with understanding / writing Objective-C code as soon as possible. The goal being effective iPhone Development. If you are reading this without any background, please read my previous post about objective-c "hello world" program structure, and creating a first basic class in objective-c.

This blog entry is a work in progress. I will keep updating this as I learn more about objective-c.

Differences between Objective C and C++ / Java.



















































ConceptC++ / JavaObjective-CDetails
File name extension.h / .c / .cpp / .java.h / .mDeclarations in .h file. Implementation in .m
Declaring a method / messageint add(int x, int y)- (int) add:(int)x with:(int)ytypes are always enclosed in brackets
Calling the method / sending a messageint result = obj.add(10, 12);int result = [obj add:10 with:12];message name and first parameter are same
Nesting method callsint r = obj.add(obj.getX(), obj.getY);int r = [obj add:[obj getX] with:[obj getY]];
FormattingAll printf formats (%s, %d etc)All C formats, and %@%@ prints and object's "description"
Calling methods in parent classsuper.init();[super init];
Refering to myself / current objectthis.add(10, 12);[self add:12 with:12];



Other things
  • Type (id) represents current class. No need to specify name of the class
  • NULL: the keyword "nil" represents a null value.
  • Message passing is nil safe. nil elements do not core dump / throw a NullPointerException. Data members are auto-initialized to nil
  • All method / message binding in Objective-C is dynamic. Classes maintain a list of messages that will be accepted at runtime. All other messages are passed on to parent. This processing happens at run time.
  • For Class level messages, replace the "-" with "+" as the first character of the message declaration and implementation

Objective-C: creating a basic class

Objective-C is an object oriented implementation added to C. Apart from objects, everything is C works in Objective-C. Here, I try to document what a class looks like, and what it all means. Bear in mind that I am just starting to learn objective-c myself, so, wouldn't recommend anyone using this class in a production environment :)

Objective-C separates a class into its declaration and implementation. It is not enforced, but is a best practice to put the declaration in a header (.h) file, and implementation in a .m file. See notes embedded in the source code below for details.

Geek.h (Class declaration)

#import <Foundation/Foundation.h>

// An Objective-C class is declared using the interface tag
// The parent of this class is declared after the ":"
// Best Practice tip: Always inherit from NSObject, when you don't have anything else to inherit from.

@interface Geek : NSObject{
// All data members go inside the braces
NSString *name;
int level;
}

/*
* All supported messages (functions) are declared before the @end tag
* Best-Practice tip: All messages that initialize the class should begin with "initWith".
* This provides the reader with good information.
* a.k.a. declared initializers
*
* You don't really have to declare messages that this class will override.
* It would be good idea to do so, in the interest of readability
*
* All data types are enclosed in brackets.
* id is a special type that means "myself". Any method that declares (id) as return type, must return "self"
*
* Methods don't really have a name. If you want, you could consider the first
* parameter as the name itself.
*/
- (id) initWithName:(NSString *)name level:(int)level;

// As a general rule of thumb, getters usually don't begin with "get"
- (NSString *)name;
- (int)level;
- (void) setName:(NSString *)name;
- (void) setLevel:(int)level;

@end // Marks the end of the interface tag


Geek.m (class definition)

#import "Geek.h"

// implementation of this class starts from here
@implementation Geek

// message definitions follow C rules
// overridden message (not declared, but defined)
- (id) init {
if (self = [super init]) {
level = 10; // set default level
}
}

- (id) initWithName:(NSString *)newName level:(int)newLevel {
[self init];
[self setName:newName];
[self setLevel:newLevel];

return self;
}

- (NSString *)name {
return name;
}

- (int)level {
return level;
}

- (void)setName:(NSString*)newName {
// See blog entry on memory management for retain / release notes.
[newName retain];
[name release];
name = newName;
}

- (void) setLevel:(int)newLevel {
level = newLevel;
}

// This is equivilant to Java's toString method.
- (NSString *)description {
NSString *desc = [NSString stringWithFormat:@"Name=%s level=%d", [name UTF8String], level];
return desc;
}

- (void)dealloc {
[name release];
[super dealloc];
}

@end // marks end of implementation of the class


Using the class above. Write a main.m file:


#import "Geek.h"

int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

Geek *a = [[Geek alloc] init];
[a setName:@"Jack"];
NSLog(@"Hello. Geek: %@", a);
[a dealloc];

a = [[Geek alloc] initWithName:@"Jill" level:15];
NSLog(@"Hello. Geek: %@", a);
[a dealloc];

[pool release];
return 0;
}


Compiling and running the program:

$ gcc -framework Foundation Geek.m main.m
$ ./a.out
2008-12-15 19:24:47.043 a.out[7052:10b] Hello. Geek: Name=Jack level=10
2008-12-15 19:24:47.045 a.out[7052:10b] Hello. Geek: Name=Jill level=15


There you have it, a pretty useless class, but illustrates basic objective-c usage. Should be useful is getting started with iPhone development.