Передача аргументов по значению или по ссылке в объекте C

Я как бы новичок в цели c, и я пытаюсь передать аргумент по ссылке, но веду себя так, как будто это значение. Вы знаете, почему это не работает?

Это функция:

- (void) checkRedColorText:(UILabel *)labelToChange {
    NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
    if (startLaterThanEnd == NSOrderedDescending){
        labelToChange.textColor = [UIColor redColor];
    }
    else{
        labelToChange.textColor = [UIColor blackColor];
    }

}

И это призыв:

UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];

Спасибо за вашу помощь


person Rafael    schedule 23.05.2010    source источник
comment
Что вы имеете в виду, говоря, что это ценность?   -  person Ben Zotto    schedule 23.05.2010


Ответы (5)


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

Objective-C - это строгий надмножество языка C, означает, что все, что делает C, делает Obj-C тоже.

Бегло просмотрев Википедию, вы можете увидеть, что Function parameters are always passed by value

Objective-C ничем не отличается. Здесь происходит следующее: всякий раз, когда мы передаем объект функции (в данном случае UILabel *), мы передаем значение содержащееся по адресу указателя.

Что бы вы ни делали, это всегда будет ценностью того, что вы проходите. Если вы хотите передать значение ссылки, вам придется передать ему ** объект (как это часто бывает при передаче NSError).

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

Вот пример для облегчения понимания:

- (void)parentFunction {
    int i = 0;
    [self modifyValueOfPassedArgument:i];
    //i == 0 still!
}

- (void)modifyValueOfPassedArgument:(NSInteger)j {
    //j == 0! but j is a copied variable. It is _NOT_ i
    j = 23;
    //j now == 23, but this hasn't changed the value of i.
}

Если вы хотите иметь возможность изменять i, вам нужно будет передать значение ссылки, выполнив следующие действия:

- (void)parentFunction {
    int i = 0; //Stack allocated. Kept it that way for sake of simplicity
    [self modifyValueOfPassedReference:&i];
    //i == 23!
}

- (void)modifyValueOfPassedReference:(NSInteger *)j {
    //j == 0, and this points to i! We can modify i from here.
    *j = 23;
    //j now == 23, and i also == 23!
}
person Pier-Olivier Thibault    schedule 07.10.2011
comment
Технически да, вы передаете примитивы только по значению. Однако это полностью вводит в заблуждение в том смысле, что, хотя указатель на объект передается по значению, этот указатель указывает на тот же объект. Поскольку ДРУГОГО СПОСОБА ПЕРЕДАЧИ (или обработки) ОБЪЕКТОВ НЕТ, объекты всегда передаются по ссылке. Единственный раз, когда они ведут себя так, как будто это не так, это если OP сказал labelToChange = nil; или что-то в этом роде, но все здесь знают, что это не сработает, и не обсуждают. - person Jared Pochtar; 22.10.2011
comment
это так вводит в заблуждение .... Объекты передаются по ссылке в объекте-c .., чтобы добавить больше удовольствия, объект задал вопрос, и на него ответили с объяснением int, которое передается по значению. И если этого недостаточно, у вас есть 60+ голосов, чтобы Objective-C поддерживал только передачу параметров по значению. - person hariszaman; 30.06.2017
comment
Это не вводит в заблуждение. Это на 100% правильно. Вы можете пройти только по значению в Objective-C. Точка. Конец истории. Объекты не передаются по ссылке. Передается указатель на объект, и этот указатель передается по значению. Конец. Эти термины имеют значения независимо от того, что, по вашему мнению, вы думаете, они должны означать. - person Wayne; 07.03.2020
comment
@ Уэйн прав. передается по ссылке, вызывающий и вызываемый используют одну и ту же переменную, а не ссылку на объект. Вот хороший обзор stackoverflow.com/questions/373419/ - person Kyle C; 14.07.2020

Objective-C, как и Java, имеет только передачу по значению. Как и в Java, доступ к объектам всегда осуществляется через указатели. «объекты» никогда не являются значениями напрямую, поэтому вы никогда не назначаете и не передаете объект. Вы передаете указатель объекта по значению. Но это не похоже на проблему - вы пытаетесь изменить объект, на который указывает указатель, что вполне допустимо и не имеет ничего общего с передачей по значению и передачей по ссылке. Я не вижу проблем с вашим кодом.

person newacct    schedule 07.10.2011
comment
Но я думаю, что все будет по-другому, когда вы будете манипулировать изменяемыми объектами, переданными по параметрам. :) Необработанные типы, такие как integer, float, double, действуют иначе, чем объект. - person Neal.Marlin; 26.07.2019
comment
@ Neal.Marlin: Семантически нет разницы между передачей изменяемых и неизменяемых объектов. В языке нет концепции изменчивости объектов. Неизменяемый объект - это просто объект, у которого нет методов, изменяющих его внутреннее состояние. - person newacct; 26.07.2019
comment
Да семантически ничем не отличается. Но поскольку необработанные типы параметров метода неявно копируются, объекты - нет. Потому что мы используем указатель для удержания объекта. Конечно, ссылка не поддерживается в C. Спасибо за ваше объяснение. - person Neal.Marlin; 27.07.2019

В objective-c нет возможности передавать объекты по значению (если вы явно не скопируете его, но это уже другая история). Пощупайте свой код - вы уверены, что вызывается checkRedColorText:? А как насчет [startDate compare: endDate], он никогда не равен NSOrderedDescending? LabelToChange равен нулю?

person Jared Pochtar    schedule 23.05.2010
comment
В методе NSOrderedDescending иногда оказывается истинным, как я и ожидал. Ярлык не равен нулю. На самом деле, если я подключил labelToChange к startHourLabel, он работает ... Мне кажется, это ведет себя так, как если бы я передавал метку как значение, а не как ссылку: S .. Я не понимаю .. Спасибо за вашу помощь ! - person Rafael; 24.05.2010
comment
Что вы подразумеваете под проводным labelToChange для startHourLabel? Вы не можете связать аргумент метода ни с чем, кроме как путем передачи его в качестве параметра. - person JeremyP; 24.05.2010
comment
Под проводкой я подразумеваю не использовать labelToChange и напрямую использовать переменную startHourLabel (только для отладки puroposes). StartHourLabel является глобальным, и его можно найти в методе. Но я хочу использовать тот же метод для других меток в представлении. Вот почему я добавил параметр labelToChange. Спасибо! - person Rafael; 24.05.2010
comment
ага! Глобальные переменные == ПЛОХО. Выполните рефакторинг кода, а затем вернитесь к этому. По сути, я придерживаюсь того, что сказал раньше; нельзя передавать объекты по значению в objective-c; поэтому проблема не в этом. Гораздо более вероятно, что это как-то связано с использованием вами глобальной переменной. - person Jared Pochtar; 25.05.2010
comment
На самом деле, я просто вернулся и прочитал проблему, и я думаю, что @kubi, возможно, был частично прав - строка 'UILabel * startHourLabel;' действительно существует в том же методе, что и вызов checkRedColorText :? Если это так, и это глобальная переменная, вы бы повторно объявляли переменную в локальной области видимости, и в основном это означает, что она игнорирует предыдущее определение. Если это так, это не будет указывать на настоящую этикетку. Если переменная объявлена ​​глобально в том же файле, что и вызывающий метод, просто оставьте строку, повторно объявляющую ее. Если нет, замените его на extern UILabel * startHourLabel; - person Jared Pochtar; 25.05.2010

Вы редактировали код между этой строкой

UILabel *startHourLabel;

а эта строчка?

[self checkRedColorText:startHourLabel];

Если нет, проблема в том, что вы повторно объявляете свою переменную startHourLabel, поэтому вы теряете любую инициализацию, которая была там ранее. Здесь вы должны получить ошибку компилятора.

person kubi    schedule 23.05.2010
comment
Предположительно, это было для иллюстративных целей, потому что, скорее всего, будет EXE_BAD_ACCESS, если вы сделаете что-нибудь с неинициализированной локальной переменной. - person Jared Pochtar; 23.05.2010
comment
Привет спасибо. Нет startHourLabel там нет, инициализация - это другая часть кода. Я объявил это в интерфейсе и сделал для него конструктор. У меня там ошибок нет ... - person Rafael; 24.05.2010

Вот возможные причины, по которым это не работает:

  1. метка, которую вы передаете в checkRedColorText, не та, которую вы думаете.
  2. результат сравнения всегда получается одинаковым.
  3. ... на самом деле нет 3.

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

Если это не метка в пере, то есть вы намеренно создаете ее программно, вам необходимо проверить адрес метки, которую вы инициализируете, и проверить адрес метки, переданной в checkRedColorText. Либо NSLog его адрес при инициализации и в checkRedColorText, либо проверьте его с помощью отладчика.

person JeremyP    schedule 24.05.2010