【问题标题】:Objective-C: Get list of subclasses from superclassObjective-C:从超类中获取子类列表
【发布时间】:2011-10-27 23:22:16
【问题描述】:

在 Objective-C 中有一种方法可以询问一个类是否有任何子类实现。

我有一个基类,它有多个子类。我想遍历所有子类并在每个子类上执行一个类选择器。

编辑

我有一组可以处理某些类型数据的类。每个处理器都是一个基类的子类,该基类提供每个处理器所需的方法。

每个类都知道它可以处理哪些数据,并且某些类可以比其他类更好地处理某些类型的数据。

我希望在每个类上都有一个类方法,它可以向工厂类提供响应,说是的,我可以处理该数据,并指出它可以处理它的能力。

然后,工厂将根据哪个类说它可以最好地处理数据来决定要实例化哪个类。

我也从 2009 年发现了这个问题(我在发布此问题之前进行了搜索,但没有找到任何东西)Discover subclasses of a given class in Obj-C

编辑 2

+ (void)load 方法看起来是我正在寻找的完美解决方案。所以我现在有以下内容:

+ (void)registerSubclass:(Class)subclass {
    NSLog(@"Registered %@", subclass);
}

在我的基类中,这是我的潜艇。

+(void)load {
    [BaseSwitch registerSubclass:[self class]];
}

现在显示每个子类的调试消息。

我的下一个问题是(可能是一个愚蠢的问题),我如何存储在 registerSubclass 方法中注册的类。有没有办法让我以后可以读取类变量?

编辑 3

在这里找到一些示例代码A simple, extensible HTTP server in Cocoa

这给我留下了以下内容,毕竟说了又做了,似乎很简单。但我想我会把它放在这里以备将来参考。

@implementation BaseSwitch

static NSMutableArray *registeredSubclasses;

+ (void)registerSubclass:(Class)subclass {
    if (registeredSubclasses == nil) {
        registeredSubclasses = [[NSMutableArray alloc] init];
    }

    [registeredSubclasses addObject:subclass];

    NSLog(@"Registered %@", subclass);
}

+ (void)logSubclasses {
    for (int i = 0; i < [registeredSubclasses count]; i++) {
        NSLog(@"%@", [registeredSubclasses objectAtIndex:i]);
    }
}

@end

感谢大家的建议,如果有其他问题,我会再多几天不回答这个问题。

【问题讨论】:

  • 为什么你不能在你链接的那个问题中使用 Mike 的建议?让你的超类公开一个 API 来为数据类型注册(子)类,并让每个子类使用该 API 来告诉超类它可以处理哪些数据类型。这也可以由不同的类而不是超类导出。
  • @Bavarious,这可能是我要走的路。但我有兴趣看看是否还有其他方法。
  • 您最好以某种方式注册课程。动态发现很有吸引力,但 Objective-C 确实不是为这种模式设计的。显式注册还具有您可以搜索一个点或一个简单字符串以生成所有注册人清单的优势。
  • 在这种情况下,您会从使用观察者设计模式中受益。您是否探索过以这种方式编码您的项目?

标签: objective-c


【解决方案1】:

Cocoa with Love 中的示例可以导致 EXC_I386_GPFLT,它代表一般保护故障。我们应该使用普通的 while 循环来检查 superClass 是否有效,而不是 do while 循环。

#import <objc/runtime.h>

NSArray * ClassGetSubclasses(Class parentClass)
{
    int numClasses = objc_getClassList(NULL, 0);

    // According to the docs of objc_getClassList we should check
    // if numClasses is bigger than 0.
    if (numClasses <= 0) {
        return [NSMutableArray array];
    }

    int memSize = sizeof(Class) * numClasses;
    Class *classes = (__unsafe_unretained Class *)malloc(memSize);

    if (classes == NULL && memSize) {
        return [NSMutableArray array];
    }

    numClasses = objc_getClassList(classes, numClasses);

    NSMutableArray<Class> *result = [NSMutableArray new];

    for (NSInteger i = 0; i < numClasses; i++) {
        Class superClass = classes[i];

        // Don't add the parent class to list of sublcasses
        if (superClass == parentClass) {
            continue;
        }

        // Using a do while loop, like pointed out in Cocoa with Love,
        // can lead to EXC_I386_GPFLT, which stands for General
        // Protection Fault and means we are doing something we
        // shouldn't do. It's safer to use a regular while loop to
        // check if superClass is valid.
        while (superClass && superClass != parentClass) {
            superClass = class_getSuperclass(superClass);
        }

        if (superClass) {
            [result addObject:classes[i]];
        }
    }

    free(classes);

    return result;
}

查看以下 GitHub 问题以供参考:

【讨论】:

    【解决方案2】:

    这个函数给你一个类的所有子类:

    #import <objc/runtime.h>
    
    NSArray *ClassGetSubclasses(Class parentClass)
    {
      int numClasses = objc_getClassList(NULL, 0);
      Class *classes = NULL;
    
      classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
      numClasses = objc_getClassList(classes, numClasses);
    
      NSMutableArray *result = [NSMutableArray array];
      for (NSInteger i = 0; i < numClasses; i++)
      {
        Class superClass = classes[i];
        do
        {
          superClass = class_getSuperclass(superClass);
        } while(superClass && superClass != parentClass);
    
        if (superClass == nil)
        {
          continue;
        }
    
        [result addObject:classes[i]];
      }
    
      free(classes);
    
      return result;
    }
    

    取自Cocoa with Love

    【讨论】:

    • @ravoorinandan 我认为你的意思是它不是为 ARC 编译的。我已经更新了它以支持 ARC。
    • @ThomasW 非常感谢。会尝试并让你知道。 :)
    • 我制作了this Class Hierarchy Logger,它使用类似于@ThomasW 建议的方法,然后循环遍历子类。如果您需要更深入地了解如何迭代子类层次结构,它可能会很有用。希望对您有所帮助!
    • 这个问题,正如作者所描述的那样,它不会给你在运行时转换的子类,而是所有子类,即 (MySubClass*)MyParentClass。
    • 有效,但使用时要小心。我用 objc_getClassList 获取类列表花了大约 60 毫秒。
    【解决方案3】:

    你永远不能列出一个类的子类。在(几乎)任何编程语言中。这是面向对象编程的基本属性之一。

    考虑更改您的对象模型。

    您可能想要的是创建一个抽象类和不同的子类,但您不应该从抽象类访问子类。您应该创建另一个对象(工厂类)来注册子类并在需要时选择合适的对象。

    请注意,您无法从类本身有效地注册一个类。对于要执行的类代码,必须首先加载该类。这意味着,您必须在其他某个类中导入其标头,这意味着您实际上是通过导入其标头来注册该类。 有两种可能的解决方案:

    1. 您的工厂类必须知道所有子类的名称(在编译时或读取某些配置文件时)。
    2. 您的工厂类有一个方法,任何人都可以将要注册的类的名称传递给该方法。如果您希望外部库注册新的子类,这是正确的解决方案。然后就可以把子类注册码放到库的主头了。

    【讨论】:

    • 不可能列出类的每个可能的子类,但是 OOP 没有阻止列出在给定时间点存在于给定程序中的子类的属性。在 ObjC 中,这可以通过 objc_getClassList() 完成,然后内省类。这不是一个好主意,但 OOP 并没有禁止它。
    • 您是对的,但从超类中执行此操作肯定违反 OOP 的原则 :) 但是,必须在某个地方加载类,并且您需要知道它们的名称(或它们时的文件)已定义)加载它们。
    • @Sulthan 它是如何“违反原则”的,为什么在什么文件中声明它们很重要?运行时有一个类列表,您可以随意查询它。我用过的每一种动态语言都有这个基本特性。
    猜你喜欢
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多