【问题标题】:How to add a category to a "hidden" class如何将类别添加到“隐藏”类
【发布时间】:2012-04-28 06:53:51
【问题描述】:

有没有办法将类别添加到您无法访问其头文件的类?

出于测试目的,我想向UITableViewCellDeleteConfirmationControl 添加一个类别,但该类(据我所知)是私有框架的一部分。

我该怎么做?


详细说明(根据 mihirios 的要求):

我正在尝试扩展 Frank 测试框架,以模拟点击当您尝试删除 UITableViewCell 时出现的确认按钮(大红色“删除”按钮)。 Frank 将tap 方法添加到UIControl。出于某种原因,Frank 通常的点击控件方式不适用于UITableViewCellDeleteConfirmationControl 类(它是UIControl 的子类)。

我已经创建了一个解决方法。我给UITableViewCell添加了一个分类,方法如下。

- (BOOL)confirmDeletion {
    if (![self showingDeleteConfirmation]) {
        return NO;
    }
    UITableView *tableView = (UITableView *)[self superview];
    id <UITableViewDataSource> dataSource = [tableView dataSource];
    NSIndexPath *indexPath = [tableView indexPathForCell:self];
    [dataSource tableView:tableView
       commitEditingStyle:UITableViewCellEditingStyleDelete
        forRowAtIndexPath:indexPath];
    return YES;
}

这会找到表的数据源并调用其tableView:commitEditingStyle:forRowAtIndexPath: 方法,该方法(根据UITableView 的文档)是用户点击确认按钮时系统所做的。

这可行,但我更愿意通过向其添加tap 方法来使UITableViewCellDeleteConfirmationControl 看起来是一个可点击的按钮,覆盖Frank 的默认方法。 tap 方法会找到包含确认按钮的单元格,然后调用 [cell confirmDeletion]

当我尝试为 UITableViewCellDeleteConfirmationControl 声明一个类别时,编译器抱怨它“无法解析接口 'UITableViewCellDeleteConfirmationControl'。”

当我尝试使用某人使用类转储生成的头文件时,链接器抱怨它找不到符号 _OBJC_CLASS_$_UITableViewCellDeleteConfirmationControl。

【问题讨论】:

  • 能否详细说明您的问题?

标签: objective-c ios objective-c-category


【解决方案1】:

出于测试目的,您始终可以使用NSClassFromString 获取类对象,然后使用class_replaceMethod 运行时方法来执行您需要的任何操作。详情请见Objective-C Runtime Reference

【讨论】:

【解决方案2】:

据我所知,您不能使用类别,但您可以在运行时手动添加方法。

一种可能的方法是,创建一个新类,实现您想要的方法,然后使用适当的 objc 运行时函数将此方法发送到 UITableViewCellDeleteConfirmationControl。有一些事情需要注意,比如存储原始函数以供以后在重载时使用,同样在你的“类别”类中,当你想调用 super 时你必须注意,因为这不起作用,你有改为使用 objc 运行时函数 objc_msgSendSuper。

只要你不需要调用 super 就可以了:

#import <objc/runtime.h>
#import <objc/message.h>

void implementInstanceMethods(Class src, Class dest) {
    unsigned int count;
    Method *methods = class_copyMethodList(src, &count);

    for (int i = 0; i < count; ++i) {
        IMP imp = method_getImplementation(methods[i]);
        SEL selector = method_getName(methods[i]);
        NSString *selectorName = NSStringFromSelector(selector);
        const char *types = method_getTypeEncoding(methods[i]);

    class_replaceMethod(dest, selector, imp, types);        
    }
    free(methods);
}

调用方法的好点在main.m中,例如:

@autoreleasepool {
        implementInstanceMethods([MyCategory class], NSClassFromString(@"UITableViewCellDeleteConfirmationControl"));
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([YourAppDelegate class]));
}

但我不知道你为什么不把确认处理移到控制器类中。

【讨论】:

  • 为什么我不把它放在控制器类中:这是一个通用的测试框架,它对任何特定的应用程序一无所知。
【解决方案3】:

只要编译器可以(最终)链接到有问题的类,您就可以为它创建一个类别。更重要的问题是如何设计类别,因为您似乎无权访问原始类的源代码。

【讨论】:

  • 那么,如何创建类别?使用简单的@interface Foo (Bar),编译器会抱怨它没有Foo 的接口声明。
猜你喜欢
  • 2017-05-02
  • 2022-01-12
  • 2015-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多