У меня есть пользовательский контроллер представления контейнеров, который управляет иерархией представления моего приложения. Я знаю, что каждый контроллер является своего рода дочерним элементом этого контроллера контейнера. Я подумал, что было бы неплохо иметь категорию на UIViewController, которая позволила бы мне получить доступ к контроллеру контейнера, независимо от того, где я нахожусь в иерархии.objc_setAssociatedObject в наборах категорий для всех подклассов
Это включает в себя рекурсивную прогулку по иерархии диспетчера, поэтому я подумал, что было бы неплохо попробовать и сделать это только один раз на контроллер. Таким образом, с objc_setAssociatedObject, я установил контейнер, как только нашел его, и установил флаг, чтобы я знал, нужно ли мне ходить по иерархии при последующих вызовах (я планировал сделать недействительным то, что когда контроллер просмотра когда-либо перемещался, но это, вероятно, , и я так далеко не добрался).
В любом случае, это работает в основном по большей части, за исключением того, что мой флаг для того, была ли иерархия пройдена, кажется, прикреплена к UIViewController, а не к определенным подклассам UIViewController.
Я swizzled + load, чтобы попытаться установить значения по умолчанию для моих связанных объектов безрезультатно.
Любые идеи? Как получить связанные объекты в категории для связи с подклассами класса, на котором определена категория?
Вот мой код, для хорошей оценки.
#import "UIViewController+LMPullMenuContainer.h"
#import <objc/runtime.h>
static char const * const CachedKey = "__LM__CachedBoolPullMenuAssociatedObjectKey";
static char const * const PullMenuKey = "__LM__PullMenuAssociatedObjectKey";
@implementation UIViewController (LMPullMenuContainer)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL initSelector = @selector(initWithCoder:);
SEL pullViewInitSelector = @selector(init__LM__Swizzled__WithCoder:);
Method originalMethod = class_getInstanceMethod(self, initSelector);
Method newMethod = class_getInstanceMethod(self, pullViewInitSelector);
BOOL methodAdded = class_addMethod([self class],
initSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod([self class],
pullViewInitSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
});
}
- (instancetype)init__LM__Swizzled__WithCoder:(NSCoder *)coder {
self = [self init__LM__Swizzled__WithCoder:coder];
if (self != nil)
{
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:NO], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return self;
}
- (LMPullMenuContainerViewController*)pullMenuContainerController {
BOOL isCached = [objc_getAssociatedObject(self, CachedKey) boolValue];
if (isCached) {
return objc_getAssociatedObject(self, PullMenuKey);
} else {
return [self pullMenuParentOf:self];
}
}
- (LMPullMenuContainerViewController *)pullMenuParentOf:(UIViewController *)controller {
if (controller.parentViewController) {
if ([controller.parentViewController isKindOfClass:[LMPullMenuContainerViewController class]]) {
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, controller.parentViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return (LMPullMenuContainerViewController *)(controller.parentViewController);
} else {
return [self pullMenuParentOf:controller.parentViewController];
}
} else {
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return nil;
}
}
На данный момент я смирился с настройкой свойства вручную, когда это необходимо.
Что вы подразумеваете под флагом *, прикрепленным к UIViewController, а не к определенным подклассам UIViewController "*? Связанные объекты привязаны к экземпляру класса (sub), а не к классу. –
Это то, что я думал и ожидал, однако, когда я проходил через него, я обнаружил, что 'isCached' возвращает' YES', прежде чем он будет установлен в определенных подклассах (но после того, как он был установлен в других подклассах). –
Возможно, несвязанный, но не следует кэшировать результат также в 'return [self pullMenuParentOf: controller.parentViewController];' case? –