suppose you had Bumblebee, Jet, and Bird classes. All three, although unrelated, implement the flying behavior, and so you could say they are each a Flyer. Also notice that you could shoot all three with a gun, and so you could also say they are Shootable. In both situations you are using a concept to group unrelatedobjects based upon behaviors. Protocols model these types of situations. A class that wishes to adopt a protocol must implement the methods declared by the protocol. By requiring the adopting class to implement methods, a protocol forms a contract. That contract ensures the runtime that a class that adopts a protocol implements the methods declared by the protocol.
A protocol’s syntax is similar to a class’ interface. But unlike when writing a class, you only create a header file and do not create an implementation. A protocol begins with compiler directive and ends with compiler directive. Between the two compiler directives go any method declarations you wish an adopting class to implement. Hopper - (void) hop; - (BOOL) setHopDistance: (long long)
Classes adopt protocols. For instance, a Frog class might implement the Hopper protocol. For Frog to adopt Hopper, it must first import the Hopper.h header file. It then includes Hopper in Frog: Amphibian A frog is an amphibian, and so it inherits from its Amphibian parent class. Following the parent class is the protocol the frog adopts. The protocol’s name is surrounded by angle brackets.
1.Create a View-based application named SimpleFlyer. 2.Create a new C header file called Flyer.h. 3.Modify Flyer.h to be a Flyer protocol that declares a “fly” method. 4. Create three new Objective-C classes named Bumblebee, Bird, and Jet. Change all three so that they adopt the Flyer protocol
@protocol Flyer - (NSString *) Bumblebee.m #import Bumblebee - (NSString *) fly { bumblebee..."; Bumblebee.h #import #import Bumblebee : NSObject {
Bird.h #import #import Bird : NSObject { Bird.m #import Bird - (NSString *) fly { bird..."; Jet.h #import #import Jet : NSObject { Jet.m #import Jet - (NSString*) fly { jet...";
5. Modify SimpleFlyerViewController.h so that it knows about all three classes and the protocol by using precompiler directives. Also declare a callFly method and create three properties for each of the three classes. Also create a changeLabel method and be certain you precede the method with the IBAction keyword rather SimpleFlyerViewController.h SimpleFlyerViewController : UIViewController { Bumblebee * myBee; Bird * myBird; Jet * myJet; IBOutlet UILabel * myLabel; (nonatomic,retain) Bumblebee * (nonatomic,retain) Bird * (nonatomic,retain) Jet * (nonatomic,retain) UILabel * myLabel; - (IBAction) changeLabel: (id) sender; - (NSString*) callFly: (id ) 6. Create an IBOutlet for a UILabel and name the property myLabel.
7. Modify SimpleFlyerViewController.m so that it synthesizes the properties and implements the callFly method Do not forget to import the classes from the interface. SimpleFlyerViewController.m #import "SimpleFlyerViewController.h" #import "Flyer.h" #import "Bumblebee.h" #import "Bird.h" #import myLabel; static int clicks = 0; - (IBAction) changeLabel: (id) sender { if(clicks == 0) { self.myLabel.text = [self callFly:self.myBee]; } else if(clicks == 1) { self.myLabel.text = [self callFly:self.myBird]; } else { self.myLabel.text = [self callFly:self.myJet]; } clicks++; } - (NSString *) callFly: (id ) aFlyer { return [aFlyer fly]; } - (void) viewDidLoad { [super viewDidLoad]; self.myBee = [[Bumblebee alloc] init]; self.myBird = [[Bird alloc] init]; self.myJet = [[Jet alloc] init]; } - (void)dealloc { [self.myJet release]; [self.myLabel release]; [self.myBee release]; [self.myBird release]; [super dealloc];
8.Double-click SimpleFlyerViewController.xib to open it in Interface Builder. 9.Add a button to the view’s canvas. Double-click the button and change its title to Fly. 10.Add a label to the view’s canvas and resize the label 11.Connect the File’s Owner myLabel outlet to the label Remember, the“outlet” term is simply shorthand for referring to the IBOutlet
12.Connect the File’s Owner callFly: action to the button’s Touch Up Inside event 13. Save and exit Interface Builder.
Properties and Protocols Any classes that then wish to adopt the protocol either must implement custom accessor methods or must synthesize the property. A property’s syntax in a protocol is similar to an interface, only you do not declare an instance variable. Countable (nonatomic, retain) NSNumber * By declaring a property in a protocol, you are informing the compiler that implementing classes must either synthesize the property or create custom accessor methods. But note, any class implementing the protocol must synthesize the property and declare the property as an instance variable in the class’ header.
(nonatomic,retain) NSString * singerName; - (void) For Example Tenor.h #import #import Tenor : NSObject { NSString * singerName; Tenor.m #import singerName; - (void) sing { the singing tenor...", self.singerName);
SingingFool.m #import #import "Tenor.h" #import "Singer.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Tenor * myTenor = [[[Tenor alloc] init] autorelease]; myTenor.singerName [myTenor sing]; [pool drain]; return 0; } Jameserotti, the singing tenor... OutPut
Optional Methods Objective-C allows developers considerable flexibility. Protocols can also declare optional methods that adopters are not required to implement. -(void) punch; -(void) - (void) compiler directive is the default; methods without a preceding compiler directive are assumed
If you wish to call an optional protocol method in another method such as hitInFace, you must check for the method’s existence in the adopting class. -(void) hitInFace: (id ) aFighter { if([aFighter { [aFighter cry]; } The easiest way to ensure a class responds to an optional protocol method is through the respondsToSelector: method.
//default - (void) - (void) Glutton.h #import #import Glutton : NSObject { Glutton.m #import Glutton - (void) eat { eating..."); } - (void) belch { belching..."); Socialite.h #import #import Socialite : NSObject {
Socialite.m #import Socialite - (void) eat { is eating..."); EatingExample.m #import #import "Glutton.h" #import "Socialite.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Glutton * myGlutton = [[[Glutton alloc] init] autorelease]; Socialite * mySocialite = [[[Socialite alloc] init] autorelease]; [myGlutton eat]; [mySocialite eat]; if([myGlutton { [myGlutton belch]; } if([mySocialite { [mySocialite belch]; } [pool drain]; return 0; }
Protocols and id - (void) doAttach: (id) aFloater; - (void) detach: ( ) aFloater; - (void) sinkAFloater: (id ) aFloater; Using id Only One way you can use a protocol as a method parameter is by using the id keyword. The id type is a generic pointer that can point to any object. -(void) doAttach: (id) aFloater - { if([aFloater { [aFloater }
Using id with a Protocol Restriction You can restrict a method’s parameter so that it can still take an id, but only if that id’s object adopts the specified protocol. The following detach method illustrates. -(void) detach: ( ) aFloater { [aFloater detach]; } In detach the id parameter is implicit. The method takes any object provided the object implements the Floater protocol. You can also make the id explicit, as the following sinkAFloater method illustrates. -(void) sinkAFloater: (id ) aFloater { [aFloater sink]; }
Adopting Multiple Protocols Objective-C allows objects to adopt more than one protocol. When a class adopts more than one protocol, you list the protocols as a comma- separated list. Duck : NSObject Duck : NSObject Swimmer - (NSString *) Duck.h #import #import "Flyer.h" #import Duck : NSObject {
Duck.m #import Duck - (NSString *) fly { duck..."; } - (NSString *) swim { duck..."; SimpleFlyerViewController.h SimpleFlyerViewController : UIViewController { Bumblebee * myBee; Bird * myBird; Jet * myJet; Duck * myDuck; IBOutlet UILabel * myLabel; (nonatomic,retain) Bumblebee * (nonatomic,retain) Bird * (nonatomic,retain) Jet * (nonatomic,retain) Duck * (nonatomic,retain) UILabel * myLabel; - (IBAction) changeLabel: (id) sender; - (NSString*) callFly: (id ) aFlyer; - (void) callSwim: (id)
SimpleFlyerViewController.m #import "SimpleFlyerViewController.h" #import "Flyer.h" #import "Bumblebee.h" #import "Bird.h" #import "Jet.h" #import myDuck; static int clicks = 0; - (IBAction) changeLabel: (id) sender { id myObject = nil; if(clicks == 0) { self.myLabel.text = [self callFly:self.myBee]; myObject = self.myBee; } else if(clicks == 1) { self.myLabel.text = [self callFly:self.myBird]; myObject = self.myBird; } else if(clicks == 2) { self.myLabel.text = [self callFly:self.myJet]; myObject = self.myJet; } else { self.myLabel.text = [self callFly:self.myDuck]; myObject = self.myDuck; } [self callSwim:myObject]; clicks++; } - (NSString *) callFly: (id ) aFlyer { return [aFlyer fly]; } - (void) callSwim: (id) aFlyer { if ( [aFlyer ) { NSLog([aFlyer swim]); } - (void) viewDidLoad { [super viewDidLoad]; self.myBee = [[[Bumblebee alloc] init] autorelease]; self.myBird = [[[Bird alloc] init] autorelease]; self.myJet = [[[Jet alloc] init] autorelease]; self.myDuck = [[[Duck alloc] init] autorelease]; } - (void)dealloc { [self.myJet release]; [self.myBee release]; [self.myBird release]; [self.myDuck release]; [self.mylabel release]; [super dealloc];
Extending Protocols The syntax for a protocol extending another protocol is as follows: Swimmer Sinkable - (void) Floater.h #import Floater - (void) Swimmer.h #import Swimmer -(void) Driftwood.h #import #import Driftwood : NSObject {
Driftwood.m #import Driftwood -(void) floatMe { floating..."); } -(void) sink { sinking...."); Duck.h #import #import Duck : NSObject { Duck.m #import Duck - (void) swim { swimming..."); } -(void) floatMe { floating...."); } - (void) sink { sinking...."); LeadWeight.h #import #import LeadWeight : NSObject { } - (void) sinkASinkable: (id )
LeadWeight.m #import LeadWeight - (void) sinkASinkable: (id ) aSinkable; { [aSinkable sink]; DriftwoodDuck.m #import #import "Duck.h" #import "Driftwood.h" #import "LeadWeight.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Duck * myDuck = [[[Duck alloc] init] autorelease]; [myDuck floatMe]; [myDuck swim]; Driftwood * myDriftwood = [[[Driftwood alloc] init] autorelease]; [myDriftwood floatMe]; LeadWeight * myWeight = [[[LeadWeight alloc] init] autorelease]; [myWeight sinkASinkable:myDriftwood]; [myWeight sinkASinkable:myDuck]; [pool drain]; return 0; } Debugger console logging duck floating.... duck swimming... driftwood floating... driftwood sinking.... duck sinking....
Categories When you want to add methods to a class, you typically extend it. However this solution isn't always perfect, especially if you want to rewrite the functionality of a class that you don't have the source code to. Categories allow you to add functionality to already existing classes without extending them. Ruby also has similar functionality to this.
Categories allows programmer to add functionality to already existing classes without extending them. BaseClass.hBaseClass.m BaseClass : NSObject { int num1, num2; } -(void)set :(int) x and: (int) y; -(int)add; BaseClass -(void)set :(int) x and: (int) y { num1 = x; num2 = y; } -(int)add { return num1+num2; } -(int)sub { if(num1>num2){ return num1-num2; } else return num2-num1;
SubClass.hSubClass.m BaseClass(Category) - BaseClass(BaseClass) -(void)show:(int)x { printf("Result is : %d \n",x); #import"BaseClass.m“ #import"SubClass.m“ #import int main() { BaseClass *obj = [[BaseClass alloc] init]; [obj set:10 and:8]; [obj show:[obj add]]; [obj show:[obj sub]]; [obj release]; return 0; } Result is : 18 Result is : 2
1.There is the Fraction (Math) Fraction (Math). 2.There can only be one category with the same name. Additional cateogies may be added on with different but unqiue names. 3.Categories can't add instance variables. 4.Categories are useful for creating private methods. Since Objective-C has no notion of private/protected/public methods like java does, one has to create categories that hide such functionality. The way this is done is by moving the private methods from your class's header (.h) file to the implementation file (.m)
BetterDuck+Duck.h #import #import Duck (BetterDuck) -(void) BetterDuck+Duck.m #import Duck (BetterDuck) - (void) quack {
DriftwoodDuck.m #import //#import "Duck.h" #import "Driftwood.h" #import "LeadWeight.h" #import "BetterDuck+Duck.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Duck * myDuck = [[[Duck alloc] init] autorelease]; [myDuck float]; [myDuck swim]; Driftwood * myDriftwood = [[[Driftwood alloc] init] autorelease]; [myDriftwood float]; LeadWeight * myWeight = [[[LeadWeight alloc] init] autorelease]; [myWeight sinkASinkable:myDriftwood]; [myWeight sinkASinkable:myDuck]; Duck * myBetterDuck = [[[Duck alloc] init] autorelease]; [myBetterDuck float]; [myBetterDuck swim]; [myBetterDuck quack]; [pool drain]; return 0; }
Posing
It enable programmer to pose subclass to super class globally. When subclass posed on super class method which are same in both classes are override with the subclass methods. BaseClass.h BaseClass.m BaseClass : NSObject { int num1, num2; } -(void)set :(int) x and: (int) y; -(int)add; -(int)sub; BaseClass -(void)set :(int) x and: (int) y { num1 = x; num2 = y; } -(int)add { return num1+num2; } -(int)sub { if(num1>num2) { return num1-num2; } Else return num2-num1; } -(void)show:(int)x { printf("Base class result : %d \n",x);
SubClass.h SubClass.m SubClass : BaseClass SubClass -(void)show:(int)x { printf("Sub class result : %d \n",x);
#import"BaseClass.m" #import"SubClass.m" #import int main() { // create base class object. BaseClass *obj1 = [[BaseClass alloc] init]; [obj1 set:10 and:8]; [obj1 show:[obj1 add]]; [obj1 show:[obj1 sub]]; // pose subclass to baseclass. [SubClass poseAsClass: [BaseClass class]]; // create base class object after posing. BaseClass *obj2 = [[BaseClass alloc] init]; [obj2 set:11 and:6]; [obj2 show:[obj2 add]]; [obj2 show:[obj2 sub]]; / / free memory [obj1 release]; [obj2 release]; return 0; } we have two classes BaseClass and SubClass. Method show() is defined in both class but with different message. Before posing when we create object of base class, show method will show the output according to the base class but after posing it will display output from show method defined in sub class. Base class result : 18 Base class result : 2 Sub class result : 17 Sub class result : 5