Надлежащий способ использовать Objective C

Информатика всегда была несколько противоречащей; часть, которая это о компьютерах, не является наукой, и часть, это - наука, не о компьютерах.

Университеты склонны склоняться больше на 'научном' конце (алгоритмы, datastrctures, компиляторы, и т.д.), потому что те вещи намного более 'бесконечны', чем текущие промышленные лучшие практики, которые имеют тенденцию развиваться и изменяться из года в год. Управление версиями, например, претерпело удивительные изменения за прошлые 5 или 10 лет, но большой-O является все еще большим-O, и хеширование, B-деревья, и рекурсия все еще так же полезна, как они были 40 лет назад. Их идея состоит в том, чтобы обычно давать Вам достаточно основ, что можно затем взять инструменты как мерзавец и понять то, что это означает, когда Вам говорят, что базовый datastructure является нециклическим ориентированным графом хешей SHA-1, и что разработчики упорно работали для оптимизации количества syscalls так, чтобы это был io-bound.

Теперь, думайте о том, где Вы изучили все вещи, необходимо было знать, чтобы понять, что последнее предложение - если ответ является 'университетом', они делают хорошо задание.

10
задан groundhog 14 August 2009 в 23:39
поделиться

5 ответов

Я понимаю, откуда вы пришли в отношении названия ключевого слова @interface , но Objective-C использует два ключевых слова @interface ] и @implementation , чтобы различать объявление класса (интерфейс) и его определение (реализацию). Часть @interface обычно помещается в файл заголовка класса, а часть @implementation входит в исходный файл. Я на 100% согласен с вами, что внутренние переменные экземпляра действительно не должны появляться в файле заголовка. Я не уверен, что привело к такому решению, но, скорее всего, оно связано с наследованием объектов. Когда вы наследуете от родительского класса:

#import "ParentClass.h"
@interface MyClass : ParentClass
...

Вы также наследуете все переменные экземпляра, что компилятору будет очень сложно определить, если эти переменные не объявлены в заголовке класса.

Вы также правы насчет использования @protocol , поскольку он по существу определяет интерфейс, который класс должен придерживаться. Протоколы кажутся ответом Objective-C на множественное наследование.

По моему опыту с Objective-C, протоколы используются не очень часто. У них есть свое место, но большая часть кода использует ключевые слова @interface и @implementation для определения класса, а протоколы используются только тогда, когда требуются некоторые дополнительные функции, которые не они не входят в иерархию классов, но все же должны использоваться несколькими классами.

Если вам нужны рекомендации, я бы сказал:

  1. Согласитесь с именованием ключевых слов @interface и @implementation , даже если оно не совсем соответствует тому, что вы ожидаете. При программировании на Objective-C, пейте Kool-Aid и смотрите на эти ключевые слова так, как задумывали разработчики языка.
  2. Используйте протоколы, когда они имеют смысл в вашем коде, но не злоупотребляйте ими в попытке сделать Objective-C вести себя больше как другой язык. Это может сработать, но другие люди будут съеживаться, увидев ваш код, и это затруднит совместную работу.
  3. Продолжайте задавать такие хорошие вопросы. Вы подняли несколько очень важных моментов!
10
ответ дан 3 December 2019 в 15:52
поделиться

Во-первых, вы должны понять, что делаете классическую ошибку: язык, который вы уже знаете, определяет термины, используемые во всех языках - как если бы конкретный язык установил каноническую номенклатуру.

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. Это должно помочь вам в мышлении.

12
ответ дан 3 December 2019 в 15:52
поделиться

Я также понял, что на самом деле ничто не заставляет мои общедоступные интерфейсы содержать переменные экземпляра ...

@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» для реализации этих протоколов.

1
ответ дан 3 December 2019 в 15:52
поделиться

Никто не имеет еще объяснил, почему переменные экземпляра появляются в @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 . Может, это и не красиво, но вот оно.

структура базового класса содержит только один ivar - указатель с именем isa на структуру Class , представляющую класс этого объекта. Итак, если бы я хотел создать подклассы следующим образом:

@interface GLObject : NSObject {
  int x;
}
@end

, мне нужно знать, как создать структуру, которая выглядит так:

{
  Class isa;
  int x;
}

, следовательно, макет ivar родительского класса (и по индукции любой класс) должен быть частью публичного контракта класса, то есть частью его @interface . Может, это и не красиво, но вот оно.

структура базового класса содержит только один ivar - указатель с именем isa на структуру Class , представляющую класс этого объекта. Итак, если бы я хотел создать подклассы следующим образом:

@interface GLObject : NSObject {
  int x;
}
@end

, мне нужно знать, как создать структуру, которая выглядит так:

{
  Class isa;
  int x;
}

, следовательно, макет ivar родительского класса (и по индукции любой класс) должен быть частью публичного контракта класса, то есть частью его @interface . Может, это и не красиво, но вот оно.

т.е. часть его @interface . Может, это и не красиво, но вот оно.

т.е. часть его @interface . Может, это и не красиво, но вот оно.

3
ответ дан 3 December 2019 в 15:52
поделиться

Альтернативный вариант, если вы не можете поддерживать 64-битную среду выполнения и исключительно синтезировать ivars, - это создать ivar id в вашем классе. Если вам нужно вернуться в свой класс после доставки и добавить больше переменных экземпляра, вы можете использовать этот id в качестве контейнера для них.

Это позволяет вам писать менее сложный код, но дает вам некоторую свободу действий. чтобы изменить что-то за спиной вашего клиента без принудительной перекомпиляции.

Вводя ivar как id , вы ничего не обещаете клиентскому коду (это должно быть сделано @private в любом случае).

Я могу представить, чтобы все ваши общедоступные классы действовали как прокси для частных реализаций, что стало бы немного неуправляемым. Имейте в виду, если вы это сделаете, вы можете использовать пересылку во время выполнения, чтобы уменьшить объем кода, который вам нужно написать в общедоступном классе. См. - [NSObject forwardingTargetForSelector:] . Это задокументировано в 10.5 Foundation Release Notes

1
ответ дан 3 December 2019 в 15:52
поделиться