PDA

View Full Version : Custom Class Won't Respond to Method?




ArtOfWarfare
Oct 8, 2009, 10:36 PM
I'm getting the following warning when I try to compile:
'PolygonShape' may not respond to '+initWithNumberOfSides:minimumNumberOfSides:maximumNumberOfSides:'

Here's the code that seems relevant...

from PolygonShape.h:
@interface PolygonShape : NSObject
-(id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max;

from PolygonShape.m:
-(id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max
{
if (self = [super init])
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"Triangle", 3,
@"Square", 4,
@"Pentagon", 5,
@"Hexagon", 6,
@"Heptagon", 7,
@"Octagon", 8,
@"Nonagon", 9,
@"Decagon", 10,
@"Hendecagon", 11,
@"Dodecagon", 12,
nil];

numberOfSides = sides;
minimumNumberOfSides = min;
maximumNumberOfSides = max;
angleInDegrees = (180 * (numberOfSides - 2)/numberOfSides);
angleInRadians = (angleInDegrees * M_PI / 180);
name = [dictionary objectForKey: [NSString stringWithFormat:@"%d", sides]];
}
return self;
}

-(id)init
{
if (self = [super init])
{
return [self initWithNumberOfSides:5 minimumNumberOfSides:3 maximumNumberOfSides:10];
}
}

-(NSString *)description
{
return [NSString stringWithFormat:@"Hello, I am a %d-sided polygon (aka, a '%@',) with angles of %f degrees (%f radians.)", numberOfSides, name, angleInDegrees, angleInRadians];
}


and from the main file
#import "PolygonShape.h"
PolygonShape *test = [PolygonShape initWithNumberOfSides:5 minimumNumberOfSides:3 maximumNumberOfSides:10];
NSLog (@"%@", [test description]);

I don't know that I'm understanding how to properly use init and alloc...

Edit: The log ends up showing this:

2009-10-08 23:30:27.879 Assignment 1B - WhatATool[10579:a0f] +[PolygonShape initWithNumberOfSides:minimumNumberOfSides:maximumNumberOfSides:]: unrecognized selector sent to class 0x100003750
2009-10-08 23:30:27.880 Assignment 1B - WhatATool[10579:a0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[PolygonShape initWithNumberOfSides:minimumNumberOfSides:maximumNumberOfSides:]: unrecognized selector sent to class 0x100003750'

Which seems to match up with what the warning says during compile.



admanimal
Oct 8, 2009, 10:53 PM
You want


PolygonShape *test = [[PolygonShape alloc] initWithNumberOfSides....]

gnasher729
Oct 9, 2009, 04:27 AM
1. You are trying to call a class method of class PolygonShape. That's what the warning says very clearly: "+" is for class methods, "-" is for instance methods.

2. Your init method ends up calling [super init] twice, once directly, once indirectly. That's asking for trouble.

ArtOfWarfare
Oct 9, 2009, 10:34 AM
Thank you admanimal, it works now :)

As for your points gnasher729...
1.) I'm still finding it difficult to keep track of which is which... I left it as a - in front of the method though and it seems to work anyways. (And actually, it gives me an error about using instance variables in a class method if I use + instead.)

2.) Thanks for pointing that out, I removed the call for [super init] from my init method.

So yeah, thanks guys :)

whooleytoo
Oct 9, 2009, 11:18 AM
Thank you admanimal, it works now :)

As for your points gnasher729...
1.) I'm still finding it difficult to keep track of which is which... I left it as a - in front of the method though and it seems to work anyways. (And actually, it gives me an error about using instance variables in a class method if I use + instead.)

2.) Thanks for pointing that out, I removed the call for [super init] from my init method.

So yeah, thanks guys :)

alloc is a class method, which you probably will rarely need to override. init... methods are instance methods, which you will often override.

The alloc & init combination is very common. You tell the PolygonShape class to allocate a new PolygonShape object; and then calling the initWithNumberOfSides... method of that object to initialise it. The alloc class method won't refer to any instance variables, the init... instance method most likely will.

ArtOfWarfare
Oct 9, 2009, 11:57 AM
Thanks for the explanation whooleytoo :)

Just a quick question on dealloc... am I doing it right here?

NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
[polygonArray addObject:[[PolygonShape alloc] initWithNumberOfSides:4
minimumNumberOfSides:3
maximumNumberOfSides:7]];
// I do some stuff with it in the middle, then I'm done with it...
for (PolygonShape *shape in polygonArray)
{
[shape dealloc];
}
[polygonArray dealloc];

My dealloc method for the PolygonShape class is...

-(void)dealloc
{
[super dealloc];
NSLog (@"Deallocated.");
}

Is that really all that I need? I know that it's calling the dealloc method at the right times because it logs "Deallocated" each time, but I'm not sure that [super dealloc] is truly all I need to properly deallocate it...

Guiyon
Oct 9, 2009, 12:09 PM
In nearly all of the normal cases you should never call -dealloc. This is called by the system when either the GC is triggered or the retain count hits 0. Calling -dealloc before this point will cause undefined behavior. Either use the GC behavior or the retain/release method of maintaining objects.

The correct way of handing polygonArray is:
NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
[polygonArray addObject:[[PolygonShape alloc] initWithNumberOfSides:4
minimumNumberOfSides:3
maximumNumberOfSides:7]];
// I do some stuff with it in the middle, then I'm done with it...
for (PolygonShape *shape in polygonArray)
{
//[shape dealloc];
[shape release];
}
//[polygonArray dealloc];
[polygonArray release];

chown33
Oct 9, 2009, 12:16 PM
am I doing it right here?

NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
[polygonArray addObject:[[PolygonShape alloc] initWithNumberOfSides:4
minimumNumberOfSides:3
maximumNumberOfSides:7]];
// I do some stuff with it in the middle, then I'm done with it...
for (PolygonShape *shape in polygonArray)
{
[shape dealloc];
}
[polygonArray dealloc];


This code is definitely wrong. First, you should never call dealloc on any object, unless it's [super dealloc] in that object's own dealloc implementation. When you directly call dealloc, you are circumventing the retain/release mechanism, and grave disorder may ensue when you least expect it.

For your polygonArray, you should do two things:
1. simply release it when you're done with it.
2. call release on each PolygonShape after it's been added.

#1 is needed because you own the array.

#2 is necessary because you own each PolygonShape you added to the array. When each item was added, the array itself also called retain, so it's also claiming ownership. If you release the array, then it relinquishes ownership by calling release on each item, but you still have ownership of each shape added. So, if you release after the shape is added, you relinquish your ownership, and the only remaining owner is the array. Then when you release the array, it releases all its items, so everything is now ownerless, and its memory is deallocated.


As to your implementation of dealloc in PolygonShape, if all you're doing is [super dealloc], then you don't need to override the default. That is, if weren't for the NSLog call, you wouldn't even have to implement dealloc, because the inherited method (which is the one invoked by [super dealloc]) would already be correct and complete.

However, if PolygonShape has instance variables that are objects owned by that instance of PolygonShape, then dealloc must do two things: release all its owned objects, then call [super dealloc].


You should read and re-read the Cocoa Memory Management Guide until the rules are burned into your memory, and until the above explanation becomes blindingly obvious. That guide discusses the rules of ownership, and also discusses how and why to implement dealloc.

whooleytoo
Oct 9, 2009, 12:18 PM
A couple of comments:

- You alloc..init.. your PolygonShape and add it to the array all in one line. This isn't "wrong", but could present a potential problem, if garbage collection is off. alloc sets the reference count to 1, addObject: increments it again so you'd actually need to release the object twice or it'll leak.

I'd recommend splitting into separate lines:


NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
PolygonShape* myShape = [[PolygonShape alloc] initWithNumberOfSides:4
minimumNumberOfSides:3
maximumNumberOfSides:7];
[polygonArray addObject: myShape];
[myShape release]; // This is ok! addObject: has already retained the object.


- More importantly, don't call dealloc! You should call release instead. Dealloc deletes the object immediately, whereas release will call dealloc if nothing else is referring to it. If you call dealloc directly, you risk leaving dangling pointers which would likely cause a crash if de-referenced.

You can just call removeAllObjects to clear the array (and send each item a release), and release the array itself.

ArtOfWarfare
Oct 9, 2009, 04:24 PM
Thank you all,
I've changed it from dealloc to release, and dealloc seems to be firing at the right times (I have it set to send an NSLog message each time an object is deallocated.)

Additionally, the shapes are releasing their names when they're deallocated... I think that's the only thing they need to release since everything else associated with the class is just instance variables... unless I'm wrong about that... IDK, I got compile errors trying to tell instance variables to release themselves.

chown33
Oct 9, 2009, 06:37 PM
I think that's the only thing they need to release since everything else associated with the class is just instance variables... unless I'm wrong about that... IDK, I got compile errors trying to tell instance variables to release themselves.

You didn't post the source of PolygonShape.h, so no one knows the instance variables but you. Before posting the source and believing what we tell you, though, try working it out by yourself.

First consider whether the instance variables are object references or whether they're basic C types like int, double, or float?

Only objects can be sent messages, so if you're trying to send a message to an int or float, then you probably need to re-read the part about basic types vs. objects. Basic types may also be called simple types, primitive types, intrinsic types, fundamental types, etc. Objects may be called objects, object pointers, object references. etc. Terminology depends on which book or reference you're reading, and you haven't said what that is yet.