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

Наилучшее использование динамических функций Objective-C

Я использую Objective-C в течение некоторого времени, но, исходя из фона статического типа (С#), я думаю, что использую его очень статическим образом. Объявление объектов как id чувствует себя чуждым мне, и я не вижу, что это такое. Может ли кто-нибудь осветить свет для меня, чтобы лучше понять это?

4b9b3361

Ответ 1

Objective-C - это своего рода гибридный язык, в котором вы можете быть как динамическим, так и статическим, как вы хотите. Вы можете объявить все типы всех переменных, если хотите, вы можете даже объявить делегатные переменные как NSObject <Protocol> *, если хотите. Тип id работает меньше как реальный тип и больше похож на подсказку компилятору, говоря ему: "Эй, я знаю, что я делаю, просто доверь мне на это", заставляя компилятор избегать любого типа проверяя эту конкретную переменную.

Первым очевидным преимуществом системы типа Objective-C является то, что типы контейнеров (NSArray, NSDictionary, NSSet) принимают и возвращают типы идентификаторов. Это полностью устраняет необходимость в шаблонах и генериках (например, в С++, Java и С#).

Даже лучше, вы можете иметь контейнеры с элементами любого типа внутри. Пока вы знаете, что входит внутрь, никто не будет жаловаться, если вы добавите два NSStrings, один NSNumber и NSValue внутри того же NSArray. Вы можете сделать это на других языках, но вам нужно использовать базовый класс "Объект" или тип void *, а затем вам нужно вставлять и удалять (или перебрасывать вверх) вниз переменные, чтобы получить такое же поведение. В Objective-C вы просто назначаете, который удаляет шум, создаваемый операторами литья и бокс-операциями. Затем вы можете спросить "replysToSelector:" или "class" для каждого объекта, чтобы узнать идентификатор и операции, которые вы можете выполнять с ними, во время выполнения. В Objective-C отражение является гражданином первого класса.

Другим преимуществом является сокращение времени компиляции; компиляция программы Objective-C, как правило, намного быстрее, чем ее эквивалент в С++, учитывая, что не так много проверок типов, и много ссылок выполняется во время выполнения. Компилятор больше доверяет программисту.

Наконец, Objective-C система динамического типа позволяет иметь такой инструмент, как Interface Builder. Это основная причина, по которой Cocoa и Cocoa Touch имеет более быстрое время разработки; GUI может генерировать код с типами "id" повсеместно, и это десериализуется всякий раз, когда NIB загружается в память. Единственный язык, который близок к Objective-C с точки зрения дизайна интерфейса, - это С# (и VB.NET, конечно), но по цене гораздо более тяжелого приложения.

Я лично предпочитаю работать с более статическим типом проверки, и даже включил параметр "Обращаться с предупреждением как ошибки" в компиляторе Objective-C; Я написал сообщение в блоге об этом:

http://akosma.com/2009/07/16/objective-c-compiler-warnings/

Это особенно полезно, когда вы работаете с разработчиками, которые не знакомы с языком. Это заставляет компилятор скулить чаще обычного:)

Специалисты из статического типа могут не соглашаться со всеми этими пунктами, утверждая, что проверка статического типа позволяет использовать IDE Intellisense и улучшать обслуживание в целом. Я работал с .NET на протяжении многих лет (2001-2006), и я должен сказать, что динамические языки имеют тенденцию создавать меньше кода, читать легче и в целом дают больше свободы для работы. Компромисс (всегда есть компромисс) заключается в том, что во время компиляции меньше информации. Но, как я обычно говорю, компиляторы - это плохой набор тестов. Лучшее, что у ИМХО - это хороший набор тестов, и хорошая группа тестировщиков людей, пытающих ваш код, найти ошибки, независимо от того, какой язык вы выберете.

Ответ 2

Objective-C динамизм сияет не только в том, что каждый объект является id. Скорее, он сияет во время работы Objective-C и простота в использовании. Несколько примеров умного использования среды выполнения самим Apple:

DO позволяет настроить прокси-объект для объекта Obj-C в отдельном приложении/отдельном компьютере. Это делается путем перехвата всего сообщения, отправленного на прокси-объект, его упаковки, отправки его в другое приложение и вызова его там.

KVO реализуется путем динамической замены метода setter, чтобы он автоматически уведомлял наблюдателей. (Ну это на самом деле более тонкое, чем это...)

Аксессоры CoreData генерируются во время выполнения для каждого подкласса NSManagedObject и т.д.

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

Майк Эш написал прекрасную серию сообщений в блогах о том, как работает среда выполнения и как ее эффективно использовать. Вам просто нужно это прочитать! DO, KVO, пересылка сообщений и многое другое. В сети также есть много других интересных сообщений, например забава с kvc и обмен сообщениями более высокого порядка 1, 2.

Ответ 3

На самом деле довольно редко, что вам нужно объявить объект как тип id, так как вы, как правило, знаете, какого типа вы ожидаете. Иногда вы можете использовать тип id<Protocol>, если вы не знаете фактический тип объекта, но знаете, что он должен соответствовать определенному протоколу.

Есть ли определенный сценарий, о котором вы думаете?

Ответ 4

Передающий экземпляр как id является общим при разработке метода действия; подключение кнопки к методу, цель выглядит как doSomething:(id) sender;.

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

-(void) doSomething:(id) sender {
    // Get the sender tag whatever it is
    int tag = [sender tag]; 
    switch(tag) {
        case 1:
            // ...
            break;
        case 2:
            // ...
            break;
    }
}