Странные значения в переменной экземпляра массива int в Objective-C - PullRequest
2 голосов
/ 01 ноября 2011

Я искал похожие темы, но пока не повезло, так что вот так:

В классе Objective-C я объявил переменную экземпляра указателя int для хранения массива int:

@interface MyList : NSObject {
    int index;      // A simple int to hold an index reference
    NSString *name; // The name of the list
    int *bookList;  // A pointer to an int array that holds a list of numbers
}

@property (nonatomic) int index;
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int *bookList;

@end

Я проверил это следующим образом, и все данные, содержащиеся в переменных экземпляра, были правильно сохранены и отображены с помощью операторов NSLog:

MyList *aList = [[MyList alloc] init];
[aList setIndex:1];
[aList setName:@"ListOne"];
[aList setBookList:(int []){1, 2, 3, 0}];

NSLog(@"Show MyList object's data after object is populated");
NSLog(@"[%d]: %@", aList.index, aList.name);
for (int i = 0; i < 4; i++) {
    NSLog(@"bookList[%d] = %d", i, aList.bookList[i]);
}

Однако , когда я отправляю этот объект в качестве аргумента методу и пытаюсь напечатать содержимое массива int, я получаю странные числа, и то же самое происходит после возврата из метода :

-(void)displayMyList:(MyList *)theList {
    NSLog(@"Show MyList object's data in displayMyList method");
    NSLog(@"[%d]: %@", theList.index, theList.name);
    for (int i = 0; i < 4; i++) {
        NSLog(@"bookList[%d] = %d", i, theList.bookList[i]);
    }
}

Я не знаю, что не так с моим тестовым кодом, поскольку значения переменных экземпляра index и name не изменяются при отправке объекта в метод displayMyList:. Я пошагово отлаживал, и указатель на массив int все время указывает на один и тот же адрес, поэтому кажется, что где-то есть побочный эффект, который изменяет значения массива, или я не понимаю, как размещается память для этого типа указателей на int массивы. Может быть, это просто какая-то арифметика указателей, которую я не понимаю, потому что я не видел этого ни в одной из книг по программированию для iPhone, которые у меня есть.

Я хотел реализовать массив int, потому что математика, которую я делаю с ним, очень проста, и казалось, что использование NSArray было излишним (если этот подход не работает, я всегда могу пойти с NSArray хотя).

Ответы [ 2 ]

4 голосов
/ 01 ноября 2011

Ваша проблема здесь:

[aList setBookList:(int[]){1,2,3,0}];

Область действия ссылки, выделенной в стеке, ограничена только окружающей функцией, поэтому вы получаете значения мусора в другой области, поскольку стек, в котором она была выделена, больше не используется.

Вам нужно malloc этот список int s, чтобы поместить эту ссылку в кучу, вот так:

int sourceList[] = { 1, 2, 3, 0 }; 
int *bookList = malloc(sizeof sourceList); 

memcpy(aList->bookList, sourceList, sizeof sourceList);

Поскольку вы вызвали malloc, вы должны в конечном итоге освободить память, занятую «массивом», вызвав free в вашем методе -dealloc.

В качестве альтернативы вы можете использовать NSArray из NSNumber объектов, например:

@property (nonatomic, strong) NSArray *bookList;

//...

#define NUMINT(x) [NSNumber numberWithInt:x]

NSArray *bookList = [NSArray arrayWithObjects:NUMINT(1), NUMINT(2), NUMINT(3), NUMINT(0), nil];
[aList setBookList:bookList];
1 голос
/ 01 ноября 2011

Массив, который вы создаете при назначении, является локальным для этого метода; память снова используется после завершения метода, а это означает, что в конечном итоге там заканчиваются значения мусора. Сам указатель не меняется, потому что это ивар, а адрес, на который он указывает, не меняется, потому что вы его не переназначили. Изменяется только содержимое указанного адреса.

Вам нужно будет самим управлять памятью, если вы хотите, чтобы она оставалась на всю жизнь вашего объекта. Это не так уж сложно, при условии, что вам не нужно будет передавать массив любому другому объекту. * Нет необходимости переключаться на NSArray, если у вас уже есть логика, использующая int s.

// Get the memory
// malloc returns a generic C pointer, void *, so the value needs to be
// cast to make the compiler happy. 
int * arr = (int *)malloc(LEN_OF_BOOKLIST * sizeof(int));
// Fill in values
//...
// Assign to the ivar
[aList setBookList:arr];

Затем вам нужно освободить эту память, когда объект разрушен:

- (void) dealloc {

    free(bookList);
    // Clean up other ivars
    [super dealloc];
}

В целом, это очень похоже на ручную обработку памяти любого старого объекта. Вы вызываете malloc (с аргументом, указывающим необходимый объем памяти) вместо отправки alloc в класс (который знает, сколько памяти требуется) и используете free вместо release для освобождения памяти , (Также обратите внимание, что free, поскольку отсчет ссылок отсутствует, немедленно помечает память для повторного использования.)

* Именно поэтому был изобретен подсчет ссылок.

...