diff options
author | Adam Bard <github@adambard.com> | 2014-01-29 22:05:55 -0800 |
---|---|---|
committer | Adam Bard <github@adambard.com> | 2014-01-29 22:05:55 -0800 |
commit | a51a09a0b2fea9113aaaf3c31262d3e7b33feb89 (patch) | |
tree | 01dfef58f1514c48061c0d1739c464b1d79afa5c | |
parent | 4a9b80d5f5a0b2618bf7b7f5b4a6b603f6f7d7f0 (diff) | |
parent | 734bc28f90dc86cf8ac2472aa04775a2549cb978 (diff) |
Merge pull request #486 from levibostian/objective-c-protocols
Objective-C protocols, categories, and extensions
-rw-r--r-- | objective-c.html.markdown | 220 |
1 files changed, 212 insertions, 8 deletions
diff --git a/objective-c.html.markdown b/objective-c.html.markdown index 6f48c155..0f0165ec 100644 --- a/objective-c.html.markdown +++ b/objective-c.html.markdown @@ -430,7 +430,8 @@ distance = 18; // References "long distance" from MyClass implementation return @42; } -// To create a private method, create the method in the @implementation but not in the @interface +// Objective-C does not have private method declarations, but you can simulate them. +// To simulate a private method, create the method in the @implementation but not in the @interface. - (NSNumber *)secretPrivateMethod { return @72; } @@ -444,15 +445,218 @@ distance = 18; // References "long distance" from MyClass implementation @end // States the end of the implementation -/* - * A protocol declares methods that can be implemented by any class. - * Protocols are not classes themselves. They simply define an interface - * that other objects are responsible for implementing. - */ -@protocol MyProtocol - - (void)myProtocolMethod; +/////////////////////////////////////// +// Categories +/////////////////////////////////////// +// A category is a group of methods designed to extend a class. They allow you to add new methods +// to an existing class for organizational purposes. This is not to be mistaken with subclasses. +// Subclasses are meant to CHANGE functionality of an object while categories instead ADD +// functionality to an object. +// Categories allow you to: +// -- Add methods to an existing class for organizational purposes. +// -- Allow you to extend Objective-C object classes (ex: NSString) to add your own methods. +// -- Add ability to create protected and private methods to classes. +// NOTE: Do not override methods of the base class in a category even though you have the ability +// to. Overriding methods may cause compiler errors later between different categories and it +// ruins the purpose of categories to only ADD functionality. Subclass instead to override methods. + +// Here is a simple Car base class. +@interface Car : NSObject + +@property NSString *make; +@property NSString *color; + +- (void)turnOn; +- (void)accelerate; + +@end + +// And the simple Car base class implementation: +#import "Car.h" + +@implementation Car + +@synthesize make = _make; +@synthesize color = _color; + +- (void)turnOn { + NSLog(@"Car is on."); +} +- (void)accelerate { + NSLog(@"Accelerating."); +} + +@end + +// Now, if we wanted to create a Truck object, we would instead create a subclass of Car as it would +// be changing the functionality of the Car to behave like a truck. But lets say we want to just add +// functionality to this existing Car. A good example would be to clean the car. So we would create +// a category to add these cleaning methods: +// @interface filename: Car+Clean.h (BaseClassName+CategoryName.h) +#import "Car.h" // Make sure to import base class to extend. + +@interface Car (Clean) // The category name is inside () following the name of the base class. + +- (void)washWindows; // Names of the new methods we are adding to our Car object. +- (void)wax; + @end +// @implementation filename: Car+Clean.m (BaseClassName+CategoryName.m) +#import "Car+Clean.h" // Import the Clean category's @interface file. + +@implementation Car (Clean) + +- (void)washWindows { + NSLog(@"Windows washed."); +} +- (void)wax { + NSLog(@"Waxed."); +} + +@end + +// Any Car object instance has the ability to use a category. All they need to do is import it: +#import "Car+Clean.h" // Import as many different categories as you want to use. +#import "Car.h" // Also need to import base class to use it's original functionality. + +int main (int argc, const char * argv[]) { + @autoreleasepool { + Car *mustang = [[Car alloc] init]; + mustang.color = @"Red"; + mustang.make = @"Ford"; + + [mustang turnOn]; // Use methods from base Car class. + [mustang washWindows]; // Use methods from Car's Clean category. + } + return 0; +} + +// Objective-C does not have protected method declarations but you can simulate them. +// Create a category containing all of the protected methods, then import it ONLY into the +// @implementation file of a class belonging to the Car class: +@interface Car (Protected) // Naming category 'Protected' to remember methods are protected. + +- (void)lockCar; // Methods listed here may only be created by Car objects. + +@end +//To use protected methods, import the category, then implement the methods: +#import "Car+Protected.h" // Remember, import in the @implementation file only. + +@implementation Car + +- (void)lockCar { + NSLog(@"Car locked."); // Instances of Car can't use lockCar because it's not in the @interface. +} + +@end + +/////////////////////////////////////// +// Extensions +/////////////////////////////////////// +// Extensions allow you to override public access property attributes and methods of an @interface. +// @interface filename: Shape.h +@interface Shape : NSObject // Base Shape class extension overrides below. + +@property (readonly) NSNumber *numOfSides; + +- (int)getNumOfSides; + +@end +// You can override numOfSides variable or getNumOfSides method to edit them with an extension: +// @implementation filename: Shape.m +#import "Shape.h" +// Extensions live in the same file as the class @implementation. +@interface Shape () // () after base class name declares an extension. + +@property (copy) NSNumber *numOfSides; // Make numOfSides copy instead of readonly. +-(NSNumber)getNumOfSides; // Make getNumOfSides return a NSNumber instead of an int. +-(void)privateMethod; // You can also create new private methods inside of extensions. + +@end +// The main @implementation: +@implementation Shape + +@synthesize numOfSides = _numOfSides; + +-(NSNumber)getNumOfSides { // All statements inside of extension must be in the @implementation. + return _numOfSides; +} +-(void)privateMethod { + NSLog(@"Private method created by extension. Shape instances cannot call me."); +} + +@end + +/////////////////////////////////////// +// Protocols +/////////////////////////////////////// +// A protocol declares methods that can be implemented by any class. +// Protocols are not classes themselves. They simply define an interface +// that other objects are responsible for implementing. + // @protocol filename: "CarUtilities.h" +@protocol CarUtilities <NSObject> // <NSObject> => Name of another protocol this protocol includes. + @property BOOL engineOn; // Adopting class must @synthesize all defined @properties and + - (void)turnOnEngine; // all defined methods. +@end +// Below is an example class implementing the protocol. +#import "CarUtilities.h" // Import the @protocol file. + +@interface Car : NSObject <CarUtilities> // Name of protocol goes inside <> + // You don't need the @property or method names here for CarUtilities. Only @implementation does. +- (void)turnOnEngineWithUtilities:(id <CarUtilities>)car; // You can use protocols as data too. +@end +// The @implementation needs to implement the @properties and methods for the protocol. +@implementation Car : NSObject <CarUtilities> + +@synthesize engineOn = _engineOn; // Create a @synthesize statement for the engineOn @property. + +- (void)turnOnEngine { // Implement turnOnEngine however you would like. Protocols do not define + _engineOn = YES; // how you implement a method, it just requires that you do implement it. +} +// You may use a protocol as data as you know what methods and variables it has implemented. +- (void)turnOnEngineWithCarUtilities:(id <CarUtilities>)objectOfSomeKind { + [objectOfSomeKind engineOn]; // You have access to object variables + [objectOfSomeKind turnOnEngine]; // and the methods inside. + [objectOfSomeKind engineOn]; // May or may not be YES. Class implements it however it wants. +} + +@end +// Instances of Car now have access to the protocol. +Car *carInstance = [[Car alloc] init]; +[[carInstance setEngineOn:NO]; +[carInstance turnOnEngine]; +if ([carInstance engineOn]) { + NSLog(@"Car engine is on."); // prints => "Car engine is on." +} +// Make sure to check if an object of type 'id' implements a protocol before calling protocol methods: +if ([myClass conformsToProtocol:@protocol(CarUtilities)]) { + NSLog(@"This does not run as the MyClass class does not implement the CarUtilities protocol."); +} else if ([carInstance conformsToProtocol:@protocol(CarUtilities)]) { + NSLog(@"This does run as the Car class implements the CarUtilities protocol."); +} +// Categories may implement protocols as well: @interface Car (CarCategory) <CarUtilities> +// You may implement many protocols: @interface Car : NSObject <CarUtilities, CarCleaning> +// NOTE: If two or more protocols rely on each other, make sure to forward-declare them: +#import "Brother.h" + +@protocol Brother; // Forward-declare statement. Without it, compiler would through error. + +@protocol Sister <NSObject> + +- (void)beNiceToBrother:(id <Brother>)brother; + +@end +// See the problem is that Sister relies on Brother, and Brother relies on Sister. +#import "Sister.h" + +@protocol Sister; // These lines stop the recursion, resolving the issue. + +@protocol Brother <NSObject> + +- (void)beNiceToSister:(id <Sister>)sister; + +@end /////////////////////////////////////// // Memory Management |