I'm trying to make a simple subclass of CCNode, but I can't create the object.
It gives me the error "* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[ContentPane<0x206898> init]: cannot init a class object.'"
Here is my subclass of CCNode:
#import "ContentPane.h"
#implementation ContentPane{
int content[8][4];
CCSprite *_rockPath1;
CCSprite *_rockPath2;
}
- (id)init {
self = [super init];
if (self) {
CCLOG(#"ContentPane created");
}
return self;
}
#end
Here is where I try to initiate it:
- (void)didLoadFromCCB {
// tell this scene to accept touches
self.userInteractionEnabled = TRUE;
_counter = 0;
ContentPane *pane = [ContentPane init];
}
Couple things,
In Obj-c when you want to initialize an Object, you need to allocate space for it.
That is done using the alloc keyword.
so your ContentPane *pane = [ContentPane init];
turns into ContentPane *pane = [[ContentPane alloc] init];
Also, whatever tutorial you are following, Stop... the way you have declared your variables we call them (iVars) is a very old fashioned way of doing things, they should really be properties. and Boolean values are represented by YES and NO not TRUE and FALSE
If you are here just like me wondering why you code is crashing.
[NSArray init];
should be :
[[NSArray alloc] init];
or
[NSArray array];
You crash could caused by any other class here NSArray here is for reference only.
Related
I have found that if I alloc a new object inside a Class method and return it to main() it seems to cause a memory leak when I no longer want the object.
For example, here is a simple Class that includes a Class method that returns an instance of itself:
#interface Stinker : NSObject
{
int a;
}
+(instancetype) makeAStink;
-(void) showThem;
-(void) setEm: (int) z;
#end
#implementation Stinker
-(void) showThem
{
NSLog(#"%d",a);
}
-(void) setEm: (int) z
{
a = z;
}
-(void) dealloc
{
NSLog(#"Arrrgggggh!");
}
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return temp;
}
#end
Now if I create an instance directly from main():
Stinker *aStink =[[self alloc] init];
and subsequently set aStink to nil:
aStink = nil;
the overridden dealloc method is called and the Argggggh! message is logged. That's fine and as expected.
But if I use the Class method I wrote to create an instance:
Stinker *aNewStink = [Stinker makeAStink];
the behaviour is different.
Now if I set aNewStink to nil, it will no longer point to the object but the object is not destroyed. dealloc is not called and the Arggggh message is not logged.
It seems like it still has an owner somewhere.
Of course when main() terminates the object is destroyed and dealloc is eventually called.
But this seems to suggest that unused and unloved objects are still hanging around on the heap until the program terminates.
Isn't this a memory leak?
Should I just avoid using Class methods to alloc new instances?
When using ARC, the following code
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return temp;
}
will be same with Non-ARC like this:
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return [temp autorelease];
}
Thanks to autorelease, aNewStink = nil will make aNewStink do release in next runloop.
So if you do this:
#autoreleasepool {
Stinker *aNewStink = [Stinker makeAStink];
aNewStink = nil;
}
Dealloc method is called immediately.
this is MRC (without ARC) code for your example
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return [temp autorelease];
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [Stinker makeAStink]; // obj is autoreleased object
id obj2 = [[Stinker alloc] init]; // obj2 is not autoreleased
[obj2 release]; // so you need to release it
[pool release]; // now obj is released and deallocated
so obj have an extra retain count which will be released (and deallocated) in next runloop whereas obj2 will be released immediately when release is called
this is not memory leak, it is usual behaviour and (normally) doesn't affect program performance in noticeable way
When I'm creating custom classes, I'd like to be able to skip the alloc init part of the code once I go to construct an instance of the class. Similar to how it's done with:
NSString * ex = [NSString stringWithFormat...];
Basically I already have the class set up with a custom initializer method to set up my basic variables. However, when I'm on the front end and actually making these critters I have to say:
[[Monster alloc] initWithAttack:50 andDefense:45];
and I'd rather be able to say
[Monster monsterWithAttack:50 andDefense:45];
I know it's a simple stupid thing to just get rid of the alloc part but it makes the code more readable so I'd prefer to do it that way. I originally tried just changing my method from
-(id)initWithAttack:(int) a andDefense:(int) d
to
-(id)monsterWithAttack:(int) a andDefense:(int) d
and then changing my self = [super init] to self = [[super alloc] init]; but that clearly doesn't work! Any ideas?
You have to make a class method
+(id)monsterWithAttack:(int) a andDefense:(int) d
in which you create, initialize, and return an instance (and don't forget your memory management):
+(id)monsterWithAttack:(int) a andDefense:(int) d {
// Drop the autorelease IF you're using ARC
return [[[Monster alloc] initWithAttack:a andDefense:d] autorelease];
}
What you want is a convenience constructor. It's a class method that returns a useable instance of a class and allocates memory for it at the same time.
-(id)initWithAttack:(int)a andDefense:(int)d;
+(id)monsterWithAttack:(int)a andDefense:(int)d;
+(id)monsterWithAttack:(int)a andDefense:(int)d {
//-autorelease under MRC
return [[[self class] alloc] initWithAttack:a andDefense:d];
}
-(id)initWithAttack:(int)a andDefense:(int)d {
self = [super init];
if (self){
//custom initialization
}
return self;
}
You should use a class factory method in the header of monster class.
+(id)monsterWithAttack:(int) attackValue andDefense:(int) defenseValue
in the implementetation of monster class
+(id)monsterWithAttack:(int) attackValue andDefense:(int) defenseValue {
return [[[[self class] alloc] initWithAttack:attackValue andDefense:defenseValue] autorelease];
}
The use of [self class] guarantees the correct dispatch during subclassing. If you are using ARC you can avoid the autorelease method
Class methods of this type use autorelease.
So for instance, you might say:
+ (id)
monsterWithAttack:(int) a
defense:(int) d
{
return [[Monster alloc] initWithAttack:a defense:d]
autorelease];
}
I have a marshmallow class which has (among other things) a CCSprite object as an instance variable.
here is the init method:
-(id) init
{
if((self = [super init]))
{
model = [[CCSprite spriteWithFile:#"marshmallow.png"] retain];
maxSpeed = 5; //160px per second (maxspeed * PTM_Ratio = px/second max)
gravity = 9.81; // in meters/sec^2
health = 3;
}
return self;
}
the variable is declared in another file as a global variable with the line:
Marshmallow *mainChar;
Later in the file, it is set (initiated/alloc'd) with this line:
mainChar = [[mainChar alloc] init];
while writing the previous line, xcode gave me a warning that Marshmallow might not respond to alloc. (I don't think that's related. just mentioning anything that seems wrong)
my problem is that the following line of code returns nil:
[mainChar getModel];
why does it return nil instead of the instance variable?
here is the getModel function:
-(CCSprite *)getModel
{
return model;
}
mainChar = [[mainChar alloc] init];
Shouldn't be
mainChar = [[Marshmallow alloc] init];
?
The message says an object from that class might not respond to it, not the class itself.
Your problem is in the initialization of your mainChar variable. The line you're looking for is this:
mainChar = [[mainChar alloc] init];
The warning you got is telling you that instances of type Marshmallow will not respond to the -alloc message. That is your problem: you want to call the +alloc class method instead, like so:
mainChar = [[Marshmallow alloc] init];
I think you want to do
mainChar = [[MarshMallow alloc] init];
instead of
mainChar = [[mainChar alloc] init];
The error message you got is very important.
is almost a week that i can't fix a brain painful problem:
I have a UIViewController subclass named StreamingViewControllerCommon that implement this property:
#property (nonatomic, assign, readonly) BOOL isMusicStopped;
and of course i #synthesize it in the .m file
then i have 2 subclasses of this class: Listen_UIViewController and LastNews_UIViewController that modify (not calling self.isMusicStopped but accessing directly to it) the var isMusicStopped.
In another Class i have a NSMutableDictionary that contains 2 instance (1 for each class) of these two classes but when i try to do this:
if (streamingViews){
for(StreamingViewControllerCommon* aView in streamingViews){
BOOL stopped = aView.isMusicStopped;
NSLog(#"%#",stopped);
if(stopped){
[aView closeStream];
[streamingViews removeObjectForKey:[aView class]];
[aView release];
aView = nil;
}
}
}
i obtain this error:
2011-02-08 15:55:09.760 ProjectName[6182:307] +[LastNews_UIViewController isMusicStopped]: unrecognized selector sent to class 0x2143c
2011-02-08 15:55:09.768 ProjectName[6182:307] CoreAnimation: ignoring exception: +[LastNews_UIViewController isMusicStopped]: unrecognized selector sent to class 0x2143c
But the weird thing is that in the StreamingViewControllerCommon implement also these methods:
-(void) destroyStreamer{
}
-(void) closeStream{
[self destroyStreamer];
}
and when i do:
NSMutableArray* keys = [[NSMutableArray alloc] initWithArray:[streamingViews allKeys]];
[keys removeObject:[thisView class]];
NSMutableArray *tmp = [[NSMutableArray alloc] initWithArray:[streamingViews objectsForKeys:keys notFoundMarker:#"404"]];
if (tmp){
for (StreamingViewControllerCommon* vc in tmp)
[vc closeStream];
[tmp release];
}
[streamingViews removeObjectsForKeys:keys];
i am not getting any error and the subclasse's overridden closeStream methods are right called.
What am i doing wrong?
Best Regards, Antonio
EDIT: As I wrote in the comment: Changing this:
if (streamingViews){
for(StreamingViewControllerCommon* aView in streamingViews){
BOOL stopped = aView.isMusicStopped;
NSLog(#"%#",stopped);
if(stopped){
[aView closeStream];
[streamingViews removeObjectForKey:[aView class]];
[aView release];
aView = nil;
}
}
}
in this:
if (streamingViews){
for (id aViewClass in streamingViews){
StreamingViewControllerCommon* aView = [[streamingViews objectForKey:aViewClass] retain];
//NSLog(#"%#",aView.isMusicStopped);
if(aView.isMusicStopped){
[aView closeStream];
[streamingViews removeObjectForKey:aViewClass];
[aView release];
aView = nil;
}
}
}
Did the trick :P The for each cycle of a NSDictionary returns its keys not the objects and, since i was using the Class of objects as key i was obtaining that weird exception
For whatever reason, it looks like streamingViews contains not an instance of LastNews_UIViewController, but the class itself. That's what the plus sign in +[LastNews_UIViewController isMusicStopped] signifies.
I have a class like this:
#interface MyCollection : NSObject {
NSMutableDictionary *data;
}
and in the implementation of it, I have a method to init it like this:
- (id) init {
if(self = [super init])
{
self.data = [[NSMutableDictionary alloc] init];
}
return self;
}
Now when I create a object of this class in my code like this:
MyCollection *c = [[MyCollection alloc] init];
... at which point the Leaks utility shows that I have a memory leak in the init function on the very line where I try to set up the instance variable. I am totally new to Objective C & Iphone and I can't just get what is going wrong here. I have read through the Memory Management Guide and all, but I think I'm missing something pretty serious here.
Any help would be greatly appreciated. Thanks for your time already.
you are using self.data =. So there is most likely a property. And it most likely is a property which either copies or retains your object if you use it.
By calling
self.data = [[NSMutableDictionary alloc] init];
The retain count of the NSMutableDictionary increases because of the alloc, and if the property of data has a retain or copy statement you get another increase in retain count.
you could write data = [[NSMutableDictionary alloc] init]; or self.data = [NSMutableDictionary dictionary]. This would increase the retain count only one time.
And don't forget to release the object in dealloc.
You have to release the object in your dealloc method. That's why it's showing up as a leak.
to add to what fluchtpunkt mentioned you could try this instead:
- (id) init {
if(self = [super init])
{
self.data = [NSMutableDictionary dictionaryWithCapacity:0];
}
return self;
}
and in the dealloc
-(void)dealloc
{
self.data = nil;
}
I see weird situations with the Leaks utility as sometimes it reports old leaks, sometimes it doesn't report new ones, and so on. Also, from what I could collect with all your answers and opinion elsewhere on the web, people are divided on whether one should set a pointer to nil or not.
As of now, I have solved the situation with the following approach.
- (id) init {
if(self = [super init])
{
data = [[[NSMutableDictionary alloc] initWithCapacity:0];
}
return self;
}
-(void)dealloc
{
[data release];
}
Thanks everyone for contributing.
Are you creating the instance of "MyCollection" in the interface section?
If it has method scope try to release it in the same method after you are done with it.