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];
}
Related
UILabel *testLbl = [[self alloc] init];
This is where the confusion started:
It’s usually better to use a variable other than self to refer to an instance inside a class
method:
+ (id)rectangleOfColor:(NSColor *)color {
id newInstance = [[Rectangle alloc] init]; // GOOD [newInstance setColor:color]; return [newInstance autorelease];
}
In fact, rather than sending the alloc message to the class in a class method, it’s often better to send alloc to self. This way, if the class is subclassed, and the rectangleOfColor: message is received by a subclass, the instance returned will be the same type as the subclass (for example, the array method of NSArray is inherited by NSMutableArray).
+ (id)rectangleOfColor:(NSColor *)color {
id newInstance = [[self alloc] init]; // EXCELLENT [newInstance setColor:color]; return [newInstance autorelease];
}
No, It'll cause a "UILable undeclared (first use in this function)" error.
No, it won't work. In your first line, you are sending the alloc message to an instance of a class. In the examples you copied out of Apple's documentation, they are sending alloc messages to the Class Rectangle. The difference is that your line is (apparently) inside an instance method, Apple's examples are inside class methods. There is a difference.
Like #Denis mentioned, you can do what you're trying to do by saying [[[self class] alloc] init], but in practice, don't do this. You'll almost never need the flexibility this offers and it will only muddy the intent of the new object.
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.
I want to initialize an instance of one of the subclasses of a superclass depending on the arguments to init:
[[Vehicle alloc] initWithItinerary: shortWay]; // returns a bicycle
[[Vehicle alloc] initWithItinerary: longWay]; // returns a car
I can't find examples of code like this. I wonder if this is not idiomatic in Objective C, or I simply am not looking in the right places.
You could do this via a custom init method, but it'd be kind of tedious (you'd have to invoke [super init], but then call [self release], etc...). It'd be much simpler to create a class method on Vehicle and use that as your factory method. For example:
+ (id) vehicleWithItinerary:(id)someItinerary {
if ([someItinerary isAShortWay]) {
return [[[Bicycle alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isAMediumWay]) {
return [[[RocketPack alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isALongWay]) {
return [[[Car alloc] initWithItinerary:someItinerary] autorelease];
}
return nil;
}
Look at [UIButton buttonWithType:] for an example of how Apple does this. Instead of init, they use a static method of the base class to allocate an instance of the appropriate derived class.
You can also pass around Class objects. Maybe the itinerary knows the Class or class name to allocate. You can do something like this:
[[[itinerary classToAllocate] alloc] initWithItinerary:itinerary];
or
[[NSClassFromString( [itinerary classNameToAllocate] ) alloc] initWithItinerary:itinerary];
You are allowed to release self and create a new object in init, although this is rarely used. Just watch out for recursion.
-(id) initWithItinerary:(Itinerary *)inItinerary {
[self release]; // super init never called - safe if you wrote super classes
self = [[[inItinerary classToAllocate] alloc] init];
self.itinerary = inItinerary;
return self;
}
This is called a class cluster. Several Cocoa classes work this way, including NSArray and NSString. The object returned from NSArray's init methods is never the same object that received the message. It's not that common outside of Cocoa, though, just because it's usually more complicated than people want to bother with. Basically, you figure out what actual class you want to use in your initializer, create an instance of that class, release yourself and return the other instance.
You might want to add an enum to the header file:
typedef enum {Bike, Car, JetPack
} vehicleType
That way your initWithItinerary: method can simply be:
if(VehicleType == Bike)
{
//do bike stuff
}
else if(VehicleType == Car)
{
//do car stuff
}
Why not have a method as part of the "way" that gives you a vehicle of the appropriate type for the way. e.g.
e.g.
// Somwhere before you use them. Car and Bicycle are subclasses of Vehicle
[shortWay setAppropriateVehicleType: [Bicycle class]];
[longWay setAppropriateVehicleType: [Car class]];
// when you need a vehicle
Vehicle* vehicle = [[[shortWay appropriateVehicleType] alloc] init];
Given the following property definition:
#property (nonatomic,retain) MyObject* foo;
does the following code cause a memory leak:
self.foo = [[MyObject alloc] init];
?
It looks like the alloc call increments the retain count on the object to 1, then the retain inside the property setter increases it to 1. But since the initial count is never decremented to 0, the object will stick around even when self is released. Is that analysis correct?
If so, it looks like I have two alternatives:
self.foo = [[[MyObject alloc] init] autorelease];
which is not recommended on the iPhone for performance reasons, or:
MyObject* x = [[MyObject alloc] init];
self.foo = x
[x release];
which is a bit cumbersome. Are there other alternatives?
Are there any alternatives?
No.
You are not going to be able write much of an iPhone application without using autorelease and the Cocoa Touch library uses them in many places. Understand what it's doing (adding the pointer to a list for removal on the next frame) and avoid using it in tight loops.
You can use class method on MyObject that does alloc/init/autorelease for you to clean it up.
+ (MyObject *)object {
return [[[MyObject alloc] init] autorelease];
}
self.foo = [MyObject object];
The easiest way to manage a retained property on the iPhone is the following (autorelease is not as bad as you think, at least for most uses):
-(id)init {
if (self = [super init]) {
self.someObject = [[[Object alloc] init] autorelease];
}
return self;
}
-(void)dealloc {
[someObject release];
[super dealloc];
}
The autorelease releases the reference to the floating instance which is assigned to self.object which retains its own reference, leaving you with the one reference you need (someObject). Then when the class is destroyed the only remaining reference is released, destroying the object.
As described in another answer, you can also create one or more "constructor" messages to create and autorelease the objects with optional parameters.
+(Object)object;
+(Object)objectWithCount:(int)count;
+(Object)objectFromFile:(NSString *)path;
One could define these as:
// No need to release o if fails because its already autoreleased
+(Object)objectFromFile:(NSString *)path {
Object *o = [[[Object alloc] init] autorelease];
if (![o loadFromFile:path]) {
return nil;
}
return o;
}
You are right, self.foo = [[MyObject alloc] init]; is leaking memory. Both alternatives are correct and can be used. Regarding the autorelease in such a statement: keep in mind that the object will released by the autorelease pool as soon as the current run loop ends, but it will most probably be retained a lot longer by self, so there is no issue with memory usage spikes here.
I have seen a lot of talk about dynamic typing in objective-c. But i haven't seen any examples of what i think it is supposed to be.
lets say I have a generic function that is supposed to juggle two objects (one gets allocated and the other gets freed) and the calling object attaches it self to the newly alloced object. Both are inherited from class0
Please feel free to interpret this however you want if you think it will explain something!!
If the class is picked at runtime, how do i deal with the arguments list (? is a placeholder for now)
How do i alloc a object who's class is not defined until runtime?
-(void) juggle:(?*)objclass1:(?*)objclass2{
? temp = [? alloc] init];
objclass1 = temp;
[temp release];
[objclass2.view removefromsuperview];
[self.handle insertsubview:objclass1.view];
}
I have no idea what the code you have there is trying to do, it is not syntactically valid, and manipulating views has nothing to do with your questions. Anyway, if you really don't know the type you generally use "id" which is type cast to a "void *" for codegen. It has the special property that it is assumed to receive any message, so it does not trigger compiler warnings for unknown messages.
In order to instantiate a class you just need to be holding the "Class" object for it. In Objective C all instances of a class refer to a Class object (the isa pointer in the legacy runtime), which also responds to methods. So in other words, in the following code:
NSArray *myObject = [[NSArray alloc] init];
NSArray is actually an object. So this will generate equivalent code results:
Class myClass = [NSArray class];
NSArray *myObject = [[myClass alloc] init];
or even
Class myClass = NSClassFromString(#"NSArray");
NSArray *myObject = [[myClass alloc] init];
Which uses the function NSClassFromString which walks into the runtime and finds a class with the name you pass in.
All objects return their class if use the class getter, so to instantiate an object that is the same class as an existing object like this:
- (void) leakObjectWithSameClassAs:(id)object {
[[[object class] alloc] init];
}
This is what i have now
- (void)flipfromv1tov2:(UIViewController*)v1:(NSString*)nib1:(UIViewController*)v2{
if(v1 == nil)
{
UIViewController *newview = [[[v1 class] alloc] initWithNibName:nib1 bundle:nil];
v1 = newview;
[newview release];
}
[v2.view removeFromSuperview];
[self.view insertSubview:v1.view atIndex:0];
}
I cannot verify it yet because I have a linking problem...I added this func to my root controller but for some reason I get a warning that the function is implicitly declared. And the build fails because the function call never get linked to anything