Mac No known class method for selector 'alloc' ?

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
8,668
4,242
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.
 
Last edited:

lee1210

macrumors 68040
Jan 10, 2005
3,182
1
Dallas, TX
If RMEMatch doesn't exist it's not clear if it supports +alloc. Define a skeleton or stub implementation.

-Lee
 

chown33

Moderator
Staff member
Aug 9, 2009
8,748
5,090
vertical
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.
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
8,668
4,242
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.
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).
 

chown33

Moderator
Staff member
Aug 9, 2009
8,748
5,090
vertical
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...
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).​
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
8,668
4,242
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).​
Thanks for all the explanation, chown, I get it all now.
 

ElectricSheep

macrumors 6502
Feb 18, 2004
498
3
Wilmington, DE
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.
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.
 

chown33

Moderator
Staff member
Aug 9, 2009
8,748
5,090
vertical
I would question this design as it makes the base class dependent on the implementation details of its own sub-classes.
Not necessarily.

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

What exactly are you going for here? RMETest should only ever have to know about other RMETest objects, or objects which are more abstract.
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.
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
8,668
4,242
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.
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.
 

ElectricSheep

macrumors 6502
Feb 18, 2004
498
3
Wilmington, DE
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.
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.
 

Sydde

macrumors 68020
Aug 17, 2009
2,124
3,876
IOKWARDI
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.
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.
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
8,668
4,242
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.
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.
 

Sydde

macrumors 68020
Aug 17, 2009
2,124
3,876
IOKWARDI
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.
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
 

chown33

Moderator
Staff member
Aug 9, 2009
8,748
5,090
vertical
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
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.
 

Sydde

macrumors 68020
Aug 17, 2009
2,124
3,876
IOKWARDI
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.
Yeah, I considered that as well, as nil would be silent.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.