Getting all subclasses

  • Hi

    Is there a way to get all subclases of a class. I have a class that I
    would call abstract (although I did not find any real way to declare
    some methods as virtual) and would like to get a list of all "real"
    subclass implementations.

    Thanks

    laurent
  • On Jun 17, 2008, at 10:19 PM, Laurent Cerveau wrote:

    > Hi
    >
    > Is there a way to get all subclases of a class. I have a class that
    > I would call abstract (although I did not find any real way to
    > declare some methods as virtual) and would like to get a list of all
    > "real" subclass implementations.
    >
    > Thanks
    >
    > laurent

    If you have a bunch of classes that you want to check you can call
    [SomeClass isSubclassOf:anotherClass].

    If you wrote the class yourself, what I would do is override the +
    initialize method to add the class to some kind of global array that
    you can access later. The runtime sends initialize to each class in a
    program exactly one time just before the class is sent its first
    message from within the program. So this will only work for each class
    that is actually used within the application.

    Omar Qazi
    Hello, Galaxy!
    1.310.294.1593
  • If you need a list of subclasses of a class, there isn't a built-in
    function - you have to test them one at a time. However I had the
    exact same need recently and wrote a little helper class to do it. One
    thing I ran into in my situation is that I really needed to prevent
    +initialize from ever getting called, because some low-level classes
    in the Cocoa framework wrote certain messages to the log when they
    were invoked (because they are obsolete and shouldn't be used) but
    testing for the class itself was enough to trigger this.

    So, my code uses a lower-level function. Here it is:

    @interface DKRuntimeHelper : NSObject

    + (NSArray*)    allClasses;
    + (NSArray*)    allClassesOfKind:(Class) aClass;

    @end

    @implementation DKRuntimeHelper

    + (NSArray*)    allClasses
    {
    return [self allClassesOfKind:[NSObject class]];
    }

    + (NSArray*)    allClassesOfKind:(Class) aClass
    {
    // returns a list of all Class objects that are of kind <aClass> or a
    subclass of it currently registered in the runtime. This caches the
    // result so that the relatively expensive run-through is only
    performed the first time

    static NSMutableDictionary* cache = nil;

    if ( cache == nil )
      cache = [[NSMutableDictionary alloc] init];

    // is the list already cached?

    NSArray* cachedList = [cache objectForKey:NSStringFromClass( aClass )];

    if ( cachedList != nil )
      return cachedList;

    // if here, list wasn't in the cache, so build it the hard way

    NSMutableArray* list = [NSMutableArray array];

    Class*  buffer = NULL;
    Class  cl;

    int i, numClasses = objc_getClassList( NULL, 0 );

    if( numClasses > 0 )
    {
      buffer = malloc( sizeof(Class) * numClasses );

      NSAssert( buffer != nil, @"couldn't allocate the buffer");

      (void) objc_getClassList( buffer, numClasses );

      // go through the list and carefully check whether the class can
    respond to isSubclassOfClass: - if so, add it to the list.

      for( i = 0; i < numClasses; ++i )
      {
      cl = buffer[i];

      if( classIsSubclassOfClass( cl, aClass ))
        [list addObject:cl];
      }

      free( buffer );
    }

    // save in cache for next time

    [cache setObject:list forKey:NSStringFromClass( aClass )];

    return list;
    }

    @end

    BOOL    classIsNSObject( const Class aClass )
    {
    // returns YES if <aClass> is an NSObject derivative, otherwise NO.
    It does this without invoking any methods on the class being tested.

    return classIsSubclassOfClass( aClass, [NSObject class]);
    }

    BOOL    classIsSubclassOfClass( const Class aClass, const Class subclass )
    {
    Class temp = aClass;
    int  match = -1;

    while(( 0 != ( match = strncmp( temp->name, subclass->name,
    strlen( subclass->name )))) && ( NULL != temp->super_class ))
      temp = temp->super_class;

    return ( match == 0 );
    }

    hth,

    cheers, Graham

    On 18 Jun 2008, at 3:19 pm, Laurent Cerveau wrote:

    > Hi
    >
    > Is there a way to get all subclases of a class. I have a class that
    > I would call abstract (although I did not find any real way to
    > declare some methods as virtual) and would like to get a list of all
    > "real" subclass implementations.
    >
    > Thanks
    >
    > laurent
  • Fantastic!
    Thanks to you and Omar.

    laurent

    On Jun 18, 2008, at 9:28 AM, Graham Cox wrote:

    > If you need a list of subclasses of a class, there isn't a built-in
    > function - you have to test them one at a time. However I had the
    > exact same need recently and wrote a little helper class to do it.
    > One thing I ran into in my situation is that I really needed to
    > prevent +initialize from ever getting called, because some low-level
    > classes in the Cocoa framework wrote certain messages to the log
    > when they were invoked (because they are obsolete and shouldn't be
    > used) but testing for the class itself was enough to trigger this.
    >
    > So, my code uses a lower-level function. Here it is:
    >
    >
    >
    >
    > @interface DKRuntimeHelper : NSObject
    >
    > + (NSArray*)    allClasses;
    > + (NSArray*)    allClassesOfKind:(Class) aClass;
    >
    >
    > @end
    >
    >
    >
    > @implementation DKRuntimeHelper
    >
    >
    > + (NSArray*)    allClasses
    > {
    > return [self allClassesOfKind:[NSObject class]];
    > }
    >
    >
    > + (NSArray*)    allClassesOfKind:(Class) aClass
    > {
    > // returns a list of all Class objects that are of kind <aClass> or
    > a subclass of it currently registered in the runtime. This caches the
    > // result so that the relatively expensive run-through is only
    > performed the first time
    >
    > static NSMutableDictionary* cache = nil;
    >
    > if ( cache == nil )
    > cache = [[NSMutableDictionary alloc] init];
    >
    > // is the list already cached?
    >
    > NSArray* cachedList = [cache
    > objectForKey:NSStringFromClass( aClass )];
    >
    > if ( cachedList != nil )
    > return cachedList;
    >
    > // if here, list wasn't in the cache, so build it the hard way
    >
    > NSMutableArray*    list = [NSMutableArray array];
    >
    > Class*            buffer = NULL;
    > Class            cl;
    >
    > int i, numClasses = objc_getClassList( NULL, 0 );
    >
    > if( numClasses > 0 )
    > {
    > buffer = malloc( sizeof(Class) * numClasses );
    >
    > NSAssert( buffer != nil, @"couldn't allocate the buffer");
    >
    > (void)    objc_getClassList( buffer, numClasses );
    >
    > // go through the list and carefully check whether the class can
    > respond to isSubclassOfClass: - if so, add it to the list.
    >
    > for( i = 0; i < numClasses; ++i )
    > {
    > cl = buffer[i];
    >
    > if( classIsSubclassOfClass( cl, aClass ))
    > [list addObject:cl];
    > }
    >
    > free( buffer );
    > }
    >
    > // save in cache for next time
    >
    > [cache setObject:list forKey:NSStringFromClass( aClass )];
    >
    > return list;
    > }
    >
    > @end
    >
    >
    >
    > BOOL    classIsNSObject( const Class aClass )
    > {
    > // returns YES if <aClass> is an NSObject derivative, otherwise NO.
    > It does this without invoking any methods on the class being tested.
    >
    > return classIsSubclassOfClass( aClass, [NSObject class]);
    > }
    >
    >
    > BOOL    classIsSubclassOfClass( const Class aClass, const Class
    > subclass )
    > {
    > Class    temp = aClass;
    > int        match = -1;
    >
    > while(( 0 != ( match = strncmp( temp->name, subclass->name,
    > strlen( subclass->name )))) && ( NULL != temp->super_class ))
    > temp = temp->super_class;
    >
    > return ( match == 0 );
    > }
    >
    >
    >
    >
    > hth,
    >
    >
    > cheers, Graham
    >
    >
    >
    >
    > On 18 Jun 2008, at 3:19 pm, Laurent Cerveau wrote:
    >
    >> Hi
    >>
    >> Is there a way to get all subclases of a class. I have a class that
    >> I would call abstract (although I did not find any real way to
    >> declare some methods as virtual) and would like to get a list of
    >> all "real" subclass implementations.
    >>
    >> Thanks
    >>
    >> laurent
    >
  • Another option would be to write a +load method in your abstract
    superclass that will be automatically called for any subclasses and
    register them with the superclass.

    Mike.

    On 18 Jun 2008, at 16:45, Laurent Cerveau wrote:

    > Fantastic!
    > Thanks to you and Omar.
    >
    > laurent
    >
    > On Jun 18, 2008, at 9:28 AM, Graham Cox wrote:
    >
    >> If you need a list of subclasses of a class, there isn't a built-in
    >> function - you have to test them one at a time. However I had the
    >> exact same need recently and wrote a little helper class to do it.
    >> One thing I ran into in my situation is that I really needed to
    >> prevent +initialize from ever getting called, because some low-
    >> level classes in the Cocoa framework wrote certain messages to the
    >> log when they were invoked (because they are obsolete and shouldn't
    >> be used) but testing for the class itself was enough to trigger this.
    >>
    >> So, my code uses a lower-level function. Here it is:
    >>
    >>
    >>
    >>
    >> @interface DKRuntimeHelper : NSObject
    >>
    >> + (NSArray*)    allClasses;
    >> + (NSArray*)    allClassesOfKind:(Class) aClass;
    >>
    >>
    >> @end
    >>
    >>
    >>
    >> @implementation DKRuntimeHelper
    >>
    >>
    >> + (NSArray*)    allClasses
    >> {
    >> return [self allClassesOfKind:[NSObject class]];
    >> }
    >>
    >>
    >> + (NSArray*)    allClassesOfKind:(Class) aClass
    >> {
    >> // returns a list of all Class objects that are of kind <aClass>
    >> or a subclass of it currently registered in the runtime. This
    >> caches the
    >> // result so that the relatively expensive run-through is only
    >> performed the first time
    >>
    >> static NSMutableDictionary* cache = nil;
    >>
    >> if ( cache == nil )
    >> cache = [[NSMutableDictionary alloc] init];
    >>
    >> // is the list already cached?
    >>
    >> NSArray* cachedList = [cache
    >> objectForKey:NSStringFromClass( aClass )];
    >>
    >> if ( cachedList != nil )
    >> return cachedList;
    >>
    >> // if here, list wasn't in the cache, so build it the hard way
    >>
    >> NSMutableArray*    list = [NSMutableArray array];
    >>
    >> Class*            buffer = NULL;
    >> Class            cl;
    >>
    >> int i, numClasses = objc_getClassList( NULL, 0 );
    >>
    >> if( numClasses > 0 )
    >> {
    >> buffer = malloc( sizeof(Class) * numClasses );
    >>
    >> NSAssert( buffer != nil, @"couldn't allocate the buffer");
    >>
    >> (void)    objc_getClassList( buffer, numClasses );
    >>
    >> // go through the list and carefully check whether the class can
    >> respond to isSubclassOfClass: - if so, add it to the list.
    >>
    >> for( i = 0; i < numClasses; ++i )
    >> {
    >> cl = buffer[i];
    >>
    >> if( classIsSubclassOfClass( cl, aClass ))
    >> [list addObject:cl];
    >> }
    >>
    >> free( buffer );
    >> }
    >>
    >> // save in cache for next time
    >>
    >> [cache setObject:list forKey:NSStringFromClass( aClass )];
    >>
    >> return list;
    >> }
    >>
    >> @end
    >>
    >>
    >>
    >> BOOL    classIsNSObject( const Class aClass )
    >> {
    >> // returns YES if <aClass> is an NSObject derivative, otherwise
    >> NO. It does this without invoking any methods on the class being
    >> tested.
    >>
    >> return classIsSubclassOfClass( aClass, [NSObject class]);
    >> }
    >>
    >>
    >> BOOL    classIsSubclassOfClass( const Class aClass, const Class
    >> subclass )
    >> {
    >> Class    temp = aClass;
    >> int        match = -1;
    >>
    >> while(( 0 != ( match = strncmp( temp->name, subclass->name,
    >> strlen( subclass->name )))) && ( NULL != temp->super_class ))
    >> temp = temp->super_class;
    >>
    >> return ( match == 0 );
    >> }
    >>
    >>
    >>
    >>
    >> hth,
    >>
    >>
    >> cheers, Graham
    >>
    >>
    >>
    >>
    >> On 18 Jun 2008, at 3:19 pm, Laurent Cerveau wrote:
    >>
    >>> Hi
    >>>
    >>> Is there a way to get all subclases of a class. I have a class
    >>> that I would call abstract (although I did not find any real way
    >>> to declare some methods as virtual) and would like to get a list
    >>> of all "real" subclass implementations.
    >>>
    >>> Thanks
    >>>
    >>> laurent
    >>

  • IIRC, +initialize call the super implementation if the child class
    does not override it, but not the +load method that is called exactly
    once per class and per categorie.
    So +load will not be called automatically.

    Le 18 juin 08 à 18:57, Mike Abdullah a écrit :

    > Another option would be to write a +load method in your abstract
    > superclass that will be automatically called for any subclasses and
    > register them with the superclass.
    >
    > Mike.
    >
    > On 18 Jun 2008, at 16:45, Laurent Cerveau wrote:
    >
    >> Fantastic!
    >> Thanks to you and Omar.
    >>
    >> laurent
    >>
    >> On Jun 18, 2008, at 9:28 AM, Graham Cox wrote:
    >>
    >>> If you need a list of subclasses of a class, there isn't a built-
    >>> in function - you have to test them one at a time. However I had
    >>> the exact same need recently and wrote a little helper class to do
    >>> it. One thing I ran into in my situation is that I really needed
    >>> to prevent +initialize from ever getting called, because some low-
    >>> level classes in the Cocoa framework wrote certain messages to the
    >>> log when they were invoked (because they are obsolete and
    >>> shouldn't be used) but testing for the class itself was enough to
    >>> trigger this.
    >>>
    >>> So, my code uses a lower-level function. Here it is:
    >>>
    >>>
    >>>
    >>>
    >>> @interface DKRuntimeHelper : NSObject
    >>>
    >>> + (NSArray*)    allClasses;
    >>> + (NSArray*)    allClassesOfKind:(Class) aClass;
    >>>
    >>>
    >>> @end
    >>>
    >>>
    >>>
    >>> @implementation DKRuntimeHelper
    >>>
    >>>
    >>> + (NSArray*)    allClasses
    >>> {
    >>> return [self allClassesOfKind:[NSObject class]];
    >>> }
    >>>
    >>>
    >>> + (NSArray*)    allClassesOfKind:(Class) aClass
    >>> {
    >>> // returns a list of all Class objects that are of kind <aClass>
    >>> or a subclass of it currently registered in the runtime. This
    >>> caches the
    >>> // result so that the relatively expensive run-through is only
    >>> performed the first time
    >>>
    >>> static NSMutableDictionary* cache = nil;
    >>>
    >>> if ( cache == nil )
    >>> cache = [[NSMutableDictionary alloc] init];
    >>>
    >>> // is the list already cached?
    >>>
    >>> NSArray* cachedList = [cache
    >>> objectForKey:NSStringFromClass( aClass )];
    >>>
    >>> if ( cachedList != nil )
    >>> return cachedList;
    >>>
    >>> // if here, list wasn't in the cache, so build it the hard way
    >>>
    >>> NSMutableArray*    list = [NSMutableArray array];
    >>>
    >>> Class*            buffer = NULL;
    >>> Class            cl;
    >>>
    >>> int i, numClasses = objc_getClassList( NULL, 0 );
    >>>
    >>> if( numClasses > 0 )
    >>> {
    >>> buffer = malloc( sizeof(Class) * numClasses );
    >>>
    >>> NSAssert( buffer != nil, @"couldn't allocate the buffer");
    >>>
    >>> (void)    objc_getClassList( buffer, numClasses );
    >>>
    >>> // go through the list and carefully check whether the class can
    >>> respond to isSubclassOfClass: - if so, add it to the list.
    >>>
    >>> for( i = 0; i < numClasses; ++i )
    >>> {
    >>> cl = buffer[i];
    >>>
    >>> if( classIsSubclassOfClass( cl, aClass ))
    >>> [list addObject:cl];
    >>> }
    >>>
    >>> free( buffer );
    >>> }
    >>>
    >>> // save in cache for next time
    >>>
    >>> [cache setObject:list forKey:NSStringFromClass( aClass )];
    >>>
    >>> return list;
    >>> }
    >>>
    >>> @end
    >>>
    >>>
    >>>
    >>> BOOL    classIsNSObject( const Class aClass )
    >>> {
    >>> // returns YES if <aClass> is an NSObject derivative, otherwise
    >>> NO. It does this without invoking any methods on the class being
    >>> tested.
    >>>
    >>> return classIsSubclassOfClass( aClass, [NSObject class]);
    >>> }
    >>>
    >>>
    >>> BOOL    classIsSubclassOfClass( const Class aClass, const Class
    >>> subclass )
    >>> {
    >>> Class    temp = aClass;
    >>> int        match = -1;
    >>>
    >>> while(( 0 != ( match = strncmp( temp->name, subclass->name,
    >>> strlen( subclass->name )))) && ( NULL != temp->super_class ))
    >>> temp = temp->super_class;
    >>>
    >>> return ( match == 0 );
    >>> }
    >>>
    >>>
    >>>
    >>>
    >>> hth,
    >>>
    >>>
    >>> cheers, Graham
    >>>
    >>>
    >>>
    >>>
    >>> On 18 Jun 2008, at 3:19 pm, Laurent Cerveau wrote:
    >>>
    >>>> Hi
    >>>>
    >>>> Is there a way to get all subclases of a class. I have a class
    >>>> that I would call abstract (although I did not find any real way
    >>>> to declare some methods as virtual) and would like to get a list
    >>>> of all "real" subclass implementations.
    >>>>
    >>>> Thanks
    >>>>
    >>>> laurent
    >>>


    >
  • That depends on what you mean by "automatically".  If you read the
    documentation, you'll see this bit:

    "The load message is sent to classes and categories that are both
    dynamically loaded and statically linked, but only if the newly loaded
    class or category implements a method that can respond."

    So, the method will be called "automatically" upon load, but only if
    each subclass implements its own +load implementation, which probably
    is more trouble than you want.  Also, there's no guarantee about the
    order +load calls are issued, so a subclass's +load might get called
    before its superclass's, and +load is considered bad performance juju,
    since it makes the system do work regardless of whether or not the
    application uses the class.  +initialize doesn't have those problems,
    but it does have a different one -- notice the second sentence,
    quoting here from the +initialize documentation:

    "The runtime sends initialize to each class in a program exactly one
    time just before the class, or any class that inherits from it, is
    sent its first message from within the program. (Thus the method may
    never be invoked if the class is not used.)"

    That makes it unsuitable (or at least kind of risky) for the find-all-
    the-subclasses task originally described.  objc_getClassList really is
    the right approach, though I'll note that caching the result doesn't
    work if new classes get loaded dynamically.

    --Chris N.

    On Jun 18, 2008, at 10:24 AM, Jean-Daniel Dupas wrote:

    > IIRC, +initialize call the super implementation if the child class
    > does not override it, but not the +load method that is called
    > exactly once per class and per categorie.
    > So +load will not be called automatically.
    >
    >
    > Le 18 juin 08 à 18:57, Mike Abdullah a écrit :
    >
    >> Another option would be to write a +load method in your abstract
    >> superclass that will be automatically called for any subclasses and
    >> register them with the superclass.
    >>
    >> Mike.
    >>
    >> On 18 Jun 2008, at 16:45, Laurent Cerveau wrote:
    >>
    >>> Fantastic!
    >>> Thanks to you and Omar.
    >>>
    >>> laurent
    >>>
    >>> On Jun 18, 2008, at 9:28 AM, Graham Cox wrote:
    >>>
    >>>> If you need a list of subclasses of a class, there isn't a built-
    >>>> in function - you have to test them one at a time. However I had
    >>>> the exact same need recently and wrote a little helper class to
    >>>> do it. One thing I ran into in my situation is that I really
    >>>> needed to prevent +initialize from ever getting called, because
    >>>> some low-level classes in the Cocoa framework wrote certain
    >>>> messages to the log when they were invoked (because they are
    >>>> obsolete and shouldn't be used) but testing for the class itself
    >>>> was enough to trigger this.
    >>>>
    >>>> So, my code uses a lower-level function. Here it is:
    >>>>
    >>>>
    >>>>
    >>>>
    >>>> @interface DKRuntimeHelper : NSObject
    >>>>
    >>>> + (NSArray*)    allClasses;
    >>>> + (NSArray*)    allClassesOfKind:(Class) aClass;
    >>>>
    >>>>
    >>>> @end
    >>>>
    >>>>
    >>>>
    >>>> @implementation DKRuntimeHelper
    >>>>
    >>>>
    >>>> + (NSArray*)    allClasses
    >>>> {
    >>>> return [self allClassesOfKind:[NSObject class]];
    >>>> }
    >>>>
    >>>>
    >>>> + (NSArray*)    allClassesOfKind:(Class) aClass
    >>>> {
    >>>> // returns a list of all Class objects that are of kind <aClass>
    >>>> or a subclass of it currently registered in the runtime. This
    >>>> caches the
    >>>> // result so that the relatively expensive run-through is only
    >>>> performed the first time
    >>>>
    >>>> static NSMutableDictionary* cache = nil;
    >>>>
    >>>> if ( cache == nil )
    >>>> cache = [[NSMutableDictionary alloc] init];
    >>>>
    >>>> // is the list already cached?
    >>>>
    >>>> NSArray* cachedList = [cache
    >>>> objectForKey:NSStringFromClass( aClass )];
    >>>>
    >>>> if ( cachedList != nil )
    >>>> return cachedList;
    >>>>
    >>>> // if here, list wasn't in the cache, so build it the hard way
    >>>>
    >>>> NSMutableArray*    list = [NSMutableArray array];
    >>>>
    >>>> Class*            buffer = NULL;
    >>>> Class            cl;
    >>>>
    >>>> int i, numClasses = objc_getClassList( NULL, 0 );
    >>>>
    >>>> if( numClasses > 0 )
    >>>> {
    >>>> buffer = malloc( sizeof(Class) * numClasses );
    >>>>
    >>>> NSAssert( buffer != nil, @"couldn't allocate the buffer");
    >>>>
    >>>> (void)    objc_getClassList( buffer, numClasses );
    >>>>
    >>>> // go through the list and carefully check whether the class
    >>>> can respond to isSubclassOfClass: - if so, add it to the list.
    >>>>
    >>>> for( i = 0; i < numClasses; ++i )
    >>>> {
    >>>> cl = buffer[i];
    >>>>
    >>>> if( classIsSubclassOfClass( cl, aClass ))
    >>>> [list addObject:cl];
    >>>> }
    >>>>
    >>>> free( buffer );
    >>>> }
    >>>>
    >>>> // save in cache for next time
    >>>>
    >>>> [cache setObject:list forKey:NSStringFromClass( aClass )];
    >>>>
    >>>> return list;
    >>>> }
    >>>>
    >>>> @end
    >>>>
    >>>>
    >>>>
    >>>> BOOL    classIsNSObject( const Class aClass )
    >>>> {
    >>>> // returns YES if <aClass> is an NSObject derivative, otherwise
    >>>> NO. It does this without invoking any methods on the class being
    >>>> tested.
    >>>>
    >>>> return classIsSubclassOfClass( aClass, [NSObject class]);
    >>>> }
    >>>>
    >>>>
    >>>> BOOL    classIsSubclassOfClass( const Class aClass, const Class
    >>>> subclass )
    >>>> {
    >>>> Class    temp = aClass;
    >>>> int        match = -1;
    >>>>
    >>>> while(( 0 != ( match = strncmp( temp->name, subclass->name,
    >>>> strlen( subclass->name )))) && ( NULL != temp->super_class ))
    >>>> temp = temp->super_class;
    >>>>
    >>>> return ( match == 0 );
    >>>> }
    >>>>
    >>>>
    >>>>
    >>>>
    >>>> hth,
    >>>>
    >>>>
    >>>> cheers, Graham
    >>>>
    >>>>
    >>>>
    >>>>
    >>>> On 18 Jun 2008, at 3:19 pm, Laurent Cerveau wrote:
    >>>>
    >>>>> Hi
    >>>>>
    >>>>> Is there a way to get all subclases of a class. I have a class
    >>>>> that I would call abstract (although I did not find any real way
    >>>>> to declare some methods as virtual) and would like to get a list
    >>>>> of all "real" subclass implementations.
    >>>>>
    >>>>> Thanks
    >>>>>
    >>>>> laurent
    >>>>


    >>

previous month june 2008 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
Go to today