Constructor method not being called properly - objective-c

I made a class called Player.h. The constructor is such:
-(Player*) construct
{
health = 100;
width = 50;
height = 50;
return self;
}
And it is in my header file as -(Player*) construct;
To verify I call my getWidth method and the other getters and all return as 0
Is this the right way to make a constructor? I'm trying to incorporate better OOP practices and this is the first time in Obj-C I'm really using objects

Usually, you create objects in Objective-C by way of calling alloc/init, in your case [[Player alloc] init]. Just overwrite the init method in your class - it already has the right skeleton. Do not remove the self = [[super alloc] init] line.

If you want your object to be constructed, you need to allocate in initialize it. While you can call your method -construct, it's traditionally called -init or -initWith<Blah> where <Blah> is some information like a rectangle or other useful value. You'd create an object like this:
Player* newPlayer = [[Player alloc] init];

Related

Objective-C : Something that I don't understand with the init method

I was encountering an issue with my code and somebody explained me that it was because I wasn't creating an instance for my property, he told be that I should do that in my init method. What I don't understand is this : I already created my object by doing like this :
TADIgnoringConstraint *ignorer = [TADIgnoringConstraint new];
I know that I could also use [[TADIgnoringConstraint alloc] init] but I see that the init method is already existing, so should I create a new custom initialization method with another name ? In that case, how do I create a init function ? What does this function returns ?
[TADIgnoringConstraint new]
Is exactly the same thing with
[[TADIgnoringConstraint alloc] init]
Which allocates the object in memory and initializes it. When you call init on a allocated object, it runs the init method of the object if declared, if not it runs the init of the superclass which returns the object it self.
Now what #Wain told you was to instantiate your array in your init method. Which is the typical practice in most cases.
You can do something like this:
- (id)init
{
self = [super init];
if (self) {
// Set your properties properly here
// before you actually return the object so they can be ready to use
_someMutableArray = [NSMutableArray array];
}
return self;
}
new is the same as alloc followed by init. In NSObject, new is defined as
+ (id)new
{
return [[self alloc] init];
}
Looking at the other thread, the assumed problem is that you may not be initializing the tockensArray property. If that's true, then you need to do that in the init method of TADIgnoringConstraint, which you say already exists.
What you need in that case would be something like:
self.tockensArray = [NSMutableArray array];
If that's not the actual problem, show how you declare tockensArray and what is currently in your init.
TADIgnoringConstraint *ignorer = [TADIgnoringConstraint new];
That line of code creates a new object of class TADIgnoringConstraint and stores a pointer to that object into a local variable. Because it is a local variable and apparently not stored anywhere else, it will be deleted by ARC as soon as you leave the function or method where this happens.
I strongly recommend using [[TADIgnoringConstraint alloc] init], mostly for the reason that everybody does. Following conventions means that your code is easier to read, and people including yourself don't need to check whether your problems are caused by not following conventions. (You may say: "But new is the same as alloc + init" to which I would reply "your code isn't working. Would you bet $1000 that it is indeed the same?").

Will this work?

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.

Typecasting a TableViewController Class

I'm having a problem with either typecasting or object scope. I'm getting an uncaught exception:
// Create the object here so that it's scope is outside the `if` statement, right?
searchTableViewController *newViewController;
if (rowSelected) {
// Typecast the object to a searchTableViewController
(searchTableViewController *)newViewController ;
// Initialize and Allocate
newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
// Typecast the global object to a personViewController
(personViewController *)newViewController;
// Initialize and Allocate
newViewController = [[personViewController alloc] init];
}
// Act on the object: create a data model object and store it as a property, etc.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
I have 2 similar ViewControllers searchTableViewController and personViewController. So I want to be able to instantiate either one or the other, using the same name, that way the rest of my code can act on the viewController using common properties and such.
This is causing a Terminating due to uncaught exception and regardless it seemed like the wrong way to do it anyway. I need help either in the typecasting department or I need help understanding how to declare the scope of these objects properly so I can work with them in and outside of the if statement.
Update
The easiest thing would be for me to write my code as follows. How can I declare, allocate and instantiate an object within an if statement and then have it accessible outside?:
if (rowSelected) {
searchTableViewController *newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
personViewController *newViewController = [[personViewController alloc] init];
}
// This will probably give an error since newViewController is not in the proper scope.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
You want to declare your newViewController as the common subclass of the two objects you're possibly allocating. Probably UIViewController.
UIViewController *newViewController;
if (rowSelected) {
// Initialize and Allocate
newViewController = [[SearchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
// Initialize and Allocate
newViewController = [[PersonViewController alloc] init];
}
And the cast operation isn't doing anything when you use it in-place like that.
Edit - if both of those classes have common properties, like dataModel, then you can avoid warnings by creating a common base class that derives from UIViewController and which contains those properties. You'd then change the declaration of your view controller variable in the first line above to match the intermediate base class.
Later Edit — if you don't want to create an intermediate base class, you can do the following (newViewController still must be declared as a UIViewController):
if([newViewController respondsToSelector:#selector(setMyDataModel:)]) {
DataModel *dataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
[newViewController performSelector:#selector(setMyDataModel:) withObject:dataModel];
}
RE: Your Edit
id newViewController;
if (rowSelected) {
newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
newViewController = [[personViewController alloc] init];
}
// This will probably give an error since newViewController is not in the proper scope.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];

init] as a factory method

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];

How to alloc a dynamic typed object

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