所有子类的类别集合中的objc_setAssociatedObject

时间:2013-06-21 20:00:58

标签: objective-c uiviewcontroller swizzling

我有一个自定义容器视图控制器,用于管理我的应用程序的视图层次结构。我知道每个控制器都是这个容器控制器的孩子。我认为在UIViewController上有一个允许我访问容器控制器的类别会很好,无论我在层次结构中的哪个位置。

这涉及到控制器层次结构的递归步骤,所以我认为尝试每个控制器只执行一次会很好。所以使用objc_setAssociatedObject,我设置容器一旦找到它并设置一个标志,以便我知道是否需要在后续调用中走分层次结构(我计划在视图控制器移动时使其失效,但这可能是过度杀伤,我没有那么远。)

无论如何,除了我的关于层次结构是否已被遍历的标志似乎附加到UIViewController而不是UIViewController的特定子类之外,大部分都可以正常工作。

我调整了加载以尝试在我的关联对象上设置默认值无效。

有什么想法吗?如何获取类别中的关联对象以与定义类别的类的子类关联?

这是我的代码,为了更好的衡量标准。

#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;
    }
}

现在我已经辞职,必要时手动设置属性。

1 个答案:

答案 0 :(得分:0)

碰巧,上面的代码工作得很好。我的容器控制器在第一次初始化时加载了它管理的所有控制器,而不是在第一次显示控制器时,所以对我来说看起来好像标志已经设置好了。