ObjC NSNumber存储指向Object的指针而不是返回的Value

时间:2014-12-20 01:25:41

标签: objective-c ios8 nsnumber

我编写了自己的Objective C类来处理2D向量。

@interface Vector2D : NSObject {
    struct _vecdata {
        double x;
        double y;
        double x_norm;
        double y_norm;
    } _data;
}

@property(nonatomic) double x; //custom getter + setter
@property(nonatomic) double y; //custom getter + setter
@property(nonatomic, readonly) double xNormed; //custom getter
@property(nonatomic, readonly) double yNormed; //custom getter

+ (id)vectorWithX:(double)x_ext andY:(double)y_ext;
- (id)initWithX:(double)x_ext andY:(double)y_ext;

[...]
- (double)length;

@end

#import <math.h>
@implementation Vector2D

+ (id)vectorWithX:(double)x_ext andY:(double)y_ext{
    return [[self alloc] initWithX:x_ext andY:y_ext];
}

- (id)initWithX:(double)x_ext andY:(double)y_ext {
    if ((self = [super init])) {
        _data.x = x_ext;
        _data.y = y_ext;
        [self normalize];
    }
    return self;
}

- (void)setX:(double)x {
    _data.x = x;
    [self normalize];
}

- (double)x {
    return _data.x;
}

- (double)xNormed {
    return _data.x_norm;
}

//setters and getter for y omitted (same as for x)

[...]

- (void)normalize {
    double abs = [self length];
    if (abs != 0) {
        _data.x_norm = _data.x/abs;
        _data.y_norm = _data.y/abs;
    }
}

- (double)length {
    return sqrt(_data.x*_data.x + _data.y*_data.y);
}

@end

现在我需要将调用的结果包装到对NSNumber的Vector2D实例长度调用。

NSNumber* aNSNum = @([[Vector2D vectorWithX:someXValue andY:someYValue] length]);

因为我稍后会显示aNSNum的值,所以我注意到,所有值(所有NSNumbers都是这样创建的)都在1.40537252E+14左右(因为我稍后会调用[aNSNum floatValue])。 所以我重新编译并重新运行应用程序,突然所有值都在4.73280427E+14附近。

所以我想知道,因为Vector的长度应该在0到20000的范围内,但不能更多。 我开始在调试器中玩,以了解发生了什么,并得到以下结果:

(lldb) po [[Vector2D vectorWithX:someXValue andY:someYValue] length]
0x0000000000000001

(lldb) po (double)[[Vector2D vectorWithX:someXValue andY:someYValue] length]
84.693565280958623

(lldb) po (unsigned long)[[Vector2D vectorWithX:someXValue andY:someYValue] length]
1

(lldb) po @([[Vector2D vectorWithX:someXValue andY:someYValue] length])
84.69356528095862

(lldb) po @((double)[[Vector2D vectorWithX:someXValue andY:someYValue] length])
84.69356528095862

(lldb) po @((unsigned long)[[Vector2D vectorWithX:someXValue andY:someYValue] length])
1

(lldb) po aNSNum
140537282189312

(lldb) po [aNSNum floatValue]
1.40537286E+14

(lldb) po (unsigned long)[aNSNum doubleValue]
<Vector2D: 0x7fd162c85800>

(lldb) po (double)[(unsigned long)[aNSNum doubleValue] length]
84.693565280958623

所以真正令人感兴趣的部分是关于倒数第二行。那么为什么指针地址存储在NSNumber中的Vector2D对象而不是调用-length的返回值的值?

从那时起,我尝试将伪造的代码行更改为以下变体:(没有成功)

  • NSNumber* aNSNum = @((double)[[Vector2D vectorWithX:someXValue andY:someYValue] length]);
  • NSNumber* aNSNum = @([((Vector2D *)[Vector2D vectorWithX:someXValue andY:someYValue]) length]);
  • NSNumber* aNSNum = @((double)[((Vector2D *)[Vector2D vectorWithX:someXValue andY:someYValue]) length]);

到目前为止的工作如下:

static SEL lengthSEL;
static IMP lengthIMP;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    lengthSEL = @selector(length);
    lengthIMP = class_getMethodImplementation([Vector2D class], lengthSEL);
});
Vector2D* vec = [Vector2D vectorWithX:someXValue andY:someYValue];
double len = ((double (*) (id,SEL))lengthIMP)(vec,lengthSEL);
NSNumber* aNSNum = @(len);

但是希望有人可以帮助我把它带回一个班轮。或者有一个线索,它出错了......

1 个答案:

答案 0 :(得分:1)

编辑: 编译器因为返回类型

而感到困惑

+ (id)vectorWithX:(double)x_ext andY:(double)y_ext;

id;它不知道它将获得的length方法。将声明更改为

+ (instancetype )vectorWithX:(double)x_ext andY:(double)y_ext;

并观察有多好的东西。 请访问apple docs

了解详情

您依靠文字语法@(someNumber)来检测someNumber的类型并正确处理它,在本例中是length方法的(double)返回值。数字文字的规则是here

我认为安全的做法,而不是强制转换你的变量并希望编译器选择它,就是用显式输入创建数字,即

NSNumber* aNSNum = [NSNumber numberWithDouble:[[Vector2D vectorWithX:someXValue andY:someYValue] length]];    

NSNumber文字对于常量等来说是很好的简写,但这似乎是一种使代码更难理解而不是更容易的情况。