Browse in : |
All
> Topics
> Programming
All > Journals > CVu > 171 Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: An Introduction to Objective-C
Author: Administrator
Date: 03 February 2005 13:16:10 +00:00 or Thu, 03 February 2005 13:16:10 +00:00
Summary:
Body:
Objective-C has a rich set of methods by which the contents and capabilities of an object can be queried. NSObject implements:
(Class)class returns the class object for the receiver's class.
(Class)superclass returns the class object for the class from which the receiver inherits.
(BOOL)isMemberOfClass:(Class)class returns YES if the argument to the method is an instance of the specified class.
(BOOL)isKindOfClass:(Class)class returns YES if the argument to the method is an instance of the specified class or of a class that inherits from it.
(BOOL)respondsToSelector:(SEL)aSelector returns YES if the receiving object is capable of handling a certain message.
There are also functions to query classes for their instance variables and class and instance methods, and methods can be queried for information about their arguments.
NeXT and Apple have extended the language specified by Cox in "Object-Oriented Programming, an Evolutionary Approach" with categories, protocols and, most recently, Java-style exception-handling, thread synchronisation and support for invoking methods in remote processes. The last two are considered too specialised to be dealt with in this article.
Categories add new functionality to an already existing class. They are particularly useful where you are using a third-party class library and you are not free to amend that library's source code. One solution to this problem is to derive a new class from the one you need to extend, but this may require detailed knowledge of the superclass, and inheritance notoriously breaks encapsulation. To create a category, you declare interface and implementation sections as shown in the pseudocode below:
In the header file, CategoryName.h:
#import "ClassName.h" @interface ClassName (CategoryName) method declarations @end
In CategoryName.m:
#import "CategoryName.h" @implementation ClassName (CategoryName) method definitions @end
A class has a size that is fixed at compilation time, so it is not possible to add instance variables to an existing class in this manner; the only way to do this is to use inheritance.
The file StringTokenizer.m contains the following lines:
// Create a category to forward-declare private // method in order to avoid compiler warnings about // undeclared methods. @interface StringTokenizer (Private) - (void)skipDelimiters; @end
Since -skipDelimiters is not meant to be directly accessible to the users of a class, it would be inappropriate to declare it in StringTokenizer.h, and so I have created a category in the implementation file to contain declarations of private methods. This is not strictly necessary, as an Objective-C compiler emits a warning, not an error, when it is required to compile a message to an undeclared method, and since the programmer knows that the method has been defined, the program would work perfectly well without such a category declaration.
Categories can also be used to split up the implementation of a class into separate units, with perhaps each having its own implementation file; this would facilitate the development of classes to which more than one programmer contributes. They can also be used to declare informal protocols, of which more below.
Protocols involve the declaration of a list of methods whose implementation is deferred to any class that chooses to implement them. If a class adopts an informal protocol, it can choose which methods to implement, whereas with a formal protocol, implementations of all the methods listed must reside either in the class itself or in its superclasses. This is a way of associating classes that share similar behaviour but are not closely related in the inheritance hierarchy.
There is little language support for informal protocols, but in the Foundation framework, informal protocols are often declared as a category of the root class, NSObject. Here is the list of methods in GNUStep's version of Foundation for the informal protocol NSKeyValueCoding, which defines a mechanism in which the properties of an object are accessed indirectly by name (or key), rather than directly through invocation of an accessor method or as instance variables:
@interface NSObject (NSKeyValueCoding) + (BOOL) accessInstanceVariablesDirectly; + (BOOL) useStoredAccessor; - (id) handleQueryWithUnboundKey: (NSString*)aKey; - (void) handleTakeValue: (id)anObject forUnboundKey: (NSString*)aKey; - (id) storedValueForKey: (NSString*)aKey; - (void) takeStoredValue: (id)anObject forKey: (NSString*)aKey; - (void) takeStoredValuesFromDictionary: (NSDictionary*)aDictionary; - (void) takeValue: (id)anObject forKey: (NSString*)aKey; - (void) takeValue: (id)anObject forKeyPath: (NSString*)aKey; - (void) takeValuesFromDictionary: (NSDictionary*)aDictionary; - (void) unableToSetNilForKey: (NSString*)aKey; - (id) valueForKey: (NSString*)aKey; - (id) valueForKeyPath: (NSString*)aKey; - (NSDictionary*) valuesForKeys: (NSArray*)keys; @end
Any object that derives from NSObject can select from this list which methods it needs to implement in order to acquire appropriate key-value coding functionality.
Formal protocols are enforced by the language. They are declared as in the following pseudocode:
@protocol ProtocolName method declarations @end
Here is a declaration for the NSCoding protocol for the serialisation ('flattening') and deserialisation (reconstruction) of objects associated with archiving from disk or some other form of distribution to another address space.
@protocol NSCoding - (void) encodeWithCoder: (NSCoder*)aCoder; - (id) initWithCoder: (NSCoder*)aDecoder; @end
If the class Person needed to be stored on disk, it would adopt the NSCoding protocol:
#import <Foundation/Foundation.h> @interface Person : NSObject <NSCoding> { NSString *name; NSString *address; } // Accessor methods - (NSString *)name; - (NSString *)address; - (void)setName:(NSString *)aName; - (void)setAddress:(NSString *)anAdress; // Other methods ... @end @implementation Person // Accessor methods - (NSString *)name {return name;} - (NSString *)address {return address;} - (void)setName:(NSString *)aName { [aName retain]; [name release]; name = aName; } - (void)setAddress:(NSString *)anAdress { [anAddress retain]; [address release]; address = anAdress; } // NSCoding methods - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder:coder]; [aCoder encodeObject:name]; [aCoder encodeObject:address]; } - (id) initWithCoder: (NSCoder*)aDecoder; { self = [super initWithCoder:coder]; name = [[coder decodeObject] retain]; address = [[coder decodeObject] retain]; return self; } // Called when the object is deallocated - (void) dealloc {[name release]; [address release]} @end
Formal protocols are equivalent to interfaces in Java; indeed, the designers of Java have copied this idea from Objective-C. Assuming that Foundation had been implemented in C++ you would write something like the following abstract class definition:
class NSObject; class NSCoding { virtual void encodeWithCoder(NSCoder& aCoder) = 0; virtual NSObject* decodeWithCoder( const NSCoder& aCoder) = 0; }; class Person : public NSObject, public NSCoding { NSString *name_, *address_; public: // Accessor functions NSString* name(); void setName(const NSString* aName); NSString* address(); void setAddress(const NSString* anAddress); // NSCoder virtual functions void encodeWithCoder(NSCoder& aCoder); NSObject* decodeWithCoder(const NSCoder& aCoder); // Other functions ... // Called when the object is deallocated virtual ~Person(); };
The implemention of these methods in C++ is left to the reader's imagination.
Unlike C++, neither Objective-C nor Java implements multiple inheritance, and so these languages need a separate mechanism for adopting protocols.
It cannot always be known at run-time whether a particular object implements a formal protocol; it can be tested in the following way:
if([anObject conformsTo:@protocol(NSCoding)]) [anObject encodeWithCoder:myCoder];
Simple exception-handling code could be written as follows;
Cup *cup = [[Cup alloc] init]; @try { [cup fill]; } @catch (NSException *exception) { NSLog(@"main: Caught %@: %@", [exception name], [exception reason]); } @finally { [cup release]; }
Code that might throw an exception is enclosed within a @try block, and the exception should be caught in a @catch block. A @finally block contains code that must be executed whether an exception is thrown or not.
Cup's fill method might throw an exception like this:
NSException *exception = [NSException exceptionWithName:@"HotTeaException" reason:@"The tea is too hot" userInfo:nil]; @throw exception;
ny kind of Objective-C object can be thrown.
An exception can be re-thrown by means of @throw without an argument.
Notes:
More fields may be available via dynamicdata ..