Telinir
Telinir

Reputation: 139

Error in Obj-C "unrecognized selector sent to instance"

Some information: I have two different classes, "House" and "Treasury". They are both inside the same NSMutableArray called "structures". House has a (int) variable called "ownership". I use a for loop (100 loops) to create houses and treasuries across an area. Then I have another loop that loops through "structures" and assigns an "ownership" integer to it. In that loop I use for(Houses *h in structures) but for some reason the loop also loops through the "treasury"s in structures as well. Treasury has no ownership variable though and so I get that error telling me that "setOwnership:" does not exist. I am also using Cocos2D but that shouldn't matter.

Here is what the code looks like:

// Variable
NSMutableArray *structures;


-(void) InitializeStructures
{
    structures = [[NSMutableArray alloc]init];

    for(int i = 0; i < initialColonies; i++)
    {
        House *h = [[House alloc]initWithFile:@"House.png"];
        h.position = [self ReturnPositionOnMap]; // Returns a point in playing area

        Treasury *t = [[Treasury alloc]initWithFile:@"Treasury.png"];
        t.position = [self ReturnFarPointNearOrigin:h.position];

        [structures addObject:h];
        [self addChild:h z:1];

        [structures addObject:t];
        [self addChild:t z:1];
    }
}

-(void) AssignOwnerships
{
    for(House *h in structures)
    {
        // Simplified to only show where the error occurs.
        h.ownership = [self ReturnID]; // Error occurs here.
        // The error ONLY occurs when it is iterating through a Treasury.
    }
}

Structure:

#import "CCSprite.h"
#import "ImportedGoods.h"

@interface Structure : CCSprite
{
    int _integrity;
    int _ID;
    bool _occupied;
    int _occupiedTimeLeft;
    int _occupiedTimeMax;
    Faction _faction;
}
@property(nonatomic, assign) int integrity;
@property(nonatomic, assign) int ID;
@property(nonatomic, assign) bool occupied;
@property(nonatomic, assign) int occupiedTimeLeft;
@property(nonatomic, assign) int occupiedTimeMax;
@property(nonatomic, assign) Faction faction;
@end

#import "Structure.h"

@implementation Structure

@synthesize integrity = _integrity;
@synthesize ID = _ID;
@synthesize occupied = _occupied;
@synthesize occupiedTimeLeft = _occupiedTimeLeft;
@synthesize occupiedTimeMax = _occupiedTimeMax;
@synthesize faction = _faction;

@end

House:

#import "Structure.h"
#import "ImportedGoods.h"

@interface House : Structure
{
    int _ownership;
}
@property(nonatomic, assign) int ownership;
@end

#import "House.h"

@implementation House

@synthesize ownership = _ownership;

@end

Treasury:

#import "Structure.h"

@interface Treasury : Structure
{
    int _storedWood;
    int _storedWater;
    int _storedStone;
    int _storedFood;
}
@property(nonatomic, assign) int storedWood;
@property(nonatomic, assign) int storedWater;
@property(nonatomic, assign) int storedStone;
@property(nonatomic, assign) int storedFood;
@end

#import "Treasury.h"

@implementation Treasury

@synthesize storedFood = _storedFood;
@synthesize storedWood = _storedWood;
@synthesize storedStone = _storedStone;
@synthesize storedWater = _storedWater;

@end

This is the error:

2011-11-07 18:45:29.016 Virtual World[788:10a03] -[Treasury setOwnership:]: unrecognized selector sent to instance 0x7498cc0
2011-11-07 18:45:29.022 Virtual World[788:10a03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Treasury setOwnership:]: unrecognized selector sent to instance 0x7498cc0'
*** First throw call stack:
(0x17fb052 0x198cd0a 0x17fcced 0x1761f00 0x1761ce2 0xcbe33 0xc2193 0xc1e5f 0xc1a34 0x3fc81 0xc185e 0x176151d 0x1761437 0x39b25 0x36ee2 0x17fcec9 0x91f91 0x92adf 0x94991 0x869a30 0x869c56 0x850384 0x843aa9 0x27affa9 0x17cf1c5 0x1734022 0x173290a 0x1731db4 0x1731ccb 0x27ae879 0x27ae93e 0x841a9b 0xc07ef 0x20c5 0x1)
terminate called throwing an exception(gdb) 

I know that I can just make a different NSMutableArray called "treasuries" but I'd like to keep everything in one array if possible. I want to know why it still iterates through treasuries even though I specified to iterate through "House *h in structures". If you need any additional information comment below and I will add. Thanks!

Upvotes: 0

Views: 212

Answers (2)

Jesse Black
Jesse Black

Reputation: 7976

Your assumption that

for(House *h in structures)
    {
        // Simplified to only show where the error occurs.
        h.ownership = [self ReturnID]; // Error occurs here.
        // The error ONLY occurs when it is iterating through a Treasury.
    }

will only pick the House objects from the array is wrong. You will get every object from the array. House *h in structures tells the compiler that array is full of House objects.

You should change it to

for (id h in structures){
// Simplified to only show where the error occurs.
     if ([h respondsToSelector:@selector(setOwnership:)]){
          h.ownership = [self ReturnID];
     }
}

Upvotes: 3

Craig Otis
Craig Otis

Reputation: 32054

When you use fast enumeration in Objective-C, which is the syntax:

for (MyClass *item in someArray) {
    ...
}

What you're doing is looping over every item in the array, treating each item as a pointer to a MyClass object, whether that's what the item actually is or not. It does not do what you think it does, which is only iterate over the items in the array that are of the type you declare in your loop.

What you should probably do is store your House and Treasury items in separate arrays. Do you have a specific reason for putting them in the same structure?

If you really want to store them together, you can use:

for (id item in structures) {
    if ([item isKindOfClass:[House class]]) {
        House *house = (House *) item;
        [house setOwnership:...];
    } else {
        continue;
    }
}

Upvotes: 4

Related Questions