NSInvocationをカテゴリで拡張した

[NSObject valueForKey:(NSString*)inKey]はプリミティブ型をNSNumberやNSValueでラップする。

つまりKVCを使うと、ラップ済みのオブジェクトを取得する事が出来る。ただし、この場合呼出すメソッドに引数が有ってはならない。

引数がある場合はどうするか?NSInvocationを使えば出来る。

但し、[NSInvocation getReturnValue:(void*)inPtr]で取得出来る返り値の型(void*)だ。 この返り値の型は、[NSInvocation methodReturnType]で取得出来る。当然プリミティブ型も含まれる。

この2つの値を参照してObject型を返すようなメソッドを作った。

@implementation NSInvocation (MTLAddition)

- (id) objectWrappedReturnValue
{
    NSMethodSignature* theSignature = [self methodSignature];
    const char* theReturnType   = [theSignature methodReturnType];

    void* thePtr = malloc([theSignature methodReturnLength]);

    [self getReturnValue:thePtr];

    id theResult = nil;

    if( 0 == strcmp(theReturnType, @encode(id)) )
    {
        theResult = *(id*)thePtr;
    }
    else if( 0 == strcmp(theReturnType, @encode(Class)) )
    {
        theResult = *(Class*)thePtr;
    }
    else if( 0 == strcmp(theReturnType, @encode(char)) )
    {
        theResult = [NSNumber numberWithChar:*(char*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(unsigned char)) )
    {
        theResult = [NSNumber numberWithUnsignedChar:*(unsigned char*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(short)) )
    {
        theResult = [NSNumber numberWithShort:*(short*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(unsigned short)) )
    {
        theResult = [NSNumber numberWithUnsignedShort:*(unsigned short*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(int)) )
    {
        theResult = [NSNumber numberWithInt:*(int*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(unsigned int)) )
    {
        theResult = [NSNumber numberWithUnsignedInt:*(unsigned int*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(long)) )
    {
        theResult = [NSNumber numberWithLong:*(long*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(unsigned long)) )
    {
        theResult = [NSNumber numberWithUnsignedLong:*(unsigned long*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(long long)) )
    {
        theResult = [NSNumber numberWithLongLong:*(long long*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(unsigned long long)) )
    {
        theResult = [NSNumber numberWithUnsignedLongLong:*(unsigned long long*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(float)) )
    {
        theResult = [NSNumber numberWithFloat:*(float*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(double)) )
    {
        theResult = [NSNumber numberWithDouble:*(double*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(BOOL)) )
    {
        theResult = [NSNumber numberWithBool:*(BOOL*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(NSInteger)) )
    {
        theResult = [NSNumber numberWithInteger:*(NSInteger*)thePtr];
    }
    else if( 0 == strcmp(theReturnType, @encode(NSUInteger)) )
    {
        theResult = [NSNumber numberWithUnsignedInteger:*(NSUInteger*)thePtr];
    }
    else
    {
        theResult =  [NSValue valueWithBytes:thePtr
                                    objCType:theReturnType];
    }

    free(thePtr);

    return theResult;
}

@end

コードを見れば判るようにid型、Class型、NSNumberでサポートしているプリミティブ型の三つしか対応していない。

それ以外は、[NSValue valueWithBytes:(void*)inPtr objCType:(const char*)inType]を使っている。

かなりおおざっぱな実装だが、私の用途には十分だった。ポインター型、構造体、配列型、blocks型等も実装するべきなのだろうが、やり方が判らなかった。