Почему [объект doSomething], а не [* объект doSomething]? - PullRequest
47 голосов
/ 03 февраля 2010

В Objective-C, почему [object doSomething]? Разве это не будет [*object doSomething], так как вы вызываете метод объекта? Что означает, что вы должны разыменовать указатель?

Ответы [ 9 ]

113 голосов
/ 07 февраля 2010

Ответ возвращается к C-корням Objective-C.Objective-C изначально был написан как препроцессор для компилятора C. То есть Objective-C был скомпилирован не так сильно, как был преобразован в прямой C, а затем скомпилирован.

Начнем с определениявведите id.Он объявлен как:

typedef struct objc_object {
    Class isa;
} *id;

То есть id - это указатель на структуру, первое поле которой имеет тип Class (который сам по себе является указателем на структуру, определяющую класс),Теперь рассмотрим NSObject:

@interface NSObject <NSObject> {
    Class   isa;
}

Обратите внимание, что компоновка NSObject и компоновка типа, на который указывает id, идентичны.Это происходит потому, что в действительности экземпляр объекта Objective-C на самом деле является просто указателем на структуру, первое поле которой - всегда указатель - указывает на класс, который содержит методы для этого экземпляра (наряду с некоторыми другими метаданными).

Когда вы создаете подкласс NSObject и добавляете некоторые переменные экземпляра, вы, для всех намерений и целей, просто создаете новую структуру C, которая содержит ваши переменные экземпляра как слоты в этой структуре, объединенные в слоты для переменных экземплярадля всех суперклассов.(Современная среда выполнения работает немного по-другому, так что суперкласс может иметь добавленные ивары, не требуя перекомпиляции всех подклассов).

Теперь рассмотрим разницу между этими двумя переменными:

NSRect foo;
NSRect *bar;

(NSRect - простая структура C - ObjC не задействован).foo создается с хранилищем в стеке.Он не выживет после закрытия фрейма стека, но вам также не нужно освобождать память.bar - это ссылка на структуру NSRect, которая, скорее всего, была создана в куче с использованием malloc().

Если вы попытаетесь сказать:

NSArray foo;
NSArray *bar;

Компилятор будет жаловатьсяо первом, говоря что-то вроде стековых объектов, не допускается в Objective-C .Другими словами, все Объекты Objective C должны быть выделены из кучи (более или менее - есть одно или два исключения, но они сравнительно эзотеричны для этого обсуждения), и, в результате, вы всегда относится к объекту через адрес указанного объекта в куче;вы всегда работаете с указателями на объекты (а тип id на самом деле является просто указателем на любой старый объект).

Возвращаясь к корням препроцессора языка C, вы можете перевести каждый вызов метода наэквивалентная строка C. Например, следующие две строки кода идентичны:

[myArray objectAtIndex: 42];
objc_msgSend(myArray, @selector(objectAtIndex:), 42);

Аналогично, метод объявлен так:

- (id) objectAtIndex: (NSUInteger) a;

Эквивалентно объявленной функции Cкак это:

id object_at_index(id self, SEL _cmd, NSUInteger a);

И, глядя на objc_msgSend(), объявлен первый аргумент типа id:

OBJC_EXPORT id objc_msgSend(id self, SEL op, ...);

И именно поэтому вы не* используйте *foo в качестве цели вызова метода.Выполните преобразование через вышеприведенные формы - вызов [myArray objectAtIndex: 42] переводится в вышеуказанный вызов функции C, который затем должен вызывать что-то с эквивалентным объявлением вызова функции C (все в синтаксисе метода).

Ссылка на объект переносится, потому что она дает мессенджеру - objc_msgSend () доступ к классу, чтобы затем найти реализацию метода, а также эту ссылку, становящуюся первым параметром - self - метода, который в конечном итогеВыполнено.

Если вы действительно хотите углубиться, начните здесь .Но не беспокойтесь, пока вы полностью не взяли этот грок .

15 голосов
/ 06 февраля 2010

Вы не должны действительно думать о них как о указателях на объекты. Это своего рода историческая деталь реализации, что они являются указателями, и вы используете их в синтаксисе отправки сообщений (см. Ответ @ bbum). На самом деле они просто «идентификаторы объекта» (или ссылки). Давайте немного перемотаем назад, чтобы увидеть концептуальное обоснование.

Objective-C был впервые предложен и обсужден в этой книге: Объектно-ориентированное программирование: эволюционный подход . Это не очень практично для современных программистов Какао, но мотивы для языка есть там.

Обратите внимание, что в книге все объекты имеют тип id. Вы не видите более специфических Object * в книге; это просто утечка в абстракции, когда мы говорим о «почему». Вот что говорится в книге:

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

То, как идентификатор объекта фактически идентифицирует объект, является деталью реализации, для которой возможны многие варианты. Разумный выбор, безусловно, один из самых простых и тот, который используется в Objective-C, заключается в использовании физического адреса объекта в памяти в качестве его идентификатора. Objective-C делает это решение известным C, генерируя оператор typedef в каждом файле. Это определяет новый тип, id, в терминах другого типа, который C уже понимает, а именно указателей на структуры. [...]

Идентификатор занимает фиксированное количество места. [...] Это пространство не совпадает с пространством, занимаемым личными данными в самом объекте.

(стр. 58-59, 2-е изд.)

Итак, ответ на ваш вопрос двоякий:

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

Строго типизированный синтаксис, в котором вы говорите «объект конкретно типа NSString» и, следовательно, используете NSString *, является более современным изменением и, по сути, выбором реализации, эквивалентным id.

Если это похоже на энергичный ответ на вопрос о разыменовании указателя, важно иметь в виду, что объекты в Objective-C являются «особыми» в соответствии с определением языка. Они реализованы как структуры и переданы как указатели на структуры, но концептуально они разные.

10 голосов
/ 03 февраля 2010

Поскольку objc_msgSend () объявлено так:

id objc_msgSend(id theReceiver, SEL theSelector, ...)
9 голосов
/ 03 февраля 2010
  1. Это не указатель, это ссылка на объект.
  2. Это не метод, это сообщение.
6 голосов
/ 03 февраля 2010

Вы никогда не разыменовываете указатели объектов, точка. Тот факт, что они печатаются как указатели, а не просто как «типы объектов», является артефактом языка Си. Это в точности эквивалентно системе типов Java, где объекты всегда доступны через ссылки. Вы никогда не разыменовываете объект в Java - фактически, вы не можете. Вы не должны думать о них как о указателях, потому что семантически они не являются. Это просто ссылки на объекты.

4 голосов
/ 03 февраля 2010

Я бы сказал так: то, что язык ассоциирует с серией алфавитов, это просто соглашение.Люди, разработавшие Objective-C, решили, что

[x doSomething];

означает «отправка сообщения doSomething объекту , указанному x».Они определили это таким образом, вы следуете правилу :) Одна особенность Objective-C, по сравнению, например, с C ++, состоит в том, что он не имеет синтаксиса для хранения самого объекта, а не указателя на объект.Итак,

NSString* string;

в порядке, но

NSString string;

недопустимо.Если бы это было возможно, должен был бы быть способ «отправить сообщение capitalizedString в строку string», а не «отправить» сообщение capitalizedString в строку , указанную string».Но на самом деле вы всегда отправляете сообщение объекту , указанному переменной в вашем исходном коде.

Итак, если бы разработчики Objective-C следовали вашей логике, вам бы приходилось писать

[*x doSomething];

каждый раз, когда вы отправляете сообщение ... Понимаете, * нужныпоявиться всегда после первой скобки [, образуя комбинацию [*.На этом этапе, я полагаю, вы согласны с тем, что лучше изменить язык так, чтобы вам нужно было писать [ вместо [*, изменяя значение последовательности букв [x doSomething].

4 голосов
/ 03 февраля 2010

Частично причина в том, что вы получите исключения нулевого указателя влево и вправо. Отправка сообщения на nil разрешена и часто совершенно законна (это ничего не делает и не генерирует ошибку).

Но вы можете подумать, что это аналогично нотации -> в C ++: он выполняет метод и разыменовывает указатель в виде одного кусочка синтаксического сахара.

3 голосов
/ 03 февраля 2010

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

Когда вы объявляете объект Objective-C, вы всегда объявляете его как тип указателя, потому что среда выполнения передает ваш объект другим методам и функциям; если они изменят какие-либо члены struct (путем изменения переменных экземпляра и т. д.), они будут & ldquo; apply & rdquo; на все ссылки на ваш объект, а не только локально в методе или функции.

2 голосов
/ 03 февраля 2010

Среде выполнения Objective C может потребоваться объединить объект с парой различных функций, поэтому ему нужна ссылка на объект, а не сам объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...