Messaging concept

Ratings:
(4)
Views:151
Banner-Img
  • Share this blog:

Understanding Simple Messaging

Objective-C methods look substantially different from Java methods. Although the syntax is confusing at first, it’s not difficult once you become used to it. Note that you don’t say that you ―call a method‖ when using Objective-C. Instead, you ―send a message to a receiver.‖ For instance, using Java you might type the following: objMyObject.getFooUsingID(33); When describing this line, I write that I am calling objMyObject’s getFoo method and passing the argument 33. In Objective-C, the same message appears as follows: [objMyObject getFooUsingID : 33]; When describing this line, I write that I am sending a getFooUsingID message, passing the argument 33, and objMyObject is the receiver.The difference between calling a method and sending a message isn’t Objective-C’s only difference from Java, C++, and other dot-notation languages. Objective-C uses what’s called infix notation. Infix notation mixes operands and operators. You don’t really need to fully understand infix notation, other than it means Objective-C looks substantially different from Java and C++. An Objective-C message begins with an opening square brace and ends with a closing square brace followed by a semicolon. The object’s name follows the opening brace, followed by a space, followed by the message. Arguments passed to the message follow a colon. You can, of course, have multiple-argument methods, as you will see in the next chapter. For now, though, just consider single-argument methods. Figure 3-4 summarizes an Objective-C message with a single argument.  

Using self in a Message

The term self refers to an object when sending a message, and it is also the receiver. For instance, you might make a mental note to yourself to pick up milk on the way home from work (Listing 3-8). Listing 3-8 A method using the self keyword (void)goHome {[self pickupMilk]; }(Milk*) pickupMilk { } Both methods are in the same object, and so the goHome method sends the message pickupMilk to itself, or self.  

Nested Arguments

Like Java, you can nest Objective-C messages. For instance, using Java, you might write the following: objMyObject.getFoo(objMyFooIdentifier.getID()); In Objective-C, you would write the same statement as follows: [objMyObject getFoo: [objMyFooIdentifier getID]]; Using Java, you might nest an object’s constructor in another method. objTester.testFubar(new Fubar(33)); In Objective-C, you can also nest object constructors in other methods. [objTester testFubar[[Fubar alloc] initWithInteger : 33]]]; In this method, a new Fubar instance is first allocated and then initialized with 33, and the resulting object reference is sent as an argument to the testFubar message.

Class and Instance Methods

As discussed earlier, you declare methods in a class’s interface and define methods in a class’s implementation. Just like C, a method declaration consists solely of the method’s signature, while the definition is the method’s actual implementation. In both files, there are two method types: instance and class methods. Instance methods begin with a minus sign, while class methods begin with a plus sign. A class method is similar to a Java static method, meaning you don’t need to create a class instance to use the method. For instance, the following is an instance method. - (void) sayHello: (NSString*) name Using the method requires creating a class instance first. Although not required, you should also initialize the class. Remember, all classes extend NSObject, which has an init method, so every Objective-C class is guaranteed to implement init. Simple *objSimple = [[Simple alloc] init]; [objSimple sayHello:@"James"]; Now consider class methods. Class methods begin with a plus sign. + (id) sayGoodBye; A class method doesn’t require creating a class instance before using the method. For instance, when first allocating space for an object instance, you call a class’s alloc method. If the class doesn’t implement the alloc method, the runtime traverses up the class’s inheritance hierarchy until it finds an alloc method or it reaches NSObject’s alloc method and calls it. Simple *mySimple = [Simple alloc]; [mySimple init]; This alloc method is a class method example. You don’t instantiate a class instance before calling alloc; rather, you call alloc directly using the class. You create and use class methods just like Java static methods. And like Java static methods, you have the same restrictions. You can’t reference that   class’s instance variables from a static method, as the instance variables haven’t been initialized. You also can’t refer to other instance methods from the same class as the class method. Remember, like a Java static method, you are using an uninitialized class, not an initialized object. If your class method relies upon a class being initialized, runtime errors will result.

Adding sayGoodBye as a Class Method to Simple

  1. Open the last example’s project in Open Simple.h and add the sayGoodBye method declaration to it). Be certain to use a + and not a – in the method’s signature.
  2. Add the method’s definition to m.
  3. Have m call the sayGoodBye method.
  4. Build and run the application, and ―Goodbye...‖ is written to the debugger console.

  Simple.h modified to include sayGoodBye declaration #import <Foundation/Foundation.h> @interface Simple : NSObject { } + (void) sayGoodBye; -(void) sayHello: (NSString *) name; @end Simple.m modified to include sayGoodBye definition #import "Simple.h" @implementation Simple + (void) sayGoodBye { NSLog(@"Goodbye..."); } - (void) sayHello: (NSString *) name { NSMutableString *message = [[NSMutableString alloc] initWithString:@"Hello there"]; [message appendString:name]; NSLog(message); [message release]; } @end The main.h file modified to call sayGoodBye int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Simple * mySimple = [[Simple alloc] init]; [mySimple sayHello:@"James"]; [mySimple release]; [Simple sayGoodBye]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; } Debugger console after running ChapThree [Session started at 2008-12-17 21:33:38 -0500.] 2008-12-17 21:33:40.498 ChapThree[851:20b] Hello there James 2008-12-17 21:33:40.501 ChapThree[851:20b] Goodbye...  

The alloc and init Methods

The alloc method is how you create class instances for all Objective-C classes. This method allocates memory space for the new object instance. It is inherited from the NSObject class, so you don’t really need to implement this method yourself.The init method is how you initialize a class once allocated. Unlike the class method alloc, the init method is an instance method. The init method is also a method in NSObject. If a class has no specific initialization requirements, you don’t need to override int, nor are you required to call it when instantiating a class. However, if you have specific initialization requirements, you should override this method. Good programming practice, though, is to always call init, usually on the same line as the allocation.The init method returns an id. An id is an Objective-C type that is a pointer to the object instance’s address. The id is weakly typed, though, and the runtime treats all ids the same. Overriding an init method should always call the class parent’s init method (Listing 3-13).   Listing 3-13 A simple init implementation - (id) init { if (self = [super init]){ magicNumber = 5;} return self; } In Listing 3-13, the method assigns itself to its parent’s id. If the parent’s init method fails, it returns a nil value and the if statement fails. If it succeeds, the evaluation is true and the instance variable, magicNumber, is set to five. The init method ends by returning itself. You can also initialize an object by passing arguments. By convention, initialization methods that take arguments are named init, followed by the data type of the argument. For instance, you could modify the init method in Listing 3-13 to Listing 3-14 if you wanted to initialize with an integer passed as a parameter. Listing 3-14 A simple init method - (id) initWithInt : (int) value { if (self = [super init]) magicNumber = value; return self; }

Managing Memory Using Retain and Release

Unlike Java or C#, when programming for the iPhone, you manage memory manually; there is no garbage collection on the iPhone. Although as of OS X 10.5, Cocoa includes an option to use automatic garbage collection, this option is not available on the iPhone. Table 3-1 summarizes Objective-C’s memory management methods. Table 3-1 Allocate memory for new object and assign object reference count of one. Add receiver to autorelease pool. Deallocate memory for an object with zero reference count. Decrease object’s reference count by one. Increase object’s reference count by one. Returns the object as an id. See documentation. NSObject Memory Management Related Methods Objective-C uses reference counts to determine if memory should be released or retained. When you create a class instance, the runtime allocates memory for the object and assigns that object a reference count of one. For instance, suppose you had a class named Simple. You first allocate space for it using NSObject’s alloc method. Simple *objSimple = [[Simple alloc] init]; You then use the object. [objSimple sayHello:@"James"];   When finished, you call its release method. If no release method is found, the runtime moves up the classes’ inheritance hierarchy until it finds a release implementation. As all classes extend NSObject, if no release instance is found, the runtime calls NSObject’s release method. [objSimple release]; When an object’s reference count reaches zero, the runtime calls the object’s dealloc method to deallocate the object. As with retain, if the runtime doesn’t find a dealloc method, it moves up the inheritance hierarchy until it finds one. If no dealloc method is found, the runtime calls NSObject’s dealloc method and the object is deallocated so the memory can be reclaimed. You’ve already seen how alloc, release, and dealloc work; you allocate memory for an object and assign it a reference count of one using the alloc method, and you decrement the reference count by one when calling release. When an object’s reference count reaches zero, the program calls NSObject’s dealloc method. The retain method increments an object’s reference by one and returns the object reference as an id. Unlike Java, this referencing isn’t automatic; you must explicitly call retain to increase an object’s reference count. For instance, consider the following Objective-C code (Listing 3-15). Listing 3-15 Using retain Simple *objSimple = [[Simple alloc] init]; Simple *objSimpleTwo = objSimple; NSLog(@"retaincount: %d", [objSimple retainCount]); [objSimple release]; //this causes an error because objSimpleTwo is released [objSimpleTwo sayHello:@"James"];   NOTE Typically, there is no reason to call retainCount. In this chapter, I use retainCount to illustrate Objective-C memory management.The first line allocates objSimple, and the runtime assigns the object a reference count of one. The second statement creates a new pointer to the objSimple object; both objSimple and objSimpleTwo point to the same physical object in memory. But because the code doesn’t call retain, the physical object’s reference count is not incremented. When the object is then released, the reference count is decremented by one and the reference count for the object becomes zero. The object is deallocated, so the next line fails, as objSimpleTwo is pointing to deallocated memory space.Instead, the code should have explicitly retained objSimpleTwo. [objSimpleTwo retain]; Retaining objSimpleTwo would have incremented the object’s reference count by one, bringing it to two. Then, when objSimple was released, the object’s reference count would still be one and the object would not be deallocated. The subsequent call to sayHello would work just fine, as the object that objSimpleTwo pointed to would still exist. Note, this is a somewhat unrealistic example, as you will never write code like Listing 3-15, but it illustrates retain and release.You can override the NSObject’s retain, release, dealloc, and alloc methods. But if you do, be certain to call the object’s super method version. The method call for these methods must make it up the inheritance hierarchy to NSObject for memory management to function correctly.

Using Manual Memory Management

  1. Open the previous Try This project and implement dealloc, retain, release, and alloc in m (Listing 3-16). Note that retain returns an id, and that all these methods are declared in NSObject and don’t require you adding their signatures to Simple.h.
  2. Modify m to write log statements of the Simple’s retaincount (Listing 3-17).
  3. Build and run the The debugger includes the logging added in Listing 3-16 (Listing 3-18).

  Listing 3-16 Simple.m modified to include memory management methods #import "Simple.h" @implementation Simple + (void) sayGoodBye {NSLog(@"Goodbye..."); } - (void) sayHello: (NSString *) name { NSMutableString *message = [[NSMutableString alloc] initWithString:@"Hello there"]; [message appendString:name]; NSLog(message); [message release]; }

  • (void) dealloc { NSLog(@"deallocating ...");
  • [super dealloc];

}

  • (id) retain {

NSLog(@"retaining Simple....."); return [super retain]; }

  • (void) release { NSLog(@"releasing ....");
  • [super release];

} +(id) alloc { NSLog(@"allocating Simple...."); return [super alloc]; } @end Listing 3-17 The main.h file modified to include retainCount logging #import <UIKit/UIKit.h> #import "Simple.h" int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Simple * mySimple = [[Simple alloc] init]; NSLog(@"retaincount: %d", [mySimple retainCount]); [mySimple sayHello:@"James"]; [mySimple release]; [Simple sayGoodBye]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; } Listing 3-18 Debugger console echoing memory management logging [Session started at 2008-12-17 22:30:02 -0500.] 2008-12-17 22:30:03.894 ChapThree[1062:20b] allocating Simple.... 2008-12-17 22:30:03.895 ChapThree[1062:20b] retaincount: 1 2008-12-17 22:30:03.899 ChapThree[1062:20b] Hello there James 2008-12-17 22:30:03.903 ChapThree[1062:20b] releasing Simple..... 2008-12-17 22:30:03.904 ChapThree[1062:20b] deallocating Simple.... 2008-12-17 22:30:03.904 ChapThree[1062:20b] Goodbye... In main.m, the main method first allocates a new Simple instance and assigns the pointer (mySimple) to point to the newly allocated and initialized object. Simple *mySimple = [[Simple alloc] init];   The reference count to the object mySimple points to is one, and the debug statement in Listing 3-17 prints a retain count of one. Instance Variables and Memory In Chapter 4, you will learn about properties. You should use them and their accessor methods. If you do, you avoid this section’s complications. But you should still understand a little about instance variables and how they are handled in memory. Suppose you have an instance variable, personName, you wish to set, as in Listing 3-19. Listing 3-19 An instance variable in Simple.h #import <Foundation/Foundation.h> @interface Simple : NSObject { NSString * personName; } -(void) sayGoodBye; - (void) sayName; -(void) sayHello: (NSString *) name; @end Now suppose you modified sayHello to set personName, as in Listing 3-20. You must retain the variable; otherwise, it goes away at the method’s end. Listing 3-20 Retaining an instance variable - (void) sayHello: (NSString*) name { NSMutableString *message = [[NSMutableString alloc] initWithString: @"Hello there "]; [message appendString:name]; NSLog(message); personName = [name retain]; [message release]; }   Note that by retaining name, you are increasing its reference count by one, returning it, and then setting personName to it. Thus, retaining name is retaining personName. Not retaining a variable when assigning it to another variable, as in Listing 3-20, is a good example of the type of problem you might encounter when not using properties. The name variable pointer is passed to sayHello. Assume there is only one other pointer pointing to name (a retainCount of one). Then, after assigning personName to name, the retain count remains one. The personName pointer is now at the mercy of the pointer that originally pointed to name outside the sayHello method. When the pointer external to Simple releases the object name points to, the object is deallocated. So the personName pointer now points to deallocated memory space and an error occurs. To correct this problem, you call retain on the instance variable as in Listing 3-20. Anytime you set an instance variable, you should retain it. That way, you ensure that it will not reach a zero reference count while the instance variable still points to the object. Of course, the better solution is to always use accessor methods combined with properties. You learn about accessor methods and properties in the next chapter. NOTE You could have written the code in Listing 3-20 using one of NSString’s class methods. But using stringWithString would not illustrate using retain. personName = [NSString stringWithString:name]; Managing Memory Using Autorelease Managing reference counts manually is tiresome and error- prone. NSObject’s autorelease method manages an object’s reference count for you. The autorelease method uses what’s called a release pool to manage an object’s references. Refer to Listing 3-17 and note that this method’s first step is allocating an NSAutoreleasePool. Its second-to-last step is releasing that pool. Calling autorelease adds the object to the pool, and the pool retains the object for you. Consider the sayHelloTom method in Listing 3-21.   Listing 3-21 A method using autorelease - (void) sayHelloTom { Simple *objSimple = [[[Simple alloc] init] autorelease]; [objSimple sayHello:@"Tom"]; } The method allocates a Simple instance and then calls autorelease, assigning objSimple to the autorelease pool. When the method finishes executing, the autorelease pool is deallocated and the Simple instance is subsequently released.   NOTE The iPhone Cocoa Touch framework creates an autorelease pool for every event loop and releases it when the loop completes.Using autorelease and accepting the default autorelease pools makes memory management easy. However, the problem is that there’s a penalty: The objects persist for the release pool’s lifetime. There is one solution, and that is to manage the NSAutoReleasePool yourself. For instance, you could modify Listing 3-22 to manage its own autorelease pool. Listing 3-22 The sayHelloTom method managing its own autorelease pool - (void) sayHelloTom { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; Simple *objSimple = [[[Simple alloc] autorelease] init]; [objSimple sayHello:@"Tom"]; [pool release]; } When the pool is released and deallocated, the pool releases the object pointed to by objSimple. When the physical object pointed to by objSimple is released, the reference count is zero, so the runtime deallocates the object. You should note, though, that in this example, the results are exactly the same as if you had used the default autorelease pool. Unless creating many objects, it’s probably best to stick to the default NSAutoreleasePool rather than trying to manage it yourself. After all, how long does an event loop last? Of course, if you are doing an intensive process, involving many objects or unusually large objects, you might consider managing the autorelease pool manually.  

You liked the article?

Like : 0

Vote for difficulty

Current difficulty (Avg): Medium

Recommended Courses

1/15

About Author
Authorlogo
Name
TekSlate
Author Bio

TekSlate is the best online training provider in delivering world-class IT skills to individuals and corporates from all parts of the globe. We are proven experts in accumulating every need of an IT skills upgrade aspirant and have delivered excellent services. We aim to bring you all the essentials to learn and master new technologies in the market with our articles, blogs, and videos. Build your career success with us, enhancing most in-demand skills in the market.


Stay Updated


Get stories of change makers and innovators from the startup ecosystem in your inbox