Подтвердить что ты не робот

Почему [object doSomething], а не [* object doSomething]?

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

  • Это не указатель, это ссылка на объект.
  • Это не метод, это сообщение.

Ответ 4

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

id objc_msgSend(id theReceiver, SEL theSelector, ...)

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

[x doSomething];

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

NSString* string;

в порядке, но

NSString string;

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

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

[*x doSomething];

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

Ответ 8

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

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

Ответ 9

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