我有一个自定义容器视图控制器,用于管理我的应用程序的视图层次结构。我知道每个控制器都是这个容器控制器的某种子控制器。我认为在UIViewController上有一个类别会很好,无论我在层次结构中的什么位置,都可以访问容器控制器。
这涉及到控制器层次结构的递归遍历,所以我认为最好尝试每个控制器只遍历一次。因此,使用objc_setAssociatedObject,我在找到容器后设置容器,并设置一个标志,这样我就知道是否需要在后续调用中遍历层次结构(我计划在视图控制器移动的情况下使其无效,但这可能太过分了,我没有做到这一点)。
不管怎么说,这在大多数情况下都很好,只是我的层次结构是否被遍历的标志似乎附加到了UIViewController,而不是UIViewController的特定子类。
我嗖嗖地加了+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;
}
}
现在,我已经放弃了在必要时手动设置属性。
碰巧,上面的代码运行得很好。我的容器控制器在第一次初始化时加载了它管理的所有控制器,而不是在第一次显示控制器时加载,所以对我来说,标志似乎在应该设置之前就已经设置好了。