Почему [возражают doSomething] и не [*object doSomething]?

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

48
задан Michael Petrotta 22 June 2013 в 17:23
поделиться

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, которая содержит ваши переменные экземпляра как слоты в этой структуре, объединенные со слотами для переменных экземпляра для всех суперклассов.(Современная среда выполнения работает немного по-другому, так что суперкласс может иметь добавленные ivars без необходимости перекомпиляции всех подклассов).

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

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 - метода. что в конечном итоге выполняется.

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

113
ответ дан 7 November 2019 в 12:04
поделиться

Поскольку objc_msgSend () объявлен следующим образом:

id objc_msgSend(id theReceiver, SEL theSelector, ...)
10
ответ дан 7 November 2019 в 12:04
поделиться
  1. Это не указатель, это ссылка на объект.
  2. Это не метод, это сообщение.
9
ответ дан 7 November 2019 в 12:04
поделиться

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

2
ответ дан 7 November 2019 в 12:04
поделиться

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

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

3
ответ дан 7 November 2019 в 12:04
поделиться

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

6
ответ дан 7 November 2019 в 12:04
поделиться

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

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

4
ответ дан 7 November 2019 в 12:04
поделиться

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

[x doSomething];

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

NSString* string;

- это нормально, но

NSString string;

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

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

[*x doSomething];

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

4
ответ дан 7 November 2019 в 12:04
поделиться

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

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

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

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

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

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

(pp58-59, 2nd ed.)

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

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

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

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

15
ответ дан 7 November 2019 в 12:04
поделиться