Информатика всегда была несколько противоречащей; часть, которая это о компьютерах, не является наукой, и часть, это - наука, не о компьютерах.
Университеты склонны склоняться больше на 'научном' конце (алгоритмы, datastrctures, компиляторы, и т.д.), потому что те вещи намного более 'бесконечны', чем текущие промышленные лучшие практики, которые имеют тенденцию развиваться и изменяться из года в год. Управление версиями, например, претерпело удивительные изменения за прошлые 5 или 10 лет, но большой-O является все еще большим-O, и хеширование, B-деревья, и рекурсия все еще так же полезна, как они были 40 лет назад. Их идея состоит в том, чтобы обычно давать Вам достаточно основ, что можно затем взять инструменты как мерзавец и понять то, что это означает, когда Вам говорят, что базовый datastructure является нециклическим ориентированным графом хешей SHA-1, и что разработчики упорно работали для оптимизации количества syscalls так, чтобы это был io-bound.
Теперь, думайте о том, где Вы изучили все вещи, необходимо было знать, чтобы понять, что последнее предложение - если ответ является 'университетом', они делают хорошо задание.
Я понимаю, откуда вы пришли в отношении названия ключевого слова @interface
, но Objective-C использует два ключевых слова @interface
] и @implementation
, чтобы различать объявление класса (интерфейс) и его определение (реализацию). Часть @interface
обычно помещается в файл заголовка класса, а часть @implementation
входит в исходный файл. Я на 100% согласен с вами, что внутренние переменные экземпляра действительно не должны появляться в файле заголовка. Я не уверен, что привело к такому решению, но, скорее всего, оно связано с наследованием объектов. Когда вы наследуете от родительского класса:
#import "ParentClass.h"
@interface MyClass : ParentClass
...
Вы также наследуете все переменные экземпляра, что компилятору будет очень сложно определить, если эти переменные не объявлены в заголовке класса.
Вы также правы насчет использования @protocol
, поскольку он по существу определяет интерфейс, который класс должен придерживаться. Протоколы кажутся ответом Objective-C на множественное наследование.
По моему опыту с Objective-C, протоколы используются не очень часто. У них есть свое место, но большая часть кода использует ключевые слова @interface
и @implementation
для определения класса, а протоколы используются только тогда, когда требуются некоторые дополнительные функции, которые не они не входят в иерархию классов, но все же должны использоваться несколькими классами.
Если вам нужны рекомендации, я бы сказал:
@interface
и @implementation
, даже если оно не совсем соответствует тому, что вы ожидаете. При программировании на Objective-C, пейте Kool-Aid и смотрите на эти ключевые слова так, как задумывали разработчики языка. Во-первых, вы должны понять, что делаете классическую ошибку: язык, который вы уже знаете, определяет термины, используемые во всех языках - как если бы конкретный язык установил каноническую номенклатуру.
Java была начата в 1990 году внутри компании Sun. Objective-C уже был выпущен в 1986 году. Так что, во всяком случае, терминология интерфейса Java расходится с Objective-C, а не наоборот. Однако термин «интерфейс» имеет гораздо более долгую историю, чем любой из этих языков.
Идея @interface в Objective-C изложена в Справочнике по языку программирования Objective-C 2.0 :
] interface Часть спецификации класса Objective-C, которая объявляет его открытый интерфейс, который включает имя суперкласса, переменные экземпляров и прототипы общедоступных методов.
Методы экземпляра являются общедоступными, если они объявлены в интерфейсе. если они объявлены в @implementation, они являются частными.
Реализация входит в @implementation. Возможно, вас сбивает с толку то, что в Java нет понятия раздела объявления класса, как в Objective-C. Объявление класса объявляет интерфейс класса в общем виде, без специфики реализации. @Implementation имеет фактическую реализацию, в которой подробно описывается, как реализован класс @interface.
Нет ничего плохого в том, что objective-c отличается от Java, он просто другой.
Но вы правы, протоколы - это самый близкий аналог java-интерфейсов в объекте-c, который вы найдете. Это не суперкласс ни для чего, и не не имеют какой-либо связанной реализации. Вы предоставляете протоколы так же, как интерфейсы в Java.
Однако обратите внимание, что протоколы не так распространены, как классы в Objective-C. Это должно помочь вам в мышлении.
Я также понял, что на самом деле ничто не заставляет мои общедоступные интерфейсы содержать переменные экземпляра ...
@interface Foo : NSObject
// some methods but no ivars
+ (Foo*) fooWithBlahBlah
@end
И файл реализации ...
@implementation Foo
+ (Foo*) fooWithBlahBlah {
return [[SomeDerivedFoo alloc] init];
}
@end
Где SomeDerivedFoo объявлен внутри, а не видимые для клиентов ...
@interface SomeDerivedFoo : Foo
{
// instance vars
}
// overrides of methods
Это позволило бы компоненту предоставить интерфейс, свободный от зависимостей ivar. Фактически, это модель, которую я вижу в так называемых «кластерах классов». Мне это "кажется" намного лучше.
Моя большая проблема с ivars в публичном интерфейсе связана с контролем зависимостей. Если мои ivars - это не что иное, как другие классы Objective-C, тогда да, я могу обойтись без предварительных объявлений, но в случае встроенных структур C или других типов, не являющихся классами, я должен видеть определение этих типов, и поэтому мои клиенты теперь зависят от их.
Изменение переменной экземпляра не должно вызывать перекомпиляцию клиентов моего интерфейса. Повторное связывание - это нормально, но не перекомпиляция. В случае общей структуры не требуется даже повторного связывания. Это не имеет ничего общего с каким-либо конкретным языком. Это связано с изоляцией от деталей. На мой взгляд, это довольно универсальная цель.
Это то, что привело к моему первоначальному вопросу о протоколах и интерфейсах. Похоже, что для библиотеки или фреймворка более «правильным» решением будет предоставить набор протоколов вместе с некоторыми классами «Provider» или «Factory» для реализации этих протоколов.
Это не имеет ничего общего с каким-либо конкретным языком. Это связано с изоляцией от деталей. На мой взгляд, это довольно универсальная цель.Это то, что привело к моему первоначальному вопросу о протоколах и интерфейсах. Похоже, что для библиотеки или фреймворка более «правильным» вариантом будет предоставить набор протоколов вместе с некоторыми классами «Provider» или «Factory» для реализации этих протоколов.
Это не имеет ничего общего с каким-либо конкретным языком. Это связано с изоляцией от деталей. На мой взгляд, это довольно универсальная цель.Это то, что привело к моему первоначальному вопросу о протоколах и интерфейсах. Похоже, что для библиотеки или фреймворка более «правильным» решением будет предоставить набор протоколов вместе с некоторыми классами «Provider» или «Factory» для реализации этих протоколов.
Никто не имеет еще объяснил, почему переменные экземпляра появляются в @interface
класса Objective-C. Объекты реализованы как структуры C, которые содержат переменные экземпляра. Когда вы создаете новый подкласс, новый тип структуры определяется со всеми ivars суперкласса, за которыми следуют ivars нового класса. Структура суперкласса должна содержать ivars его суперкласса, а затем это «черепахи полностью вниз» в структуру, которая представляет ivar базового класса.
В случае NSObject
(и действительно Object
) структура базового класса содержит только один ivar - указатель, называемый isa
в структуру Class
, представляющую класс этого объекта. Итак, если бы я хотел создать подклассы следующим образом:
@interface GLObject : NSObject {
int x;
}
@end
, мне нужно знать, как создать структуру, которая выглядит так:
{
Class isa;
int x;
}
, следовательно, макет ivar родительского класса (и по индукции любой класс) должен быть частью публичного контракта класса, то есть частью его @interface
. Может, это и не красиво, но вот оно.
isa
на структуру Class
, представляющую класс этого объекта. Итак, если бы я хотел создать подклассы следующим образом:
@interface GLObject : NSObject {
int x;
}
@end
, мне нужно знать, как создать структуру, которая выглядит так:
{
Class isa;
int x;
}
, следовательно, макет ivar родительского класса (и по индукции любой класс) должен быть частью публичного контракта класса, то есть частью его @interface
. Может, это и не красиво, но вот оно.
isa
на структуру Class
, представляющую класс этого объекта. Итак, если бы я хотел создать подклассы следующим образом:
@interface GLObject : NSObject {
int x;
}
@end
, мне нужно знать, как создать структуру, которая выглядит так:
{
Class isa;
int x;
}
, следовательно, макет ivar родительского класса (и по индукции любой класс) должен быть частью публичного контракта класса, то есть частью его @interface
. Может, это и не красиво, но вот оно.
@interface
. Может, это и не красиво, но вот оно. т.е. часть его @interface
. Может, это и не красиво, но вот оно. Альтернативный вариант, если вы не можете поддерживать 64-битную среду выполнения и исключительно синтезировать ivars, - это создать ivar id
в вашем классе. Если вам нужно вернуться в свой класс после доставки и добавить больше переменных экземпляра, вы можете использовать этот id
в качестве контейнера для них.
Это позволяет вам писать менее сложный код, но дает вам некоторую свободу действий. чтобы изменить что-то за спиной вашего клиента без принудительной перекомпиляции.
Вводя ivar как id
, вы ничего не обещаете клиентскому коду (это должно быть сделано @private
в любом случае).
Я могу представить, чтобы все ваши общедоступные классы действовали как прокси для частных реализаций, что стало бы немного неуправляемым. Имейте в виду, если вы это сделаете, вы можете использовать пересылку во время выполнения, чтобы уменьшить объем кода, который вам нужно написать в общедоступном классе. См. - [NSObject forwardingTargetForSelector:]
. Это задокументировано в 10.5 Foundation Release Notes