Скрытые возможности Objective-C

Objective-C получает все более широкое распространение благодаря использованию Apple для Mac OS X и iPhone. Каковы некоторые из ваших любимых "скрытых" особенностей языка Objective-C?

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

8 ответов

Метод Swizzling

По сути, во время выполнения вы можете поменять одну реализацию метода на другую.

Вот объяснение с кодом.

Один умный вариант использования для отложенной загрузки общего ресурса: обычно вы реализуете sharedFoo метод получения блокировки, создания foo если необходимо, получить его адрес, снять блокировку, а затем вернуть foo, Это гарантирует, что foo создается только один раз, но каждый последующий доступ тратит время с блокировкой, которая больше не нужна.

С методом Swizzling, вы можете сделать то же самое, что и раньше, за исключением foo был создан Swizzling, чтобы поменять начальную реализацию sharedFoo со вторым, который не проверяет и просто возвращает foo что мы теперь знаем, был создан!

Конечно, метод swizzling может привести к неприятностям, и могут быть ситуации, когда приведенный выше пример - плохая идея, но эй... вот почему это скрытая функция.

позирует

Objective-C позволяет классу полностью заменить другой класс в приложении. Говорят, что замещающий класс "представляет собой" целевой класс. Все сообщения, отправленные целевому классу, затем принимаются классом позирования. Есть некоторые ограничения на то, какие классы могут представлять:

  • Класс может выдавать себя за одного из своих прямых или косвенных суперклассов.
  • Класс представления не должен определять никаких новых переменных экземпляра, которые отсутствуют в целевом классе (хотя он может определять или переопределять методы).
  • Никакие сообщения не должны были быть отправлены в целевой класс до постановки.

Позиционирование, подобно категориям, позволяет глобально расширять существующие классы. Изложение разрешает две особенности, отсутствующие в категориях:

  • Класс позирования может вызывать переопределенные методы через super, включая реализацию целевого класса.
  • Класс позирования может переопределять методы, определенные в категориях.

Пример:

@interface CustomNSApplication : NSApplication
@end

@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu
{
     // do something with menu
}
@end

class_poseAs ([CustomNSApplication class], [NSApplication class]);

Это перехватывает каждый вызов setMainMenu для NSApplication.

Переадресация объекта / метод отсутствует

Когда объекту отправляется сообщение, для которого у него нет метода, система времени выполнения дает ему еще один шанс обработать вызов, прежде чем отказаться. Если объект поддерживает -forward:: метод, среда выполнения вызывает этот метод, передавая ему информацию о необработанном вызове. Возвращаемое значение от переадресованного вызова передается обратно исходному вызывающему методу.

-(retval_t)forward:(SEL)sel :(arglist_t)args {
  if ([myDelegate respondsTo:sel])
 return [myDelegate performv:sel :args]
 else
 return [super forward:sel :args];
 }

Содержимое из Objective-C Pocket Reference

Это очень мощный инструмент, который активно используется в сообществе Ruby для различных DSL, рельсов и т. Д. Создан в Smalltalk, который повлиял как на Objective-C, так и на Ruby.

ISA Switching

Нужно переопределить все поведение объекта? Вы можете изменить класс активного объекта с помощью одной строки кода:

obj->isa = [NewClass class];

Это только изменяет класс, который получает вызовы метода для этого объекта; это не меняет расположение объекта в памяти. Таким образом, это действительно полезно только тогда, когда у вас есть набор классов с одинаковыми ivars (или один с подмножеством других), и вы хотите переключаться между ними.

Один фрагмент кода, который я написал, использует это для отложенной загрузки: он выделяет объект класса A, заполняет пару критических иваров (в данном случае, в основном, это номер записи) и переключает isa указатель для указания на LazyA, Когда любой метод, кроме очень маленького набора, как release а также retain называется, LazyA загружает все данные с диска, завершает заполнение иваров, переключает isa указатель назад на Aи переадресует вызов в реальный класс.

категории

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

Приятно добавлять удобные методы в обычно используемые классы, такие как NSString или NSData.

#include <Foundation/Debug.h>

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

Objective-C Runtime Ссылка

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

Позвольте мне дать способ, которым можно использовать систему времени выполнения.

Допустим, кто-то разрабатывает API-интерфейс для внешней среды, который будет использоваться третьими сторонами. И что кто-то разрабатывает класс в рамках, который абстрактно представляет пакет данных, мы назовем его MLAbstractDataPacket, Теперь дело за приложением, которое связывает в рамках с подклассом MLAbstractDataPacket и определить пакеты данных подкласса. Каждый подкласс должен переопределять метод +(BOOL)isMyKindOfDataPacket:(NSData *)data,

Имея в виду эту информацию...

Было бы хорошо, если MLAbstractDataPacket предоставил удобный метод, который возвратил правильный инициализированный класс для пакета данных, который приходит в форме +(id)initWithDataPacket:(NSData *)data,

Здесь только одна проблема. Суперкласс не знает ни о одном из своих подклассов. Так что здесь вы можете использовать метод времени выполнения objc_getClassList() вместе с objc_getSuperclass() чтобы найти классы, которые являются подклассами MLAbstractDataPacket. Как только у вас есть список подклассов, вы можете попробовать +isMyKindOfDataPacket: на каждом, пока один не найден или не найден.

Справочную информацию об этом можно найти по адресу http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html.

Мне нравится многословный метод именования [myArray writeToFile:myPath atomically:YES]где каждый аргумент имеет метку.

Другие вопросы по тегам