Custom Class Won't Respond to Method?

Discussion in 'Mac Programming' started by ArtOfWarfare, Oct 8, 2009.

  1. macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #1
    I'm getting the following warning when I try to compile:
    Here's the code that seems relevant...

    from PolygonShape.h:
    Code:
    @interface PolygonShape : NSObject
    Code:
    -(id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max;
    from PolygonShape.m:
    Code:
    -(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
    Code:
    #import "PolygonShape.h"
    Code:
    	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:

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

    Joined:
    Apr 22, 2005
    #2
    You want

    Code:
    PolygonShape *test = [[PolygonShape alloc] initWithNumberOfSides....]
    
     
  3. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #3
    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.
     
  4. thread starter macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #4
    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 :)
     
  5. macrumors 603

    whooleytoo

    Joined:
    Aug 2, 2002
    Location:
    Cork, Ireland.
    #5
    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.
     
  6. thread starter macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #6
    Thanks for the explanation whooleytoo :)

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

    Code:
    	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...

    Code:
    -(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...
     
  7. macrumors 6502a

    Joined:
    Mar 19, 2008
    Location:
    North Shore, MA
    #7
    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:
    Code:
    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];
     
  8. macrumors 603

    Joined:
    Aug 9, 2009
    #8
    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.
     
  9. macrumors 603

    whooleytoo

    Joined:
    Aug 2, 2002
    Location:
    Cork, Ireland.
    #9
    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:

    Code:
    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.
     
  10. thread starter macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #10
    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.
     
  11. macrumors 603

    Joined:
    Aug 9, 2009
    #11
    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.
     

Share This Page