1

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

这涉及到控制器层次结构的递归遍历,所以我认为最好尝试每个控制器只执行一次遍历。因此,使用 objc_setAssociatedObject,我在找到它后设置容器并设置一个标志,以便我知道是否需要在后续调用中遍历层次结构(我计划在 viewcontroller 曾经移动时使其无效,但这可能是矫枉过正,而我并没有那么远)。

无论如何,这在大多数情况下都可以正常工作,除了我的层次结构是否已遍历的标志似乎附加到 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;
    }
}

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

4

1 回答 1

0

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

于 2013-07-01T14:50:29.057 回答