类别中的objc_setAssociatedObject为所有子类设置



我有一个自定义容器视图控制器,用于管理我的应用程序的视图层次结构。我知道每个控制器都是这个容器控制器的某种子控制器。我认为在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;
    }
}

现在,我已经放弃了在必要时手动设置属性。

碰巧,上面的代码运行得很好。我的容器控制器在第一次初始化时加载了它管理的所有控制器,而不是在第一次显示控制器时加载,所以对我来说,标志似乎在应该设置之前就已经设置好了。

相关内容

  • 没有找到相关文章

最新更新