Target.m
author Jens Alfke <jens@mooseyard.com>
Sat May 17 13:14:48 2008 -0700 (2008-05-17)
changeset 9 823e7e74088e
parent 7 59addced5e2a
child 11 e5976864dfe9
permissions -rw-r--r--
* Assert macros now put the failure code in a separate segment.
* Added $string utility.
     1 //
     2 //  Target.m
     3 //  MYUtilities
     4 //
     5 //  Created by Jens Alfke on 2/11/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "Target.h"
    10 
    11 
    12 @implementation MYTarget
    13 
    14 
    15 - (id) initWithReceiver: (id)receiver action: (SEL)action
    16 {
    17     self = [super init];
    18     if( self ) {
    19         NSMethodSignature *sig = [receiver methodSignatureForSelector: action];
    20         CAssert(sig,@"%@<%p> does not respond to %@",[receiver class],receiver,NSStringFromSelector(action));
    21         CAssert(sig.numberOfArguments==3,
    22                @"-[%@ %@] can't be used as a target because it takes >1 param",
    23                [receiver class],NSStringFromSelector(action));
    24         CAssert(0==strcmp([sig getArgumentTypeAtIndex: 2],"@"),
    25                @"-[%@ %@] can't be used as a target because it takes a non-object param",
    26                [receiver class],NSStringFromSelector(action));
    27         NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sig];
    28         inv.target = receiver;
    29         inv.selector = action;
    30         _invocations = [inv retain];
    31     }
    32     return self;
    33 }
    34 
    35 + (MYTarget*) targetWithReceiver: (id)receiver action: (SEL)action
    36 {
    37     return [[[self alloc] initWithReceiver: receiver action: action] autorelease];
    38 }
    39 
    40 - (void) dealloc
    41 {
    42     [_invocations release];
    43     [super dealloc];
    44 }
    45 
    46 
    47 - (NSArray*) invocations
    48 {
    49     NSMutableArray *invocations = $castIf(NSMutableArray,_invocations);
    50     if( ! invocations )
    51         invocations = [NSMutableArray arrayWithObject: _invocations];
    52     return invocations;
    53 }
    54 
    55 
    56 - (NSString*) description
    57 {
    58     NSMutableString *desc = [NSMutableString stringWithFormat: @"%@{", self.class];
    59     BOOL first = YES;
    60     for( NSInvocation *inv in self.invocations ) {
    61         if( first )
    62             first = NO;
    63         else
    64             [desc appendString: @", "];
    65         [desc appendFormat: @"-[%@ %s]", [inv.target class], inv.selector];
    66     }
    67     [desc appendString: @"}"];
    68     return desc;
    69 }
    70 
    71 
    72 static BOOL equalInvocations( NSInvocation *a, NSInvocation *b )
    73 {
    74     return a.target==b.target && a.selector==b.selector;
    75 }
    76 
    77 
    78 - (BOOL) isEqual: (MYTarget*)t
    79 {
    80     if( ! [t isKindOfClass: [self class]] )
    81         return NO;
    82     if( [_invocations isKindOfClass: [NSInvocation class]] && [t->_invocations isKindOfClass: [NSInvocation class]] )
    83         return equalInvocations(_invocations,t->_invocations);
    84     NSArray *myInvocations = self.invocations, *itsInvocations = t.invocations;
    85     unsigned n = myInvocations.count;
    86     if( n != itsInvocations.count )
    87         return NO;
    88     for( int i=0; i<n; i++ )
    89         if( ! equalInvocations( [myInvocations objectAtIndex: i],
    90                                 [itsInvocations objectAtIndex: i] ) )
    91             return NO;
    92     return YES;
    93 }
    94 
    95 
    96 - (void) addTarget: (MYTarget*)target
    97 {
    98     setObj(&_invocations,[self invocations]);
    99     [_invocations addObjectsFromArray: target.invocations];
   100 }
   101 
   102 
   103 static id invokeSingleTarget( NSInvocation *invocation, id param )
   104 {
   105     id result = nil;
   106     if( invocation && invocation.target ) {
   107         [invocation retain];
   108         @try{
   109             [invocation setArgument: &param atIndex: 2];
   110             [invocation invoke];
   111             
   112             NSMethodSignature *sig = invocation.methodSignature;
   113             NSUInteger returnLength = sig.methodReturnLength;
   114             if( returnLength==0 ) {
   115                 result = nil; // void
   116             } else {
   117                 const char *returnType = sig.methodReturnType;
   118                 if( returnType[0]=='@' ) {
   119                     [invocation getReturnValue: &result];
   120                 } else {
   121                     UInt8 returnBuffer[returnLength];
   122                     [invocation getReturnValue: &returnBuffer];
   123                     result = [NSValue valueWithBytes: &returnBuffer objCType: returnType];
   124                 }
   125             }
   126         }@finally{
   127             [invocation release];
   128         }
   129     }
   130     return result;
   131 }
   132 
   133 
   134 - (id) invokeWithSender: (id)sender
   135 {
   136     id result;
   137     NSMutableArray *invocations = $castIf(NSMutableArray,_invocations);
   138     if( invocations ) {
   139         result = nil;
   140         for( NSInvocation *invocation in invocations )
   141             result = invokeSingleTarget(invocation,sender);
   142     } else {
   143         result = invokeSingleTarget(_invocations,sender);
   144     }
   145     return result;
   146 }
   147 
   148 
   149 @end