1. Qu’est-ce que la délégation?
La délégation est un système présent dans plusieurs classes du framework Cocoa, sous Mac OS X (et donc également sur iPhone OS).
Ce système permet aux dévelopeurs d’applications Cocoa d’interagir en fonction d’événement précis liés aux fonctionnements intrinsèques des objets Cocoa.
Prenons par exemple l’objet NSWindow, qui comme son nom l’indique permet d’afficher et de contrôler une fenêtre.
Cet objet fenêtre possède un certain nombre de méthode, comme par exemple ‘close’, ou ‘open’, permettant respectivement d’ouvrir et de fermer le fenêtre en question.
Lors de la programmation d’une application Cocoa, il peut être fort utile d’être informé lors de l’ouverture ou de la fermeture d’une fenêtre, pour allouer ou libérer des ressources, terminer des tâches ou des threads, etc.
Le système de délégation du framework Cocoa permet ainsi d’attacher une instance d’un objet à un autre objet, le premier ayant la possibilité d’agir sur le second en fonction de ses différentes phases d’exécution.
La définition d’un objet délégué sur un autre objet se passe par convention par la méthode ‘setDelegate’, prenant en argument unique l’instance du délégué.
Par exemple, pour définir un objet de type ‘Foo’ comme délégué d’un objet NSWindow:
Foo * foo = [ [ Foo alloc ] init ]; NSWindow * window = [ [ NSWindow alloc ] initWithContentRect: NSMakeRect( 0, 0, 100, 100 ) styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: NO ]; [ window setDelegate: foo ];
Les deux premières lignes créent respectivement un objet de type ‘Foo’ (défini dans notre application), et un objet de type ‘NSWindow’ (du framework Cocoa).
[ window close ];
- ( void )windowWillClose: ( NSNotification * )notification;
2. Comment fonctionne la délégation?
@interface NSWindow: NSObject { @protected id delegate; } - ( id )delegate; - ( void )setDelegate: ( id )object; @end @implementation NSWindow - ( id )delegate { return delegate; } - ( void )setDelegate: ( id )object { delegate = object; } @end
@property( nonatomic, assign, readwrite ) delegate;
@synthesize delegate;
- ( void )close { // Do something... if( [ delegate respondsToSelector: @selector( windowWillClose ) ] ) { [ delegate windowWillClose ]; } // Do something... }
Si c’est le cas, elle l’exécute, et poursuit sa propre exécution.
3. Délégation et notification
[ [ NSNotificationCenter defaultCenter ] addObserver: foo selector: @selector( myObserverMethod: ) name: NSWindowWillCloseNotification object: window ]:
- ( void )myObserverMethod: ( NSNotification * )notification;
Ce qu’il faut bien comprendre, c’est que le système de notification permet uniquement d’être averti de certains événements, alors que le système de délégation permet en plus de modifier le comportement de l’objet concerné.
- ( BOOL )windowShouldClose: ( NSWindow * )window;
4. Chaîne de délégation
[ window setDelegate: foo ]; [ window setDelegate: bar ];
5. Implémentation – MultipleDelegateObject
/* MultipleDelegateObject.h */ @interface MultipleDelegateObject: NSObject { @protected DelegateChain * delegate; } - ( void )addDelegate: ( id )object; - ( void )removeDelegate: ( id )object; - ( NSArray * )delegates; @end;
/* MultipleDelegateObject.m */ @implementation - ( id )init { if( ( self = [ super init ] ) ) { delegate = [ [ DelegateChain alloc ] init ]; } return self; } - ( void )dealloc { [ delegate release ]; [ super dealloc ]; } - ( void )addDelegate: ( id )object { [ delegate addDelegate: object ]; } - ( void )removeDelegate: ( id )object { [ delegate removeDelegate: object ]; } - ( NSArray * )delegates { return [ delegate delegates ]; } @end
6. Implémentation – DelegateChain
/* DelegateChain.h */ @interface DelegateChain: NSObject { @protected id * delegates; NSUInteger numberOfDelegates; NSUInteger sizeOfDelegatesArray; NSMutableDictionary * hashs; } - ( void )addDelegate: ( id )object; - ( void )removeDelegate: ( id )object; - ( NSArray * )delegates; @end
- ( id )init { if ( ( self = [ super init ] ) ) { hash = [ [ NSMutableDictionary dictionaryWithCapacity: 10 ] retain ]; if( NULL = ( delegates = ( id * )calloc( 10, sizeof( id ) ) ) ) { // Error management... } } return self; }
- ( void )dealloc { free( delegates ); [ hashs release ]; [ super dealloc ]; }
- ( void )addDelegate: ( id )object { NSString * hash; if( object == nil ) { return; } if( numberOfDelegates == sizeOfDelegatesArray ) { if( NULL == ( delegates = ( id * )realloc( delegates, ( sizeOfDelegatesArray + 10 ) * sizeof( id ) ) ) ) { // Error management... } sizeOfDelegatesArray += 10; } hash = [ [ NSNumber numberWithUnsignedInteger: ( NSUInteger )object ] stringValue ]; if( [ hashs objectForKey: hash ] != nil ) { return; } delegates[ numberOfDelegates ] = object; [ hashs setObject: [ NSNumber numberWithUnsignedInteger: numberOfDelegates ] forKey: hash ]; numberOfDelegates++; }
- ( void )removeDelegate: ( id )object { NSString * hash; NSUInteger index; NSUInteger i; if( object == nil || numberOfDelegates == 0 ) { return; } hash = [ [ NSNumber numberWithUnsignedInteger: ( NSUInteger )object ] stringValue ]; if( [ hashs objectForKey: hash ] == nil ) { return; } index = [ [ hashs objectForKey: hash ] unsignedIntegerValue ]; for( i = index; i < numberOfDelegates - 1; i++ ) { delegates[ i ] = delegates[ i + 1 ]; } [ hash removeObjectForKey: hash ]; numberOfDelegates--; }
- ( NSArray * )delegates { NSUInteger i; NSMutableArray * delegatesArray; if( numberOfDelegates == 0 ) { return [ NSArray array ]; } delegatesArray = [ NSMutableArray arrayWithCapacity: numberOfDelegates ]; for( i = 0; i < numberOfDelegates; i++ ) { [ delegatesArray addObject: delegates[ i ] ]; } return [ NSArray arrayWithArray: delegatesArray ]; }
Il s’agit d’une simple boucle sur notre tableau de pointeur, qui ajoute les objets pointés dans un objet de type ‘NSArray’.
7. Runtime et re-routage des méthodes
if( [ delegate respondsToSelector: @selector( someMethod ) ] ) {}
- ( BOOL )respondsToSelector: ( SEL )selector { NSUInteger i; for( i = 0; i < numberOfDelegates; i++ ) { if( [ delegates[ i ] respondsToSelector: selector ] == YES ) { return YES; } } return NO; }
- ( NSMethodSignature * )methodSignatureForSelector: ( SEL )selector { NSUInteger i; for( i = 0; i < numberOfDelegates; i++ ) { if( [ delegates[ i ] respondsToSelector: selector ] == YES ) { return [ [ delegates[ i ] class ] instanceMethodSignatureForSelector: selector ]; } } return nil; }
- ( void )forwardInvocation: ( NSInvocation * )invocation { NSUInteger i; for( i = 0; i < numberOfDelegates; i++ ) { if( [ delegates[ i ] respondsToSelector: [ invocation selector ] ] == YES ) { [ invocation invokeWithTarget: delegates[ i ] ]; } } }
8. Conclusion
@interface NSObject( MyCategory ) - ( void )sayHello; @end @implementation NSObject( MyCategory ) - ( void )sayHello { NSLog( @"Hello world!" ); } @end
1 comment
Merci énormement pour cette article