_Dynamic Design Patterns in Objective-C_ by William Grosso Listing One // From the header file -- define a pointer to member function for a method bool appBecameActiveCanBeCalled; id (* appBecameActive) (id, SEL, id); // From the .m (code) file (and heavily annotated) - (void) setDelegate: newDelegate { SEL tokenizedMethodName; // the @selector compiler directive tokenizes // the method name. // First, we check to see if we have a delegate and, if so, whether the new // delegate is from the same class; if both these are true, just return. if ((nil!=_internalDelegate) && ([newDelegate cisMemberOf: [_internalDelegate class])) { _internalDelegate = [newDelegate retain]; //delegate won't be freed return; } // We need to explicitly check which methods our new delegate has implemented. _internalDelegate = [newDelegate retain]; //ensure delegate won't be freed tokenizedMethodName=@selector(appBecameActive:); if (YES==[ newDelegate respondsTo: tokenizedMethodName]) { // syntax is [object memberFunction: argumentOne tag: argument2 ...]; We now // know that the new delegate object implements appBecameActive. We will get // function pointer (for speed, might be calling this function often and // and therefore want to avoid rebinding the selector). Usually, we wouldn't // get the function pointer. AppBecameActive=(id (*) (id, SEL, id) [_internalDelegate methodFor: tokenizedMethodName]; appBecameActiveCanBeCalled=YES; } else { appBecameActiveCanBeCalled =NO; } // .... } Listing Two // code in main application - loadObscureClassFromDisk { // Our goal is to find a requried class object (which we will return) Class classThatsNeeded; // Has it been loaded ? Try to find the class object using a wrapper // function which encapsulates the run-time function objc_lookUpClass(); classThatsNeeded =NXClassFromString("ObscureClassName"); If (nil== classThatsNeeded) { // No class object. We need to load it. // Build a path name, relative to where application was installed by // concatenating a subdirectory name onto [[NXApp mainBundle] directory] char * fullPathDirectory; .... NXBundle * bundleForClass =[[NXBundle alloc] initForDirectory: fullPathDirectory]; classThatsNeeded=[ bundleForClass classNamed:"ObscureClassName"]; } return classThatsNeeded; } Listing Three // code in an element - (bool) accept: aVisitor { // Protocols are like interfaces in javathey define a set of methods. // Objects, when declared (in their header files), inherit from one concrete // class and multiple protocols. This accept method checks whether the // visitor conformsTo (inherits from) a certain protocol declared earlier // or if the visitor is of a particular class. if (([aVisitor conformsTo: MyProtocol]) !! ([aVisitor isKindOf: acceptableClass])) { return [aVisitor visit: self]; } return NO; } // code in a visitor - visit: anElement { // again, do type checking. Check whether element is an instance of a // specific class; if that fails, whether it inherits from some other class. // classOne and classTwo are declared previously. if ([anElement isMemberOf: classOne]) { // perform some sequence of actions } else if ([anElement isKindOf: classTwo]) { // perform some sequence of actions } return NO; } Listing Four template CommandObject { private: T& target void (T::memberFunctionToCall)(); public: CommandObject(T& theTarget, void (T::theMemberFunctionToCall)()); void Do(); } // comment: ask Don to finish this. Listing Five // headers -- Serializer implements - (void) writeInt: (id) value; - (void) writeFloat: (id) value; - (void) writeDouble: (id) value - (void) writeChar: (id) value - (void) writeObject: (id) value // Serializable implements - (void) serializeUsing: (Serializer *) serializerToUse; - (void) initWithDeserializer: (Deserialier *) deserializerWithMyState; // Serializer implements - (int) readInt; - (float) readFloat; - (double) readDouble; - (char) readChar; - (id) readObject; // from Serializer's Code - (void) writeObject: (id) value { if (YES==[self objectAlreadyWritten: value]) { // The object thas already been serialized. We just store the // internal uuid (so we can rebuild the object graph). [self storeReferenceToObject: value]; } else { // first log that we're storing the object. [self createReferenceFor: value]; [self writeString: NSStringFromClass([[value class])]; [value serializeUsing: self]; } } // from Deserializer's code - (id) readObject { // .... // Here we need to exhume a previously unknown object. Class classObjectForValue; id value; classObjectForValue = NSClassFromString([self readNextString]); if (nil!= classObjectForValue) { value=[[ classObjectForValue alloc] initFromSerializer: self]; } else { // Bad thing happened here. Raise an exception because our data is corrupt. // ... } return value; } Listing Six // header (.h) file #import @interface CommandObject: NSObject { id target; id argument; NSString * nameOfMethod; SEL selectorForMethod; } - (void) dealloc; - init; - (void) setArgumentObject: newArgument; - (void) setTargetObject: newTarget; // Function overloading would make the next two methods more pleasant. - (void) setTargetMethodByString: (NSString *) methodName; - (void) setTargetMethodBySelector: (SEL) methodSelector; - (bool) do; - (void) initWithCoder: (NSCoder *) decoder; - (void) encodeWithCoder: (NSCoder *) encoder; @end // code (.m) file #import "CommandObject.h" @implementation CommandObject - (void) dealloc { // retain, release, and autorelease are all part of reference counting. // ref counting is in NEXTSTEP, but not part of the Objective-C spec. // When an object's ref count goes to zero, it is deallocated. [target autorelease]; [argument autorelease]; [nameOfMethod autorelease]; [super dealloc]; return; } - init { // init is like a constructor. The object has already been // allocated and needs to have its variables initialized. [super init]; nameOfMethod=argument=target=nil; //always safe cause messages to nil //don't cause problems. return self; //note that the return value of init has to be //specified, unlike the return value from a //C++ constructor. } - (void) setArgumentObject: newArgument { if (nil!=argument) { [argument autorelease]; } argument=newArgument; [argument retain]; return; } - (void) setTargetObject: newTarget { if (nil!=target) { [target autorelease]; } target=newTarget; [target retain]; return; } - (void) setTargetMethodByString: (NSString *) methodName { nameOfMethod = [methodName copy]; //make our own copy //to guard against changes selectorForMethod= NSSelectorFromString (nameOfMethod); return; } - (void) setTargetMethodBySelector: (SEL) methodSelector { selectorForMethod= methodSelector; nameOfMethod= [NSStringFromSelector (selectorForMethod) retain]; return; } - (bool) do { if (YES==[target respondsTo: selectorForMethod]) { [target perform: selectorForMethod]; return YES; } return NO; } - (void) initWithCoder: (NSCoder *) decoder { // From target, object, and nameOfMethod, we can get selector back (in fact, // we'll call function to do so). Hence, object graph can be fully restored. // .... } - (void) encodeWithCoder: (NSCoder *) encoder { // We write out three objects--target, argument,and nameOfMethod. // this is just the "serialize" method discussed earlier. // ... } @end