#objective-c #syntax #pass-by-reference #class-variables
#objective-c #синтаксис #передача по ссылке #переменные класса
Вопрос:
Я пытаюсь использовать в своем коде как можно меньше памяти. Я пробовал два способа отправки объекта пользовательского класса в метод. Я не уверен, есть ли какая-либо разница между этими двумя подходами. Допустим, у меня есть 2 класса, Class1 и Class2, каждый со своими собственными переменными класса и методами, конечно.
Весь код написан в Class1
Подход 1:
Class2 *class2Object = [[Class2 alloc] init];
[self doSomething: class2Object];
[class2Object release];
-(void) doSomething: (Class2 *) var {
int a = var.a;
}
Подход 2:
Class2 *class2Object = [[Class2 alloc] init];
[self doSomething: amp;class2Object];
[class2Object release];
-(void) doSomething: (Class2 **) var {
int a = var->a;
}
Есть ли какая-либо разница в производительности между этими двумя методами? Является ли второй подход полностью бессмысленным? Почему я могу использовать точечную нотацию в подходе 1, но должен использовать -> в подходе 2?
Спасибо.
Комментарии:
1. В Objective-C. Такой вещи, как переменная класса, не существует.
Ответ №1:
Есть ли какая-либо разница в производительности между этими двумя методами?
действительно, разница в производительности незначительна из-за того, что в подходе 2 у вас есть еще одно косвенное обращение (т. Е. разыменование указателя, см. Также Ниже); таким образом, подход 1 сэкономит вам несколько тактов.
Является ли второй подход полностью бессмысленным?
подход 2 полезен, например, когда вы хотели бы выделить новый экземпляр типа Class2 и передать его обратно вызывающему через тот же аргумент; скажем:
- (bool)cloneObject:(Class2 **)var;
вы передаете объект; объект клонируется и возвращается в var; поскольку изменяется адрес самого объекта, вам необходимо иметь указатель на указатель на объект, чтобы установить новый адрес; возвращаемое значение указывает только на то, была ли операция выполнена правильно.
Конечно, в этом примере было бы более естественным делать:
- (Class2)cloneObject:(Class2*)var;
т. е. вы возвращаете указатель на только что выделенный объект, но вариант использования все еще сохраняется.
Почему я могу использовать точечную нотацию в подходе 1, но должен использовать -> в подходе 2?
во втором случае вы должны использовать ->
, потому что вы имеете дело не с указателем на объект напрямую; вы имеете дело с указателем на указатель на объект; что вам нужно сделать в таких случаях, это, прежде всего, «разыменование» вашего указателя (т. Е. Применение operator *), чтобы получить указатель на объект, а затем доступ к последнему, как вы бы поступили в противном случае; это можно записать как:
(*var).a
здесь var
это указатель на указатель на объект Class2
; *var
это результат его разыменования, так что у вас есть указатель на объект Class2
; наконец, .a
это синтаксис для доступа к свойству объекта. синтаксис:
var->a
это просто «сокращение» для операций, описанных выше.
Комментарии:
1. Спасибо за подробный ответ! Определенно проясняет ситуацию.
Ответ №2:
В подходе 1 вы получаете доступ к свойству с именем a
. Это выглядит нормально.
В подходе 2 я шокирован тем, что ваш код компилируется. Также совершенно бесполезно передавать указатель на class2Object
. Единственная причина когда-либо передавать указатели на объекты заключается в том, что вызываемому методу необходимо обновить этот параметр (например, если это выходной параметр), чего здесь нет. Передача указателя на объект абсолютно не влияет на использование памяти. Объекты уже хранятся как указатели (именно поэтому вы пишете Class2 *
), поэтому нет накладных расходов на копирование объектов, которые вы могли бы увидеть при передаче объекта, выделяемого стеком в C (в Obj-C нет концепции объектов, выделяемых стеком, за исключением блоков, что является странным угловым случаем, о котором вам не нужно беспокоиться).
Итак, в принципе, просто придерживайтесь подхода 1 здесь.
Комментарии:
1. Хорошо, значит, если бы я обновлял значение var.a в doSomething, то подход 2 можно использовать так, как написано?
Ответ №3:
x->y
синтаксис в C такой же, как (*x).y
, поэтому нет никакой разницы в том, что он делает, за исключением дополнительного (ненужного) приема указателя и разыменования, как говорили другие. Но я хотел бы подробнее остановиться на более интересном смежном моменте.
Вы должны понимать, что в
-(void) doSomething: (Class2 *) var {
int a = var.a;
}
точка — это доступ к свойству. (Это не может быть доступом к полю структуры, поскольку var
является указателем.) Это в точности эквивалентно int a = [var a];
(если только для средства получения не было явно задано значение другого метода).
Как и при всех вызовах метода, он будет проходить через механизм динамической передачи сообщений. Если вы хотите избежать незначительных накладных расходов, связанных с этим, вы могли бы получить доступ к переменной напрямую.
Если собственность a
синтезируется из переменной экземпляра a
(можно было бы назвать что-то еще, мы будем говорить a
сейчас), вместо прямого доступа к переменной через свойство, вы можете вместо этого объявить его общедоступным (Через @public
; в противном случае переменные экземпляра являются защищенными по умолчанию), а затем получить доступ к нему напрямую через ->
:
-(void) doSomething: (Class2 *) var {
int a = var->a;
}
(Обратите внимание, что это отличается от того, что у вас есть, потому что var
это сам указатель на объект, а не указатель на указатель.)
Однако общедоступные переменные экземпляра обычно считаются плохим стилем и не поощряются. Его использование нарушает абстракцию в объектно-ориентированном программировании. Я привожу это здесь для полноты картины, потому что технически это самый «быстрый» метод доступа к переменной экземпляра.