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型等も実装するべきなのだろうが、やり方が判らなかった。

保険金Getだぜ!!

保険屋さんから書類が来た。この間の雪で壊れた雨どいの修理費の書類。

築30年ごえのボロ屋に住んでる貧乏人にとっては、この手の火災保険の保険金は有り難い。

毎年の支払額を考えると損するのだろうが、修理費が定額になるので計画が立て易くなるのが利点。

なお、修理費の見積もりが終わってないので本当はまだ保険金はゲット出来ていません。

Cのマクロでは部分文字列は出来ない

ObjC上で、似たようなコードを沢山書いていたのでマクロを使おうとした。

id objectWrappingFrom(void* inPtr, const char* inType)
{
    id theResult = nil;

    if( strcmp(@encode(char), inType) )
    {
        theResult = [NSNumber numberWithChar:*(char*)inPtr];
    }
    else if( strcmp(@encode(unsigned char), inType) )
    {
        theResult = [NSNumber numberWithUnsignedChar:*(unsigned char*)inPtr];
    }// こんなのが沢山
    .
    .
    .
    .
    else
    {
        theResult =  [NSValue valueWithBytes:inPtr
                                    objCType:inType];
    }

    return theResult;
}

NSNumberのメソッド名中の型名、@encode中の型名、キャストの型名が微妙に違う。 メソッド名の型名だけcamelCaseになっている。

これをマクロで記述しようと一日中頑張ったが私の能力では無理だった。

Cのマクロでは文字列から部分文字列を取り出せないらしい。 文字列の結合は##で出来るが、文字列を切り分ける方法が無い。

1/18日追記

上のコードは思いきり間違い。

strcmpは文字列が等しい時に0を返すので、

id objectWrappingFrom(void* inPtr, const char* inType)
{
    id theResult = nil;

    if( strcmp(@encode(char), inType) == 0) // <--- コレ
    {
        theResult = [NSNumber numberWithChar:*(char*)inPtr];
    }
    .
    .
    .

としなければならない。

筋肉痛

昨日の雪かきで体中が痛い。普段の運動不足を痛感する。

雨どいの破損の保険は査定が来るとの事。

今日のお仕事の成果は無し。