2013-05-23 2 views
1

Иногда я хочу вернуть массив чего-то, я знаю, что я должен сделать, чтобы вызывающий мог сделать массив и модифицировать массив в методе. Но, оказывается, это работает?return fixed size array

@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, возвращаемое значение на самом деле представляет собой весь массив, а не только указатель. (16 для CGPoint[2], 8 для int[2], 20 для int[5])

Но на самом деле использовать возвращаемое значение, я могу сделать это только

CGPoint *ps = [t test]; 

и здесь ps это просто указатель. Итак, вопрос в том, могу ли я безопасно возвращать массив вроде этого? где находится массив, выделенный в? В стеке функции вызова или в стеке функции звонящего? Верно ли, что весь массив скопирован из функции callee или как struct complier распределяет массив в функции вызывающего абонента автоматически?

Я использую Xcode 4.6.2


Update

Похоже, это только ковшики для 32бит приложение, которое включает в себя IOS приложение (ARM), IOS симулятор (x86) и 32-битный OSX приложение.

+0

Пожалуйста, конденсируйте код, чтобы он просто показывал вашу проблему. Не ожидайте, что люди пройдут через десятки строк в поисках базового вопроса, подобного этому. –

+0

@ смысл-имеет значение моя проблема: могу ли я сделать CGPoint * ps = [t test]; ', это недостаточно ясно? –

ответ

1

C не поддерживает возврат массива. Почему это, по-видимому, работает для вас, я понятия не имею. Я вошел код, добавил недостающие методы/импорт, исправлены предупреждения в формате, не отлажен первый NSLog - ни один из которых изменяет код какого-либо существенным образом - и результат:

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

Короче, фигня - что и следовало ожидать.

Это с Xcode 4.6.2 на 10.8.4 с использованием компилятора LLVM Apple LLVM. Переключитесь на компилятор gcc-llvm, и он даже не будет скомпилирован - ошибка в методах не может вернуть массивы. Для полноты также проверены варианты языка C11 (Apple LLVM) - выполняется и производит мусор, как указано выше.

Если вам действительно нужно передавать массивы по значению вы можете сделать это, используя простой трюк: примитивные типы (целый, дробный и т.д.) и структуры все принятые ценились в 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 компилятора Intel (Apple & GCC), и они этого не делают (и GCC не поддерживает объявление возвращаемого типа в качестве массива, то есть ошибки времени компиляции).

HTH

+0

Какой тип возврата вы получаете от 'method_copyReturnType' и какой размер? –

+0

@xlc - см. Добавление – CRD

+0

Почему это работает, вероятно, потому, что указатель, который содержит массив, все еще указывает на (теперь недействительную) память. Это неопределенное поведение, но это не значит, что он должен потерпеть крах. –