Классы Objective-C, указатели на примитивные типы и т. Д. - PullRequest
0 голосов
/ 15 апреля 2010

Я коротко изложу очень длинную историю и приведу пример моей проблемы.

Учитывая класс, который имеет указатель на примитивный тип в качестве свойства:

@interface ClassOne : NSObject
{
int* aNumber
}

@property int* aNumber;

Класс создается, а aNumber назначается и присваивается значение, соответственно:

ClassOne* bob = [[ClassOne alloc] init];
bob.aNumber = malloc(sizeof(int));
*bob.aNumber = 5;

Затем по ссылке передается значение aNumber отдельного экземпляра этого типа класса, соответственно:

ClassOne* fred = [[ClassOne alloc] init];
fred.aNumber = bob.aNumber;

Затем указатель Фреда на aNumber освобождается, перераспределяется и присваивается новое значение, например 7.

Теперь у меня проблема; Поскольку Фреду был присвоен тот же указатель, что и у Боба, я ожидаю, что aNumber Боба теперь будет иметь значение 7. Это не так, потому что по какой-то причине его указатель был освобожден, но не переназначен (он все еще указывает на тот же адрес, который был сначала выделен, который теперь освобожден). Однако указатель Фреда имеет выделенное значение 7 в другом месте памяти.

Почему он так себя ведет? Что я недопонимаю? Как я могу заставить его работать как C ++?

Edit: Хорошо, свежее утро, и я вижу, что я привел очень плохой пример с 5 вечера. То, что я пытаюсь сделать, больше похоже на это:

@interface classOne : NSObject
{
    int* numA;
}
@property int* numA;

@implementation...etc

numA выделено и ему присвоено значение. Позже, в отдельном потоке (с необходимыми блокировками и т. Д.), Это делается:

int* numB= malloc(sizeof(int));
*numB = 5;  
free(RefToClassOne.numA);
RefToClassOne.numA = numB;

numA освобождается, но ему не присваивается значение, на которое указывает numB, и это поведение, которое я хотел бы.

Часть более длинной истории состоит в том, что это счетчик вершин для части буфера вершин, который передается в openGL. Я понимаю, что это не должен быть указатель, но буфер float * для координат обрабатывается таким же образом и должен иметь переменный размер, поэтому я хочу исправить это, чтобы решить и эту проблему.

Ответы [ 4 ]

3 голосов
/ 15 апреля 2010

То, что вы неправильно понимаете, заключается в том, что (а) вы не можете передавать вещи по ссылке в Objective-C, и (б), даже если бы вы могли, это вам не поможет.

Objective-C позволяет передавать вещи только по значению. Иногда, как в случае объектов или указателей, передаваемое вами значение является самой ссылкой, но оно обрабатывается как значение. Прозрачные ссылки в стиле C ++ не существуют.

Но предположим, что они у нас были. Как это поможет в этом случае? Переменная экземпляра aNumber по-прежнему имеет тип int*; когда вы присваиваете ему (как в fred.aNumber = bob.aNumber), вы должны создать копию. На данный момент не имеет значения, что было передано по ссылке, и не имеет значения, что вещи являются переменными экземпляра. Ваш код фактически такой же, как

int* bobNumber;
int* fredNumber;

bobNumber   = malloc(sizeof(int));
*bobNumber  = 5;
fredNumber  = bobNumber;

Здесь bobNumber и fredNumber - это разные переменные & mdash; они имеют разные имена, живут в разных местах в памяти и т. Д. & Mdash; имеют одинаковое значение. Теперь значение, которое они имеют, является ссылкой на другое место в памяти, поэтому они являются эквивалентными ссылками. Однако что произойдет, если мы изменим один из них?

free(fredNumber);
fredNumber  = malloc(sizeof(int));
*fredNumber = 7;

Поскольку аргументы функции передаются по значению, free не может ничего сделать с fredNumber; он может работать только со значением fredNumber, освобождая ссылочную память. Поскольку это то же самое, что и значение bobNumber, мы видим этот эффект, если пытаемся разыменовать bobNumber. Далее мы присваиваем значение fredNumber. Поскольку fredNumber и bobNumber живут в разных местах памяти, это назначение, естественно, ничего не делает с bobNumber. На этом этапе fredNumber != bobNumber, поэтому, естественно, когда мы присваиваем 7 *fredNumber, ничего не происходит с *bobNumber (что в любом случае недопустимо, только что было free d).

Обратите внимание, что ваш комментарий о том, чтобы "заставить его работать как в C ++", странный; C ++, как я уже сказал, тоже не работает таким образом. Если вы действительно хотите, чтобы это работало в C ++, вам понадобится ссылочная переменная экземпляра

class ClassTwo {
  public:
    int*& aNumber;
    ClassTwo(int*& an) : aNumber(an) { }
};

Обратите внимание, что an необходимо передать по ссылке; Изначально я пытался сделать это без этого, а затем в конструкторе была создана копия, которая вызвала тот же старый набор проблем.

Теперь, независимо от того, передадим ли мы bob по ссылке, он все равно будет иметь такую ​​же ссылку aNumber, поэтому мы можем построить что-то вроде

int* shared;
ClassTwo bob(shared);
bob.aNumber = new int(5);
ClassTwo fred(bob.aNumber);
delete fred.aNumber;
fred.aNumber = new int(7);

И все будет работать так, как вы ожидаете. Тем не менее, это может быть не очень хорошая идея. Обратите внимание, что по одной причине ссылки на переменные shared должны иметь возможность ссылаться на что-либо. И это может вызвать проблемы: если ссылка на объект выходит из области видимости, поведение ссылки не определено.

0 голосов
/ 15 апреля 2010

Это работает так же в C ++. Вот эквивалентный пример:

class Bob {
    public:
    int *aNumber;
};

void wontWork() {
    Bob bob, fred;
    bob.aNumber = new int;
    *bob.aNumber = 5;
    fred.aNumber = bob.aNumber;
    delete fred.aNumber;
    fred.aNumber = new int;
    *fred.aNumber = 7;
    cout << *bob.aNumber << *fred.aNumber << endl;
}

Ожидаете ли вы, что * bob.aNumber будет 7 здесь? Когда вы сделали delete fred.aNumber, это освободило память, на которую указывали и Боб, и Фред. Затем вы переназначили fred для указания на новую память, но вы не переназначили bob, так что это просто висячий указатель. Так что нет ничего, что заставило бы bob быть равным 7. Помните, что указатели - это просто простые значения, например, int. Нет волшебства, которое заставляет два указателя, указывающие на один и тот же адрес, синхронизироваться друг с другом.

0 голосов
/ 15 апреля 2010

Ну, насколько я вижу, ваш код делает именно то, что вы говорите.

Использование указателя на int - не самый совместимый способ обработки значения; вам нужно соответствующим образом вызвать free, и мне будет проще использовать объект NSValue, если вы просто хотите передать значение между объектами.

0 голосов
/ 15 апреля 2010

Если вы устанавливаете оба так, чтобы они указывали на один и тот же объект, то когда вы освобождаете объект, вы фактически удаляете то, на что указывают оба, так что оба указателя становятся недействительными. Для переназначения необходимо повторить одну и ту же процедуру, установив оба указателя так, чтобы они указывали на один и тот же новый объект.

Уничтожение объекта не будет автоматически обновлять все указатели, которые на него указывают, поскольку указатели независимы друг от друга и ничего не знают друг о друге.

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

Полагаю, что вы ищете после того, как в C ++ вы пишете

Фред = Боб;

где Фред создает копию Боба

в этом случае вам понадобится какая-то функция клона в вашем классе.

РЕДАКТИРОВАТЬ: перефразировано

...