In the last chapter, you learned about Objective-C classes, simple message syntax, and managing memory. In this chapter, you learn about properties, multiple-argument messages, dynamic binding, polymorphism, the id type, and inheritance. You also learn about categories and protocols. And finally, you learn about Objective-C exception handling. Properties In the last chapter, you had to manage memory when setting an object’s instance variable. For instance, if using retain and release, you would write a setter method that explicitly retained the passed value (Listings 4-1 and 4-2). Listing 4-1 Writing a method that sets an instance variable (interface) @interface MyClass : NSObject { Simple * objInstanceSimple; } -(void) setObjInstanceSimple: (Simple*) newValue; @end Listing 4-2 Writing a method that sets an instance variable (implementation) @implementation MyClass - (void) setObjInstanceSimple: (Simple*) newValue { [newValue retain]; [objInstanceSimple release]; objInstanceSimple = newValue; } @end Remember, when using Objective-C objects, you are simply manipulating pointers. Pointers point to memory space. When changing an instance variable whose type is inherited from NSObject, you are changing the memory space it points to. Changing the memory space a variable points to without using retain or release almost always results in errors. In Listing 4-2, you explicitly set MyClass’s instance variable, objInstanceSimple. The method first retains newValue. The method does this to prevent newValue from being deallocated, should the object later release the newValue. The method then releases objInstanceSimple. It does this to prevent a memory leak when changing objInstanceSimple to point to the memory space pointed to by newValue. After releasing objInstanceSimple, it changes objInstanceSimple to point to newValue.Managing memory when getting and setting an object’s instance variables is a pain. Objective-C 2.0 makes instance variables easier by using properties. Properties are shortcuts for creating instance variable accessors. You create properties using compiler directives. The @property directive declares a property, @synthesize tells the compiler to generate accessors, and @dynamic tells the compiler you will provide the accessor methods. A property directive also has one or more attributes. Table 4-1 summarizes the most common attributes.The readonly attribute indicates the property is read-only, and the synthesize directive only creates a getter for the property. Retain instructs the compiler to create the setter so it retains the object. NOTE This chapter only covers a few basic principles of properties. Refer to Apple’s documentation for a more complete discussion about properties.
When using a property with retain and managing memory yourself, you must release the temporary variable. For instance, suppose you set an instance variable called objSimpleRetain and this instance variable’s property had a retain attribute. @property(retain) Simple objSimple; When setting this property, you must release whatever temporary instance you might create. Consider the following method that sets objSimpleRetain. - (IBAction)giveWelcome { Simple* temp1 = [Simple alloc]; self.objSimpleRetain = temp1; [objSimpleRetain sayHello:@"Mike"]; NSLog(@"retaincount (mike): %d", [objSimpleRetain retainCount]); [temp1 release]; } The temp1 reference is released at the method’s end. If you ran the giveWelcome method, NSLog would print two as the retain count. The retain count of two is because the method first allocates temp1, which sets the object’s retain count to one, and then the method sets the property; because the property specified retain, the object’s retain count becomes two. Finally, the method releases temp1 and the retain count returns to one. Note that you could have just as easily used autorelease and let the runtime release the temporary object at the event loop’s end by writing the method as follows: - (IBAction)giveWelcome { Simple* temp1 = [[Simple alloc] autorelease]; self.objSimpleRetain = temp1; [objSimpleRetain sayHello:@"Mike"]; NSLog(@"retaincount (mike): %d", [objSimpleRetain retainCount]); }
You can also specify a property use assignment by using the assign attribute. @property(assign) Simple objSimple; Specifying assign is equivalent to simply assigning a pointer to an object without increasing its retain count. You must take care to not call autorelease or release a temporary object, as the assigned property simple points to the temporary object. I generally avoid using assign for objects. Where assign is appropriate is for creating primitive properties. For instance, you might make an integer a property of a class. An integer is a primitive, and so you assign values to the property; you do not assign a pointer to the integer. @property (assign) int myInteger;
Sometimes you might wish to obtain an independent object copy. You accomplish this using the copy attribute. When you use the copy attribute, the setter creates a new object and the original object is duplicated. This property is an independent object, not related to the original. There are two copy types: shallow and deep.A shallow copy is when you only duplicate an object’s references to its instance variables, while a deep copy is when you make a copy of those instance variables as well. Making a shallow copy is easy—a deep copy, not so much. To copy your own custom class, your class must implement the NSCopying protocol.You learn more about protocols later; a comprehensive discussion on writing your own class that implements the NSCopying protocol is beyond this chapter’s scope and would needlessly complicate it. However, in Chapter 15, this book does briefly discuss the NSCopying protocol. Look up the NSCopying online documentation for more information.However, copying a Cocoa class that already implements the NSCopying protocol is not beyond this chapter’s scope. For instance, copying an independent string copy seems a reasonable enough requirement. Consider the class, Foo, in Listings 4-3 and 4-4, and a method that uses Foo (Listing 4-5). Listing 4-3 Foo’s interface #import <Foundation/Foundation.h> @interface Foo : NSObject { NSString * myString; } @property(copy) NSString *myString; @end Listing 4-4 Foo’s implementation #import "Foo.h" @implementation Foo @synthesize myString; @end Listing 4-5 A method that uses Foo
Foo * myFoo = [[Foo alloc] autorelease]; NSString* message = [[[NSString alloc] initWithString: @"A copied string."] autorelease]; myFoo.myString = message; message = @"A Changed string."; NSLog(myFoo.myString); } The giveWelcome method creates a Foo instance (myFoo), a new string (message), and then sets myFoo’s myString property to message. Because Foo’s interface declared that myString uses copy, myFoo creates a copy of the new string when giveWelcome sets myString. When giveWelcome changes the message to a different string, myFoo.myString remains the same value. Had you used retain or assign, the myFoo.myString’s value would have changed as well.
Remember, every class you define should ultimately inherit from the NSObject class. The NSObject class contains a dealloc method. You use this method to release any instance variables and perform other cleanup tasks. When you declare properties in your class, you should always override this method by declaring your own dealloc method in your class. You will see this over and over again in the remainder of this book. In fact, to avoid memory leaks, remember this one rule: when using properties with the attributes nonatomic and retain, always release the properties in a dealloc method. For instance, in Listing 4-3 you declare a property named myString in the class Foo. To prevent a memory leak, Foo should have a dealloc method like Listing 4-6. As you progress through this book, the dealloc method should become second nature. Listing 4-6 A simple dealloc method - (void) dealloc { [super dealloc]; [myString release]; }
As with Objective-C’s other language constructs, multiple arguments will probably appear strange at first; however, once you become accustomed to it, I am confident you will find the syntax easier than Java, C++, and other dot-notation languages. Why am I so confident that you will love Objective-C’s syntax for multiple arguments? In a word, readability. How many times have you seen code like this in a Java program? objMyClass.startPlay("Adventures of Tom Thumb", 44, new CherryPie( ), "Jack Sprat", 77); What exactly do the arguments mean? What are you sending to the startPlay method in objMyClass? Now consider the same method using Objective-C. [objMyClass startPlay: @"Adventures of Tom Thumb" audienceMembers:44 pie: [[CherryPie alloc] init] supportingActor:@"Jack Sprat" extrasNeeded:77]; You know exactly what the arguments sent to the method mean when using Objective-C. You are starting a play entitled ―Adventures of Tom Thumb‖ that has 44 members in the audience, needs a cherry pie, has a supporting actor named Jack Sprat, and requires 77 extras. The signature of the method called in the previous message has a syntax as follows: - (void) startPlay: (NSString*) title audienceMembers: (int) value pie: (CherryPie*) pievalue supportingActor: (NSString*) actorvalue extrasNeeded: (int) extrasvalue; The first argument is unnamed. The second and any further arguments are distinguished by a space followed by an argument name and colon, followed by the type in parentheses, followed by a parameter name to hold the value. Now, here’s the tricky part: When referring to a multiple-argument method, when calling the method, you refer to its named arguments. An argument’s named argument is the name prior to the argument’s data type. When using the argument within the method’s implementation that the argument is a part of, you refer to the actual parameter name, not the argument name. So, for instance, in the startPlay method’s implementation, you refer to title, value, pievalue, actorvalue, and extrasvalue. When calling the method, you refer to startPlay’s named arguments: audienceMembers, pie, supportingActor, and extrasNeeded.
(continued) Listing 4-7 Simple’s interface #import <Foundation/Foundation.h> @interface Simple : NSObject { } - (void) startPlay: (NSString*) title audienceMembers: (int) value supportingActor: (NSString*) actorvalue extrasNeeded: (int) extrasvalue; @end Listing 4-8 Simple’s implementation #import "Simple.h" @implementation Simple - (void) startPlay: (NSString*) title audienceMembers: (int) value supportingActor: (NSString*) actorvalue extrasNeeded: (int) extrasvalue { NSLog(@"The title: %@", title); NSLog(@"Audience: %d", value); NSLog(@"Supporting actor: %@", actorvalue); NSLog(@"Extras needed: %d", extrasvalue); } @end Listing 4-9 The main.m file modified to call Simple’s startPlay method #import <UIKit/UIKit.h> #import "Simple.h" int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Simple * objSimple = [[[Simple alloc] init] autorelease]; [objSimple startPlay:@"Peter Pan" audienceMembers:500 supportingActor:@"John Doe" extrasNeeded:55]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; } Listing 4-10 Debugger console output from running program [Session started at 2008-12-29 18:49:55 -0500.] 2008-12-29 18:49:57.242 SimpleMultiArg[3132:20b] The title: Peter Pan 2008-12-29 18:49:57.243 SimpleMultiArg[3132:20b] Audience: 500 2008-12-29 18:49:57.244 SimpleMultiArg[3132:20b] Supporting actor: John Doe 2008-12-29 18:49:57.245 SimpleMultiArg[3132:20b] Extras needed: 55 NOTE The method startPlay is not the method’s true name. In Objective-C, if more than one parameter, the method’s parameters (other than the first parameter) are part of the method’s name. For instance, the startPlay method’s name is actually the following. By this book’s end, you should be familiar with this naming convention, as many of the methods you use have multiple parameters. startPlay:audienceMemebers:supportingActor:extrasNeeded: Understanding the id Variable Type,Dynamic Typing, and Dynamic Binding Objective-C is a dynamically typed language. Like Java, when using Objective-C, object types can be determined dynamically at runtime rather than statically at compile time. Objective-C accomplishes this dynamic typing using the id data type.
The id variable is a data type that represents an object’s address. Because it’s just an address, id can be any object, and because its type is a pointer, you don’t need to include the * symbol, as the * symbol signifies a pointer to a specific type. For instance, Foo * myFoo; is a pointer to a Foo object. The compiler knows the pointer points to an address that contains a Foo type. However, the following, id myFoo; provides no such information to the compiler. The compiler only knows that myFoo is a pointer—the compiler knows where the pointer is pointing, but doesn’t know the data type of what myFoo points to. Only at runtime can it be determined what myFoo actually points to.
Objective-C accomplishes dynamic behavior using what’s called dynamic typing and dynamic binding. Dynamic typing means that an object’s type is not determined until runtime. For instance,a method that takes an id or an instance variable of type id has no way of knowing the object’s type until the object is actually sent to the method or assigned to the instance variable. Dynamic binding means that a method to invoke is not determined until runtime. And, unlike Java, Objective-C often doesn’t require casting an object to its specific data type before being used.
You have already seen how Objective-C classes inherit from parent classes. In the interface, you specify that a class inherits from another class by placing the parent’s name after the class’s name and a colon. @interface SimpleChild : Simple Like any object-oriented language, Objective-C classes extend ancestors further up its hierarchy, with new methods and instance variables. Objective-C child classes can also redefine an ancestor’s method. But, like Java (and unlike C++), an Objective-C class can only inherit from one parent; Objective-C doesn’t support multiple inheritance. Overriding Methods Objective-C inheritance allows overriding methods, but not instance variables. You already saw an example of overriding methods when you overrode NSObject’s dealloc, retain, and release methods in the class Foo. #import "Foo.h" @implementation Foo - (void) dealloc { NSLog(@"deallocating Foo...."); [super dealloc]; } ---snip--- @end Instead of calling NSObject’s methods, the runtime first calls Foo’s, but since the methods in Foo all call its parent’s version of each method, the runtime looks in Foo’s inheritance hierarchy for a version of the method until it finds NSObject’s version. NOTE The term ―super‖ refers to the class’s parent. For instance, you might have a method called doIt that matches the parent’s doIt method. But doIt might add functionality and not replace functionality, so you should call the parent doIt method as well. - (void) doIt { [self doMyStuff]; [super doIt]; }
Unlike Java, you cannot overload methods when using Objective-C. In Java you overload a method when you provide a method with the same name but a different signature in the same class. For instance, you could define two methods like the following. The two methods are treated as distinct methods by the Java runtime. public void myMethod(String name); public void myMethod(int age); Not so when using Objective-C, when faced with two methods like below, the compiler issues an error and will not compile your class.
Because Objective-C does not support overloading, in the Objective-C programming community, it is common practice to add the argument’s name to the method name.
Finally, you should note that for multiple argument methods, Objective-C’s lack of method overloading is not problematic if any argument other than the first is different. Remember, a method’s name includes its argument names. The following two method names are not the same.
The compiler treats these methods as distinct because their names are actually myMethod: name: and myMethod:age: and not simply myMethod. Get used to this naming convention, it might seem strange at first, but it honestly makes Apple’s documentation much easier to use.
Categories allow other classes to be extended without requiring inheritance. A categorycan contain both new methods and methods that override a class’s methods. Categories are most useful when you find yourself in a situation where a supplied class doesn’t provide functionality you want. To use a category, you create an interface and an implementation that specifies the class you wish to add a category to, followed by the category’s name in parentheses. For instance, if you wished to add a category named FooCategory to NSString, you would type the following in the FooCategory.h file. @interface NSString (FooCategory) In the FooCategory.m file, you would type the following: @implementation NSString (FooCategory) followed by whatever methods you wished to add to NSString. Then, in the class you wished to use the category’s methods, simply import the category and the runtime automatically resolves calls, such as, [objMyString aReallyDumbMethod]; to the category rather than NSString. NOTE You can’t add instance variables to a class using a category. NOTE In the category’s header file, you must import the header file containing the class the category is extending.
Protocols are similar to Java interfaces. In Java, an interface specifies the methods that a class that implements the interface must have. This is often called a contract. A class claiming to implement an interface should implement that contract—the class is promising it contains implementations for all of the interface’s method declarations. So when you write a method like the following, you can rest assured the object has the called method implementation. - (void) handleDoIt : (id <DoerProtocol>) objADoerImpl { [objADoerImpl doSomething]; } The method handleDoIt takes an id specified to implement the DoerProtocol as an argument. Then when handleDoIt calls doSomething (a method declared in DoerProtocol), the runtime automatically, using dynamic binding, finds the correct object method implementation and calls it. Incidentally, when completing the task in the dynamic binding and dynamic typing section, you created a method that took an id and then called a specific method on an object, called play. - (void) doIt: (id) value { [value play]; } Although the code compiled and worked, this is not the best way to implement a method such as this in my opinion. Instead, you should have used a protocol—that way, it’s explicit that the method’s argument must be an object that implements the play method (assuming play is declared in the protocol).
} The code in the method that takes a protocol as an argument can then call protocol methods, knowing that the class that adopts the protocol has the specified methods. It’s not until runtime that the actual class method is dynamically bound to the interface method.Consider a method that takes a protocol as an argument. At compile time, the compiler only knows that the method takes an instance of the protocol. The compiler doesn’t know the actual object’s data type. If the method calls a protocol method, the compiler only assumes the method is declared by the protocol. The compiler doesn’t know the method definition.At runtime, though, you pass an actual object to the method. When the object is passed to the method, it is dynamically typed. Then, when the method calls the protocol’s method, the protocol’s method is dynamically bound to the object’s method. Thus, just like Java, youcan specify generic methods that take a protocol as an argument and then let the runtime dynamically type and bind the object when running the application.You define a protocol using the @protocol compiler directive combined with an @end directive. You define method declarations between the two directives. For instance, the following code defines a protocol named MyProtocol. #import <Foundation/Foundation.h> @protocol MyProtocol - (NSNumber*) myMethod; @end You specify a class conforms to a protocol by adding the protocol’s name in the class’s declaration in its header file. @interface Foo : NSObject <MyProtocol> Specifying a protocol in a class’s declaration guarantees that class implements the methods declared in the MyProtocol protocol. Protocols allow adding optional method declarations in addition to required method declarations. You specify optional methods using the @optional compiler directive and required methods using the @required compiler directive. The default for methods is required. For instance, you could modify MyProtocol to have an optional myMethodTwo method. #import <Foundation/Foundation.h> @protocol MyProtocol
@optional
@end Of course, having a Java programmer’s perspective, I question the logic behind a contract with an optional method declaration—if I write code against a protocol, I darn sure want the method to be there. Of course, this is Objective-C, so unlike Java, if the method isn’t there, you don’t get an exception. Remember, in Objective-C, you send a message and if nobody can handle the message, nothing happens. You use the protocol similar to a class, only there is a subtle difference. When passing a class, you aren’t actually passing the class, but rather a pointer. For instance, in the sayHello method, you are not passing a string called name as a parameter, but rather a pointer to a string.
But a protocol isn’t a class; it’s merely a header file with declarations (not definitions). Instead, you must either pass just the protocol itself (and the id is automatically understood) or you must pass an id. Using a protocol rather than an id, you would specify something similar to the following in the interface of the class using your protocol. Suppose you wished defining a protocol for thermometers. There are many different ways a thermometer might be implemented. However, they all must tell the user his or her temperature. So if you created a Doctor class, although you know it should tell the patient his or her temperature, you do not know the specific type of thermometer the Doctor will use. So you use a protocol.
You declare that a method takes a protocol as an argument. You don’t specify the object’s class, though. For instance, in the sayTemp method, you only know objTherm adopts the ThermProtocol. You do not know objTherm’s actual class type. When using an id—incidentally, this is the preferred syntax you see in Apple’s documentation—you would specify something similar to the following.
In this example, you declare that sayTemp takes any class that implements the ThermProtocol. Actually, the first method signature without the id is also stating that sayTemp takes any class that implements the ThermProtocol, only the id is left implicit. Both method signatures work equally well, but by convention the second signature is what most programmers write.
Objective-C’s exception handling is similar to Java’s and C++’s. Like Java and C++, you use a try-catch block. Code that might raise an exception is wrapped in a try block, followed immediately by a catch block. The @try compiler directive defines a code block that might throw an exception. The @catch directive defines a code block that handles the immediately preceding try block. The @finally directive defines a code block that is always executed, regardless if an exception was thrown. @try { } @catch(NSException *e) { } @finally { } The most general exception is an NSException. All exceptions inherit from this Cocoa class. Like Java, you can throw an exception. In Objective-C, you use the @throw compiler directive. NOTE Apple recommends using the NSError Foundation Framework class rather than NSException for handling expected errors. See Apple’s ―Introduction to Error Handling Programming Guide for Cocoa‖ and also ―Introduction to Exception Programming Topics for Cocoa‖ for more information on both classes. Scattered through this book’s examples, you will see instances of NSError and NSException.
You liked the article?
Like: 0
Vote for difficulty
Current difficulty (Avg): Medium
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.