返回固定大小的数组

时间:2013-05-23 09:13:53

标签: objective-c arrays clang

有时我想返回一个数组,我知道应该做什么让调用者创建数组并修改方法中的数组。但事实证明这有效吗?

@interface Test : NSObject
@end

@implementation Test

- (CGPoint[2])test {
    CGPoint p1 = {1, 2};
    CGPoint p2 = {3, 4};
    return (CGPoint[2]) {p1, p2};
}

- (int[2])test2 {
    int i = 1;
    int i2 = 2;
    return (int[2]){i, i2};
}

- (int[5])test3 {
    int i = 1;
    int i2 = 2;
    return (int[5]){i, i2, 3, 4, 5};
}

@end

@implementation testTests

- (void)testExample
{
    Test *t = [Test new];
    CGPoint p = [t test][0];
    CGPoint p2 = [t test][1];
    CGPoint *ps = [t test];
    CGPoint p3 = ps[0];
    CGPoint p4 = ps[1];
    NSLog(@"%@ %@ %@ %@", NSStringFromCGPoint(p), NSStringFromCGPoint(p2), NSStringFromCGPoint(p3), NSStringFromCGPoint(p4));
    // {1, 2} {3, 4} {1, 2} {3, 4}

    {
        Method m = class_getInstanceMethod([Test class], @selector(test));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [2{CGPoint=ff}] 16
    }

    {
        Method m = class_getInstanceMethod([Test class], @selector(test2));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [2i] 8
    }

    {
        Method m = class_getInstanceMethod([Test class], @selector(test3));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [5i] 20
    }
}

@end

所以看起来我可以直接返回一个类似于返回结构的数组。正如NSGetSizeAndAlignment所建议的那样,返回值实际上是整个数组而不仅仅是指针。 (CGPoint[2]为16,int[2]为8,int[5]为20)

但要实际使用返回的值,我只能这样做

CGPoint *ps = [t test];

这里ps只是一个指针。所以问题是我能安全返回这样的数组吗?分配在哪里的数组?在被叫函数堆栈中还是在调用函数堆栈中?是的,整个数组是从callee函数复制还是像struct一样,编译器会自动在调用函数中分配数组?

我正在使用Xcode 4.6.2


更新

看起来这只适用于32位应用程序,包括iOS应用程序(ARM),iOS模拟器(x86)和32位OSX应用程序。

1 个答案:

答案 0 :(得分:1)

C不支持返回数组。为什么这显然适合你,我不知道。我输入了你的代码,添加了缺少的方法/导入,修复了格式警告,调整了第一个NSLog - 没有一个以任何重要方式更改代码 - 结果是:

p: {0.000000, 0.000000}
p2: {49923903424063506281151950574939686583391145211556805465272890158614887709849874883833083042335634395057539888815696938380880844447770099086084465717389090095104.000000, 5053651901346670085892443969395884608467030378650281488576308318306304.000000}
p3: {0.000000, 0.000000}
p4: {0.000000, 0.000000}

简而言之,垃圾 - 这是你所期望的。

使用Apple LLVM默认编译器,使用10.8.4上的Xcode 4.6.2。切换到gcc-llvm编译器,它甚至不会编译 - 错误是方法无法返回数组。为了完整性,还测试了C11语言选项(Apple LLVM) - 如上所述运行并生成垃圾。

如果你真的需要按值传递数组,你可以使用一个简单的技巧:原始类型(int,float等)和结构都是通过C值传递的,所以你可以通过将数据包装在struct

中来按值传递数组
typedef struct
{
   CGPoint values[2];
} Array2;

@interface Test2 : NSObject
@end

@implementation Test2

- (Array2)test
{
   CGPoint p1 = {1, 2};
   CGPoint p2 = {3, 4};
   return (Array2) {{p1, p2}};
}


- (void)testExample
{
   Test2 *t = self;
   CGPoint p = [t test].values[0];
   CGPoint p2 = [t test].values[1];
   Array2 ps = [t test];
   CGPoint p3 = ps.values[0];
   CGPoint p4 = ps.values[1];
   NSLog(@"p: %@\np2: %@\np3: %@\np4: %@", NSStringFromCGPoint(p), NSStringFromCGPoint(p2), NSStringFromCGPoint(p3), NSStringFromCGPoint(p4));
}

@end

这会产生:

p: {1.000000, 2.000000}
p2: {3.000000, 4.000000}
p3: {1.000000, 2.000000}
p4: {3.000000, 4.000000}

应该如此。

附录 - 对评论的回应

C标准不允许返回数组(和函数) - 但允许引用数组(和函数)。

对于函数参数,如果给定的类型是数组(或函数)类型,则将其调整为对数组(或函数)的引用。

虽然我不知道标准声明它,但是一些编译器(例如Apple 4.2,但不是GCC 4.2)也将参数类型调整应用于返回类型 - 所以你可以声明一个函数来返回一个数组,它被调整为对数组的引用。

Objective-C的元数据正在记录声明的,而不是已调整的类型。这就是您在调用method_copyReturnType / NSGetSizeAndAlignment时看到的结果的原因。但是,编译遵循标准,调整类型,而不是按值返回数组。

虽然说编译器当然可以选择实现逐个数组作为扩展 - 因为这样做的机制需要在那里支持逐个值的结构。我不知道是否有任何编译器支持这样的扩展。我只测试了4.2英特尔编译器(Apple& GCC)而他们没有(并且GCC不支持将返回类型声明为数组,这是编译时错误)。

HTH