Почему [объект doSomething], а не [* объект doSomething]?

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

9 ответов

Решение

Ответ возвращается к 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 - метода, который в конечном итоге казнены.

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

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

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

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

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

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

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

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

Таким образом, ответ на ваш вопрос имеет два аспекта:

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

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

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

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

id objc_msgSend(id theReceiver, SEL theSelector, ...)
  1. Это не указатель, это ссылка на объект.
  2. Это не метод, это сообщение.

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

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

[x doSomething];

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

NSString* string;

хорошо, но

NSString string;

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

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

[*x doSomething];

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

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

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

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

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

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

Другие вопросы по тегам