有时我想返回一个数组,我知道应该做什么让调用者创建数组并修改方法中的数组。但事实证明这有效吗?
@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应用程序。
答案 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