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





Objective-C разрешает классу полностью заменить другой класс в приложении. Говорят, что замещающий класс «изображает» целевой класс. Все сообщения, отправленные целевому классу, вместо этого принимаются позирующим классом. Существуют некоторые ограничения на то, какие классы могут позировать:
Позирование, как и категории, позволяет использовать глобальное расширение существующих классов. Постановка разрешает две функции, отсутствующие в категориях:
Пример:
@interface CustomNSApplication : NSApplication
@end
@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu
{
// do something with menu
}
@end
class_poseAs ([CustomNSApplication class], [NSApplication class]);
Это перехватывает каждый вызов setMainMenu для NSApplication.
Class_poseAs устарел, начиная с версии 10.5. Замена делает то же самое для каждого метода, что теперь явно поддерживается во время выполнения.
Упс, забыл связать документы.
#include <Foundation/Debug.h>
Множество инструментов для отслеживания утечек памяти, преждевременных отключений и многого другого в этом заголовочном файле.
Это специфично для Cocoa (может быть, и OpenSTEP?), А не языковой функции, хотя, поскольку никто не использует ObjC, кроме как с Cocoa, я думаю, это не имеет большого значения
Итак, это не работает для Cocoa Touch, то есть iOS? Что именно он делает?
Пересылка объекта / отсутствует метод
Когда объекту отправляется сообщение, для которого у него нет метод, исполняющая система дает ему еще один шанс обработать звонок перед тем, как отказаться. Если объект поддерживает -forward :: method, среда выполнения вызывает этот метод, передавая ему информация о необработанном звонке. Возвращаемое значение из переадресованный вызов возвращается к исходному вызывающему абоненту метод.
-(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.
Я бы рекомендовал вместо этого использовать документированный метод «- [NSObject forwardInvocation:]». Недокументированный метод -[Object forward::] давно устарел и больше не доступен в современной среде выполнения Objective-C. Такие как 64-битные и ARM на устройствах iOS.
Кто-нибудь знает, работает ли это еще? Кажется, я не могу найти никакой документации по этому поводу.
По сути, во время выполнения вы можете заменить одну реализацию метода другой.
Один из умных вариантов использования - это отложенная загрузка разделяемого ресурса: обычно вы реализуете метод sharedFoo, приобретая блокировку, создавая foo, если необходимо, получая его адрес, снимая блокировку, а затем возвращая foo. Это гарантирует, что foo создается только один раз, но каждый последующий доступ тратит время на блокировку, которая больше не нужна.
С помощью смены методов вы можете сделать то же самое, что и раньше, за исключением того, что после создания foo используйте swizzling для замены первоначальной реализации sharedFoo на вторую, которая не выполняет никаких проверок и просто возвращает foo, который, как мы теперь знаем, был создан. !
Конечно, замена методов может вызвать у вас проблемы, и могут быть ситуации, когда приведенный выше пример - плохая идея, но эй ... вот почему это функция скрытый.
Справочник по среде выполнения Objective-C
Легко забыть, что синтаксический сахар 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.
Категории
Используя категории, вы можете добавлять методы во встроенные классы без создания подклассов. Полная ссылка.
Приятно добавлять удобные методы к часто используемым классам, таким как NSString или NSData.
Отличный совет, но не совсем скрытый… :)
Мне нравится подробное именование методов, например [myArray writeToFile:myPath atomically:YES], где у каждого аргумента есть метка.
Как это спрятано? ... :)
@Psionides: Это довольно очевидно для людей, которые на самом деле используют этот язык, я согласен, но большинство людей, переходящих на какой-то другой язык, похоже, упускают этот простой факт. по крайней мере, люди, с которыми я разговаривал.
Вы не можете его пропустить! Нет, если вы действительно хотите передавать сообщения!
Нужно переопределить все поведение объекта? Фактически вы можете изменить класс активного объекта с помощью одной строчки кода:
obj->isa = [NewClass class];
Это изменяет только класс, который принимает вызовы методов для этого объекта; он не меняет расположение объекта в памяти. Таким образом, это действительно полезно только тогда, когда у вас есть набор классов с одинаковыми ivars (или один с подмножеством других), и вы хотите переключаться между ними.
Один фрагмент кода, который я написал, использует это для отложенной загрузки: он выделяет объект класса A, заполняет пару критических ivars (в данном случае в основном номер записи) и переключает указатель isa на LazyA. Когда вызывается любой метод, кроме очень маленького набора, такого как release и retain, LazyA загружает все данные с диска, завершает заполнение ivars, переключает указатель isa обратно на A и перенаправляет вызов реальному классу.
Боже мой, я никогда об этом не думал. Мне нужно это проверить, потому что это великолепно. Интересно, есть ли какие-нибудь неприятные побочные эффекты?
Я попробовал это, чтобы ответить на stackoverflow.com/questions/874906/…, но получаю ошибку компиляции: Instance variable 'isa' is protected. Есть ли способ обойти это? См. stackoverflow.com/questions/8512793/…
@MattDiPasquale Главный ответ правильный - современный способ сделать это - использовать objc_setClass ().
См. Сообщения meta.stackexchange.com/questions/56669/…, meta.stackexchange.com/questions/57226/… и связанные с ними мета-сообщения.