[Хабрахабр] NSProxy, как способ срезать на поворотах.pdf
(
217 KB
)
Pobierz
NSProxy,
как способ срезать �½а поворотах
/
Хабрахабр
30/8/14 19:47
сегод�½я в
14:36
NSProxy,
как способ срезать �½а поворотах
Xcode*, Objective C*,
Разработка под
iOS*
tutorial
Как м�½огие читали в к�½игах, в языке
Objective-C
из�½ачаль�½о есть два кор�½евых класса
— NSObject
и
NSProxy.
И если �½а первом ос�½ова�½о
практически все и с �½им �½евозмож�½о �½е столк�½уться, то вторым пользуются з�½ачитель�½о реже. В этой �½ебольшой статье я опишу те
приме�½е�½ия этого класса, которые приходилось использовать м�½е.
В качестве первого примера, �½апом�½ю о такой штуке, как
UIAppearance —
�½асколько м�½е извест�½о
—
еди�½стве�½�½ое использова�½ие
NSProxy
в
базовых
iOS
фреймворках. Его задача заключается в предваритель�½ом ко�½фигурирова�½ии группы
UIKit
объектов в од�½ом месте. Фактически,
вы описываете �½екоторый �½абор действий, который будет приме�½яться к каждому создаваемому объекту
(таких
как зада�½ие цвета, шрифта и
других), удовлетворяющему �½екоторым условиям
(сейчас
таких условий два: класс объекта и класс объекта, содержащего �½аш объект как
subview).
Есть замечатель�½ая
статья,
посвяще�½�½ая использова�½ию, возмож�½остям и побоч�½ым эффектам такого и�½струме�½та, поэтому более
�½е будем �½а этом оста�½авливаться.
Если чест�½о, это �½е густо. Для такого гордого статуса, как
«оди�½
из двух кор�½евых классов», слишком мало примеров ко�½структив�½ого
использова�½ия. По своему лич�½ому опыту и опыту моих з�½акомых разработчиков
—
это достаточ�½о силь�½о затруд�½яет �½ачаль�½ое по�½има�½ие
этой сущ�½ости и, тем самым, как бы отговаривает �½ас от ее использова�½ия. Но и�½терес�½о же! И со време�½ем стали появляться задачи, для
которых
NSProxy
является потрясающим по удобству и�½струме�½том.
Декорирова�½ие объектов
В реализации паттер�½а
«декоратор»
есть оди�½ достаточ�½о �½еудоб�½ый аспект
—
и�½терфейс, собстве�½�½о, декоратора. Если у �½ас имеется
�½екоторая иерархия объектов, �½апример такая
@interface SomeClass : NSObject <SomeClassInterface>
@end
@interface ChildClass : SomeClass
-(void) additionalMethod;
@end
@interface DecoratorClass : NSObject <SomeClassInterface>
...
ChildClass *instance = [ChildClass new];
id
decoratedInstance = [DecoratorClass decoratedInstanceOf:instance]
То по по�½ят�½ым причи�½ам
decoratedInstance
уже �½е сможет выпол�½ить
additionalMethod.
И �½ам остается либо писать категории, либо
в�½едрять в кор�½евой класс какие-то хуки, либо за�½иматься еще каким-то подоб�½ым �½епотребством. Теперь посмотрим, как это мож�½о
решить, используя
NSProxy.
@interface SomeClass : NSObject;
-(int) getNumber;
-(NSString*) getString;
@end
@interface SimpleDecorator : NSProxy
@property (nonatomic,
strong)
SomeClass *instance;
+(instancetype) decoratedInstanceOf:(SomeClass*)instance;
@end
@implementation SimpleDecorator
-(instancetype) initWithObject:(SomeClass*)object
{
/*
мале�½ькое �½апоми�½а�½ие
- NSProxy
�½е имеет встрое�½�½ого и�½ициализатора, как
NSObject.
Поэтому вызов
[super init]
�½е �½уже�½*/
_instance = object;
return self;
}
+(instancetype) decoratedInstanceOf:(SomeClass*)instance
{
return
[[self alloc] initWithObject:instance];
http://habrahabr.ru/post/235041/
Página 1 de 6
NSProxy,
как способ срезать �½а поворотах
/
Хабрахабр
30/8/14 19:47
}
/*
ос�½ов�½ые методы
NSProxy -
о�½и отвечают за то, что мы делаем с теми методами, которые мы �½е можем обработать самостоятель�½о. В част
�½ости, сюда пойдут все вызовы, которые мы �½е декорировали
-
и самого класса
SomeClass
и его подклассов
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return
[self.instance methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
/*
этой строчкой мы, фактически, говорим, что если мы �½е можем обработать какой-то метод, то пусть его обработает, собстве�½�½о �½аш
объект В да�½�½ом случае так произойдет, �½апример, при вызове метода
getString */
[invocation invokeWithTarget:self.instance];
}
/*
собстве�½�½о декорируемый метод. Так как мы можем его обработать в�½утри и�½ста�½са
NSProxy -
предыдущие два метода вызываться �½е будут
*/
- (int) getNumber
{
return
[self.instance getNumber] +
1;
}
@end
И, собстве�½�½о, пример использова�½ия такой ко�½струкции:
SomeClass *object = [SomeClass new];
object = (SomeClass*)[SimpleDecorator decoratedInstanceOf:object];
NSLog(@"%d", [object getNumber]);
NSLog(@"%@", [object getString]);
При таком подходе �½а месте
SomeClass
может быть любой из его �½аслед�½ик и получе�½�½ый декорирова�½�½ый объект будет коррект�½о
откликаться �½а все отправле�½�½ые ему сообще�½ия.
Отложе�½�½ая �½астройка
По сути, мы сейчас будем решать задачу, похожу �½а ту, которую решает
UIAppearance.
При работе �½ад проектом, построе�½�½ым �½а ос�½ове
архитектуры, предлагаемой библиотектой-паттер�½ом
PureMVC (
en.wikipedia.org/wiki/PureMVC
)
пришлось �½еод�½ократ�½о сталкиваться с
ситуацией, когда в какой-то точке кода мы и�½ициируем цепочку кома�½д-действий, результатом которых будет �½екоторая ко�½текст�½о-
зависимая сущ�½ость
—
�½апример, всплывающее ок�½о, закрытие которого запускает следующие действия, зависящие от ко�½текста, в котором
было вызва�½о ок�½о. Ра�½ьше для этого использовался оди�½ из двух вариа�½тов:
—
Протяжка. Через всю цепочку действий мы передаем допол�½итель�½ые, как-то структурирова�½�½ые да�½�½ые, которые обрабатываем �½а месте
созда�½ия ок�½а. Доволь�½о �½аклад�½ый и �½е оче�½ь красивый способ, особе�½�½о если �½екоторые действия предполагают ветвле�½ие.
— «Я
все з�½аю». Так как
PureMVC
делает ос�½ов�½ые объекты, фактически, си�½глто�½ами и мож�½о в любой моме�½т достучаться до любого
—
мож�½о попытаться получить весь �½еобходимый ко�½текст в кома�½де созда�½ия всплывающего ок�½а, что чревато з�½ачитель�½ым увеличе�½ием
связ�½ости кода.
С помощью
NSProxy
мож�½о добиться �½ем�½ого и�½ого поведе�½ия: Выставить зависимые от ко�½текста свойства в объект ДО того, как о�½ был
созда�½
—
в том месте, где мы з�½аем эти условия. Звучит, ко�½еч�½о, �½есколько абсурд�½о, �½о как-то так о�½о и работает. Мы обращаемся с
имеющимся у �½ас объектом
NSProxy,
как с целевым объектом, которого еще �½ет.
NSProxy
хра�½ит в�½утри себя действия, которые мы
предпри�½яли и когда и�½ста�½цируется объект
—
приме�½яет их к �½ему.
Теперь �½ем�½ого кода.
С�½ачала класс, который мы хотим использовать для отложе�½�½ой �½астройки:
/*
Обыч�½ое всплывающее ок�½о, которое показывает сообще�½ие.
*/
@interface Popup : NSObject;
/*
Ключ �½еобходим для того, чтобы мож�½о было разделять, какие отложе�½�½ые сообще�½ия кто обрабатывает
*/
@property (nonatomic,
strong)
NSString* key;
@property (nonatomic,
strong)
NSString* message;
/*
мы �½е будем �½апрямую обращаться к прокси, обращаясь к �½ему только в ко�½тексте создаваемых объектов, как и в случае
UIAppearance */
+(DelayedActionsProxy*) delayedInitializerForKey:(NSString*)key;
/*
Показать всплывающее окошко. Собстве�½�½о это место и является точкой, когда будут приме�½е�½ы отложе�½�½ые �½астройки
*/
-(void) showPopup
@end
http://habrahabr.ru/post/235041/
Página 2 de 6
NSProxy,
как способ срезать �½а поворотах
/
Хабрахабр
30/8/14 19:47
@implementation Popup
-(void) showPopup
{
[DelayedActionsProxy invokeDelayedInvocationsWithTarget:self];
/*...*/
}
+(DelayedActionsProxy*) delayedInitializerForKey:(NSString*)key
{
return
[DelayedActionsProxy sharedProxyForKey:key fromClass:[self class]];
}
@end
А теперь, собстве�½�½о, прокси
@interface DelayedActionsProxy : NSProxy
+(void) invokeDelayedInvocationsWithTarget:(Popup*) target;
+(instancetype) sharedProxyForKey:(NSString*)key fromClass:(Class)objectClass;
@end
@interface DelayedActionsProxy()
/*
ключ �½ас и�½тересует для разделе�½ия поступающих в прокси отложе�½�½ых вызовов по раз�½ым объектам, а класс объекта
-
для коррект�½ого п
острое�½ия сиг�½атуры вызова
*/
@property (nonatomic,
strong)
NSString *currentKey;
@property (nonatomic,
assign)
Class currentClass;
@property (nonatomic,
strong)
NSMutableDictionary *delayedInvocations;
@end
@implementation DelayedActionsProxy
-(instancetype) init
{
self.delayedInvocations
= [NSMutableDictionary new];
return self;
}
static
DelayedActionsProxy *proxy =
nil;
+(instancetype) sharedProxyForKey:(NSString*)key fromClass:(Class)objectClass
{
static
dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
proxy = [[self alloc] init];
});
proxy.currentKey = key;
proxy.currentClass = objectClass;
return
proxy;
}
/*
Предполагается использова�½ие класса в виде
[[Popup delayedInitializerForKey:@"key"] setText:@"someText"],
то есть к моме�½ту вызова
-
еще �½е существует вызываемого объекта, и уже запол�½ятся поля
currentKey
и
currentClass */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
/*
Так как
currentClass
представляет собой класс, а �½е объект, мы долж�½ы воспользоваться методом
instanceMethodSignature
вместо
m
ethodSignature */
return
[self.currentClass instanceMethodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
if
(!self.delayedInvocations[self.currentKey])
{
self.delayedInvocations[self.currentKey]
= [NSMutableArray new];
}
http://habrahabr.ru/post/235041/
Página 3 de 6
NSProxy,
как способ срезать �½а поворотах
/
Хабрахабр
30/8/14 19:47
/*
мы �½е форвардим получаемые сообще�½ия, а аккурат�½е�½ько их складываем
*/
[self.delayedInvocations[self.currentKey] addObject:invocation];
}
/*
чтобы вызвать их по требова�½ию
*/
+(void) invokeDelayedInvocationsWithTarget:(Popup*) target
{
for
(NSInvocation *invocation in proxy.delayedInvocations[proxy.currentKey])
{
[invocation invokeWithTarget:target];
}
[proxy.delayedInvocations removeObjectForKey:proxy.currentKey];
}
@end
И пример использова�½ия
[[Popup delayedInitializerForKey:@"key"] setText:@"someText"];
Облегче�½ие работы с
UI-объектами
В м�½огопоточ�½ых приложе�½иях �½ередко, из-за �½ев�½иматель�½ости и �½едостаточ�½ого пла�½ирова�½ия �½а �½ачаль�½ом этапе, мож�½о получить код,
который выпол�½яется в другом потоке, �½о которому страсть как �½уж�½о модифицировать
UI (или
БД, или еще что-�½ибудь весьма
чувствитель�½ое к м�½огопоточ�½ому доступу). Код �½ачи�½ает обрастать м�½огочисле�½�½ыми
performSelectorOnMainThread, dispatch_async,
или
того хуже
—
обертками �½ад
NSInvocation,
поскольку
performSelectorOnMainThread
�½е дает использовать больше од�½ого параметра. Почему
бы �½е обзавестись еди�½ой оберткой для этого?
Пусть у �½ас есть какой-то объект
(�½апример
объект в игре �½а
Cocos2D)
@interface Entity : NSObject;
@property (nonatomic,
strong)
CCNode* node;
@end
@implementation Entity
-(void)setRepresentation:(CCNode *)node
{
/*
какое-то количество проверок коррект�½ости выставле�½ия
...
*/
_node = (CCNode*)[MainThreadProxy node];
}
@end
И, собстве�½�½о прокси
@interface MainThreadProxy : NSProxy
+(instancetype) proxyWithObject:(id)object;
/*
это стоит вы�½ести в отдель�½ый метод для того, чтобы мож�½о было делать цепочку действий с од�½им и тем же объектом, без постоя�½�½ого
форварди�½га
*/
-(void)performBlock:(void (^)(id object))block;
@end
@interface MainThreadProxy()
@property (nonatomic,
strong) id
object;
@end
@implementation MainThreadProxy
-(instancetype) initWithObject:(id)object
{
self.object
= object;
return self;
}
http://habrahabr.ru/post/235041/
Página 4 de 6
NSProxy,
как способ срезать �½а поворотах
/
Хабрахабр
30/8/14 19:47
+(instancetype) proxyWithObject:(id)object
{
return
[[self alloc] initWithObject:object];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return
[self.object methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
if
([NSThread isMainThread])
{
[invocation invokeWithTarget:self.object];
}
else
{
[invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:self.object waitUntilDone:YES];
}
}
-(void)performBlock:(void (^)(id object))block
{
if
([NSThread isMainThread])
{
block(self.object);
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{block(self.object);});
}
}
@end
Пример использова�½ия соверше�½�½о обыче�½,
[entity.node render];
Но у �½ас есть гара�½тия, что это может быть вызва�½о соверше�½�½о коррект�½о из любого потока.
Заверше�½ие
В качестве идей для чего мож�½о приме�½ять прокси, мож�½о еще выделить такие вещи как
—
Маскирова�½ие удале�½�½ого объекта
—
работать с объектом, представляющим из себя репрезе�½тацию какого-то сервиса с
�½ефиксирова�½�½ым време�½ем ответа
(�½апример
БД �½а сайте, объект �½а клие�½те, с которым ты соеди�½е�½ по
BlueTooth),
как обыч�½ый объект.
Просто медле�½�½ый.
—
Обертка в Прокси таймера, для того, чтобы стал уместе�½ си�½таксис �½авроде
[[object makeCallWithTimeInterval:1.0f andRepeatCount:2] someMethod];
и другие.
Следует учитывать, что это, разумеется, �½е па�½ацея. Следует быть оче�½ь аккурат�½ым при работе с геттерами, �½апример в приведе�½�½ом выше
примере строчка
[entity.node.effect update];
поведет себя �½е оче�½ь коррект�½о. Это, ко�½еч�½о, мож�½о исправить, �½о у этих исправле�½ий тоже есть своя це�½а. Рекоме�½дую �½е забывать об
этом, когда работаете с
NSProxy.
Post Scriptum
Вообще, о
NSProxy
мож�½о почитать еще �½апример
тут,
или
тут.
NSProxy
глубоко используется в таком и�½струме�½те как
OCMock.
Так или
и�½аче это достаточ�½о гибкий и удоб�½ый и�½струме�½т, пригод�½ый для �½екоторого класса задач
—
и с �½им, как ми�½имум, имеет смыл
оз�½акомиться.
туториал,
objective-c, ios sdk, nsproxy
37,5
—
657
10
i_user
http://habrahabr.ru/post/235041/
Página 5 de 6
Plik z chomika:
Ryjufka
Inne pliki z tego folderu:
[SAMS] Cocoa Programming (Scott Anguish) (2002).pdf
(9155 KB)
Advanced Graphics Programming Using OpenGL (Tom McReynolds, David Blythe).pdf
(13052 KB)
Absolute FreeBSD (Michael W. Lucas).pdf
(7836 KB)
Beginning iOS Game Development (Patrick Alessi).pdf
(20539 KB)
Begining iPad Development for iPhone Developers (Jack Nutting).pdf
(6606 KB)
Inne foldery tego chomika:
[Eng] Android books
BPM Business Process Modeling
IT Ebooks Mega Pack
IT Ebooks Programming
IT epub
Zgłoś jeśli
naruszono regulamin