idとdoubleの相互変換

昨日はIvarの取得を行うコードに言及したが、同じようにIvarの設定を行う関数はruntimeのソースコードを元に以下のようなコーディングにした。

Ivar _object_setInstanceVariable(id obj, const char *name, void *value)
{
    Ivar ivar = NULL;
    
    if (obj && name) {
        if ((ivar = class_getInstanceVariable(object_getClass(obj), name))) {
            object_setIvar(obj, ivar, (__bridge id)value);
        }
    }
    return ivar;
}

昨日書いたように、void*からidへのキャストには__bridge修飾子を追加している。

リファレンスのコードだったが、残念だがこれでは上手く行かない。テストしてみればわかるのだが、ivarにint型の値を設定した呼び出しだとテストが通るのだが、double等の実数だと想定したように値を読み書きできないのである。

IvarTest.m
:
+ (void)setIvarAsInt:(id)target forName:(NSString *)name forValue:(int)newValue
{
    _object_setInstanceVariable(target, [name UTF8String], *(void**)&newValue);
}
+ (void)setIvarAsDouble:(id)target forName:(NSString *)name forValue:(double)newValue
{
    _object_setInstanceVariable(target, [name UTF8String], *(void**)&newValue);
}
// intはOK
[[self class] setIvarAsInt:self forName:@"ivarInt" forValue:9999"];
STAssertTrue([self ivarInt] == 9999, nil);
// doubleはNG
[[self class] setIvarAsDouble:self forName:@"ivarDouble" forValue:1234.5678"];
STAssertTrue([self ivarDouble] == 1234.5678, nil);

intとdoubleで扱いが違うようだ。
とここで、Mac OS X/Lionでそれぞれの型が持つサイズをダンプしてみよう。

printf("Size of int: %lubytes\n", sizeof(int));
printf("Size of double: %lubytes\n", sizeof(double));
printf("Size of void *: %lubytes\n", sizeof(void *));
printf("Size of id: %lubytes\n", sizeof(id));
実行結果
Size of int: 4bytes
Size of double: 8bytes
Size of void *: 4bytes
Size of id: 4bytes

なるほど。