Resolved No known class method for selector 'alloc' ?

Discussion in 'Mac Programming' started by ArtOfWarfare, Dec 24, 2013.

  1. ArtOfWarfare, Dec 24, 2013
    Last edited: Dec 25, 2013

    ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #1
    I have two classes:
    1 - RMETest, an object which holds an array of RMEMatches.
    2 - RMEMatch, an object which inherits from RMETest.

    Right now, the following code refuses to compile because the line written in red is marked with "No known class method for selector 'alloc'"

    I think my problem may have to do with circular references, but I'm not sure how to break them.

    Here's some code.

    Code:
    // RMETest.h
    
    #import <Foundation/Foundation.h>
    
    @interface RMETest : NSObject
    
    @property RMETest *parentTest;
    // ...
    // Other properties not relevant
    // ...
    
    - (RMETest *)initWithParentTest:(RMETest *)parentTest;
    - (void)addNewMatch;
    
    @end
    Code:
    // RMETest.m
    #import "RMETest.h"
    
    @class RMEMatch; // <- It needs to know that the class exists, but to avoid circular imports I'm using @class instead.
    
    @interface RMETest () {
        NSMutableArray *_subtests;
    }
    
    @end
    
    @implementation RMETest
    
    - (void)addSubtest:(RMETest *)subtest {
        [_subtests addObject:subtest];
    }
    
    - (RMETest *)initWithParentTest:(RMETest *)parentTest {
        if (self = [super init]) {
            self.parentTest = parentTest;
        }
        return self;
    }
    
    - (void)addNewMatch {
       [COLOR=RED] [self addSubtest:[[RMEMatch alloc] initWithParentTest:self]]; // This is the line causing problems! It says "No known class method for selector 'alloc'![/COLOR]
    }
    Code:
    // RMEMatch.h
    #import "RMETest.h"
    
    @interface RMEMatch : RMETest
    
    @end
    
    // RMEMatch.m
    #import "RMEMatch.h"
    
    @implementation RMEMatch
    
    @end
    I haven't actually gotten around to implementing RMEMatch yet, and as I was copying and pasting my code I realized that it's not going to work because _subtests isn't actually created anywhere, but my problem isn't with issues occurring at runtime: It's not even compiling right now!

    Maybe I'm just up coding too late right now, but I can't think of why this wouldn't work.
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    If RMEMatch doesn't exist it's not clear if it supports +alloc. Define a skeleton or stub implementation.

    -Lee
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    Replace these lines in "RMETest.m":
    Code:
    #import "RMETest.h"
    
    @class RMEMatch; // <- It needs to know that the class exists, but to avoid circular imports I'm using @class instead.
    
    with this single line:
    Code:
    #import "RMEMatch.h"
    
    Doing this will tell the implementation of RMETest everything it needs to know about the interface of both the RMETest class and the RMEMatch class.

    There are no circular references in the headers of "RMETest.h" and "RMEMatch.h", unless you've removed them for posting. If there were, you'd need an @class in the header itself, not in an implementation (.m) file.
     
  4. ArtOfWarfare thread starter macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #4
    I could have sworn you had a typo in your answer, but I did exactly what you said and now it works. That's a really funky import trail...

    Test.m imports Match.h which imports Test.h... not a pattern that I've ever seen before, as far as I can remember
    (A.m -> B.h -> A.h).
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    If RMEMatch wasn't a subclass of RMETest, what would the imports be? This seems right to me:
    Code:
    #import "RMETest.h"
    #import "RMEMatch.h"
    
    You need RMETest.h, because that's the class you're implementing.

    You also need RMEMatch.h, because that's a class you're calling methods on. You can't use just an @class for RMEMatch. That's just a forward declaration, which is a promise to the compiler that a typename is a classname, as distinct from other name types, such as a typedef, a variable, or a function.

    Since you need both class headers for the case when the classes have no inheritance relationship, it should also be apparent you need both class headers when they do have an inheritance relationship. That is, the same basic rule applies: if you're going to refer to methods of any class, you need its header.

    Now, since RMEMatch is a subclass of RMETest, and "RMEMatch.h" would need to import "RMETest.h" as part of its own imports, it should be clear that simply importing "RMEMatch.h" gets both interface declarations. You could do two imports, and as long as they're in order, that would still work. But it should also be clear that due to inheritance, only a single import is needed.


    As for the use of @class, it's just a forward declaration. It's not an interface definition, or anything else. And it's really only effective in limited circumstances, specifically headers.

    Apple used to have a good explanation in the language reference doc, but they don't have that doc online any more. Here's their doc from 2009:
    http://cagt.bu.edu/w/images/b/b6/Objective-C_Programming_Language.pdf

    Search it for @class, and read that whole subsection (Referring to Other Classes). Here's an excerpt:
    Since declarations like this simply use the class name as a type and don’t depend on any details of the class interface (its methods and instance variables), the @class directive gives the compiler sufficient forewarning of what to expect. However, where the interface to a class is actually used (instances created, messages sent), the class interface must be imported. Typically, an interface file uses @class to declare classes, and the corresponding implementation file imports their interfaces (since it will need to create instances of those classes or send them messages).​
     
  6. ArtOfWarfare thread starter macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #6
    Thanks for all the explanation, chown, I get it all now.
     
  7. ElectricSheep macrumors 6502

    ElectricSheep

    Joined:
    Feb 18, 2004
    Location:
    Wilmington, DE
    #7
    I would question this design as it makes the base class dependent on the implementation details of its own sub-classes. What exactly are you going for here? RMETest should only ever have to know about other RMETest objects, or objects which are more abstract.
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    Not necessarily.

    Knowledge of the names of one's subclasses doesn't necessarily require knowledge of the internal implementation of those subclasses.

    Maybe he's trying to implement a class cluster. Hard to say without knowing more about the actual implementation.

    A class cluster violates the rule of "RMETest should only ever have to know about other RMETest objects, or objects which are more abstract", yet it's clearly a well-known pattern in the Cocoa Foundation classes.
     
  9. ArtOfWarfare thread starter macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #9
    I spent some time trying to justify this design before reaching the conclusion that you are correct, this was misdesigned. Although RMETest and RMEMatch share many things in common, they should have a common abstract parent, because, for example, I don't want RMEMatches to contain one another.

    I'm just not a big fan of abstract classes in Obj-C because Obj-C doesn't have any particularly good ways of making them... you just have to document them as abstract and hope other people don't try instantiating them.
     
  10. ElectricSheep macrumors 6502

    ElectricSheep

    Joined:
    Feb 18, 2004
    Location:
    Wilmington, DE
    #10
    In this case, you might consider defining a protocol. In that way, you can publicly define the behavior of the abstract class while leaving the specifics of the implementation hidden away (for what you provide) as well as leaving it up to the developer. Yes, an RMETest could be a singular test or a collection of sub-tests, but the behavior shouldn't be concerned with how "sub-tests" are implemented; array, priority-queue, or some other collection.
     
  11. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #11
    Really? Hope no one ever instantiates them? Seems pretty straightforward to me: your abstract class would have an -init method that would check to see if the class being instantiated is the abstract class itself and, if so, destroy the instance and return nil. The point of an abstract class is to provide a set of basic methods that the subclass can use and/or override and some specific methods that the subclass must override in order to be useful.
     
  12. ArtOfWarfare thread starter macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #12
    Yeah, and then subclasses it and follows the proper design of calling super's init and... Crap, they have a nil object and now have to debug their init method to find why they're getting nil.
     
  13. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #13
    See, if the -init method says something like "if ( [self class] == [TheAbstractClass class] ) /*destroy and return nil*/; else /*do regular init*/;", the subclasses will get what is expected, because they will not show as TheAbstractClass
     
  14. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #14
    Personally, I would throw an exception rather than return nil. Mostly because they're harder to miss, but also because it can include an informative error message. In Cocoa, programming errors are one of the strongest use-cases for throwing exceptions.
     
  15. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #15
    Yeah, I considered that as well, as nil would be silent.
     

Share This Page