Скрытые функции Objective-C
Objective-C получает широкое распространение благодаря использованию Apple для Mac OS X и iPhone. Каковы некоторые из ваших любимых "скрытых" функций языка Objective-C?
- Одна функция для каждого ответа.
- Приведите пример и краткое описание функции, а не ссылку на документацию.
- Пометьте эту функцию, используя заголовок в качестве первой строки.
Ответы
Ответ 1
Метод Swizzling
В принципе, во время выполнения вы можете заменить одну реализацию метода другим.
Вот объяснение с кодом.
Один разумный вариант использования - для ленивой загрузки общего ресурса: обычно вы реализуете метод sharedFoo
, приобретая блокировку, при необходимости создавая foo
, получая его адрес, освобождая блокировку, а затем возвращаете foo
. Это гарантирует, что foo
создается только один раз, но каждый последующий доступ тратит время с блокировкой, которая больше не нужна.
С помощью метода swizzling вы можете сделать то же самое, что и раньше, за исключением того, что после создания foo
используйте swizzling для замены первоначальной реализации sharedFoo
со второй, которая не выполняет никаких проверок, и просто возвращает foo
, который мы теперь знаем, был создан!
Конечно, метод swizzling может вызвать у вас проблемы, и могут быть ситуации, когда приведенный выше пример - плохая идея, но эй... вот почему это скрытая функция.
Ответ 2
Позирует
Objective-C позволяет классу полностью заменить другой класс внутри приложения. Заменяющий класс называется "представлять как" целевой класс. Все сообщения, отправленные в целевой класс, затем принимаются классом posing. Есть некоторые ограничения, по которым классы могут представлять:
- Класс может представлять только один из его прямых или косвенных суперклассов
- Класс posing не должен определять новые переменные экземпляра, отсутствующие в целевом классе (хотя он может определять или переопределять методы).
- Перед созданием сообщения не должны отправляться сообщения в целевой класс.
Позиционирование, аналогично категориям, позволяет глобально увеличивать существующие классы. Постановка разрешает две функции, отсутствующие в категориях:
- Класс posing может вызывать переопределенные методы через super, таким образом включая реализацию целевого класса.
- Класс posing может переопределять методы, определенные в категориях.
Пример:
@interface CustomNSApplication : NSApplication
@end
@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu
{
// do something with menu
}
@end
class_poseAs ([CustomNSApplication class], [NSApplication class]);
Это перехватывает каждое обращение setMainMenu к NSApplication.
Ответ 3
Пересылка объекта/метод отсутствует
Когда объект отправляется сообщение, для которого оно отсутствует
метод, система времени выполнения дает ему еще один шанс справиться
вызов перед сдачей. Если объект поддерживает
-forward:: метод, runtime вызывает этот метод, передавая его
информация о необработанном вызове. Возвращаемое значение из
переадресованный вызов передается обратно исходному абоненту
метод.
-(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 Карманная ссылка
Это очень мощный инструмент и широко используется в сообществе Ruby для различных DSL и рельсов и т.д. Возникла в Smalltalk, которая повлияла как на Objective-C, так и на Ruby.
Ответ 4
Переключение ISA
Необходимо переопределить все поведение объекта? Фактически вы можете изменить класс активного объекта с помощью одной строки кода:
obj->isa = [NewClass class];
Это изменяет класс, получающий вызовы метода для этого объекта; он не изменяет расположение объекта в памяти. Таким образом, это действительно полезно, когда у вас есть набор классов с одинаковыми ivars (или один с подмножеством других), и вы хотите переключаться между ними.
Один кусок кода, который я написал, использует для ленивой загрузки: он выделяет объект класса A
, заполняет пару критических иваров (в данном случае, главным образом, номер записи) и переключает указатель isa
на укажите LazyA
. Когда вызывается какой-либо метод, отличный от очень небольшого набора, такого как release
и retain
, LazyA
загружает все данные с диска, заканчивает заполнение ivars, переключает указатель isa
обратно на A
и пересылает вызов реального класса.
Ответ 5
#include <Foundation/Debug.h>
Множество инструментов для отслеживания утечек памяти, преждевременных деаллоков и т.д. в этом файле заголовка.
Ответ 6
Категории
Используя категории, вы можете добавлять методы к встроенным классам без подкласса. Полная ссылка.
Приятно добавлять удобные методы к обычно используемым классам, таким как NSString или NSData.
Ответ 7
Objective-C Справочник по времени выполнения
Легко забыть, что синтаксический сахар Objective-C преобразуется в нормальные вызовы функций C, которые являются объектами Object-C Runtime. Вероятно, вам никогда не понадобится вникать и использовать что-либо во время выполнения. Вот почему я считаю это "скрытой функцией".
Позвольте мне указать способ использования системы времени выполнения.
Скажем, что кто-то разрабатывает внешний API рамки, который будет использоваться третьими лицами. И кто-то проектирует класс в рамках, который абстрактно представляет пакет данных, мы назовем его MLAbstractDataPacket
. Теперь это относится к приложению, которое связывает структуру с подклассом MLAbstractDataPacket
и определяет пакеты данных подкласса. Каждый подкласс должен переопределять метод +(BOOL)isMyKindOfDataPacket:(NSData *)data
.
С учетом этой информации...
Было бы неплохо, если MLAbstractDataPacket
предоставил удобный метод, который вернул правильный инициализированный класс для пакета данных, который приходит в форме +(id)initWithDataPacket:(NSData *)data
.
Здесь есть только одна проблема. Суперкласс не знает ни о каком из своих подклассов. Таким образом, здесь вы можете использовать метод runtime objc_getClassList()
вместе с objc_getSuperclass()
, чтобы найти классы, которые являются подклассами MLAbstractDataPacket. После того, как у вас есть список подклассов, вы можете попробовать +isMyKindOfDataPacket:
на каждом, пока он не будет найден или не найден.
Информацию об этом можно найти на http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html.
Ответ 8
Мне нравится подробный метод, называемый как [myArray writeToFile:myPath atomically:YES]
, где каждый аргумент имеет метку.