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];
Related
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 want to create a object but the type depends on the result of the if-condition:
if ([type isEqualToString:#"day"]) {
GraphDayView *graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
GraphMonthView *graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
The problem is that graphv is out of scope, so I am not able to use it after the if-statement.
So I tried to declare it as an id:
id graphv;
if ([type isEqualToString:#"day"]) {
graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
But the problem now is, that the compiler doesn't know what kind of object grapv is. So:
graphv.backgroundColor = [UIColor whiteColor];
gives an error. Anyone an idea how to solve this one?
If they share the same superclass, then use that instead of id. Otherwise, create two variables and set them to nil:
GraphDayView *gdv = nil;
GraphMonthView *gmv = nil;
Then test after the if statement to see which one was initialized.
Make GraphDayView and GraphMonthView both subclasses of GraphCalendarView. Then make your backgroundColor a property of GraphCalendarView.
(Or, if your two classes are already subclasses of a UI class that implements backgroundColor then you're home free.)
Declare your variable as GraphCalendarView graphv;, and then proceed as before. If you need to use a property/method that's unique to of one or the other of your two derived classes then cast to that class first.
Try this,
id graphv;
if ([type isEqualoString:#"day"]) {
(GraphDayView *)graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
(GraphMonthView*)graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
A lot of suggestions for a common superclass have been made which are most liekly suitable.
In the event they are not then you could use typcasts. However, typecasts may cause exceptions like unknown selecor or bad_exec. If you want to savely use typcasts then you should always check either isKindOfClass or respondsToSelector.
Sample:
id someClassObject; //or any other common superclass* instead of id
If ([something isTrue])
someClassObject = [[AClass alloc] init]; //assuming ARC. If not then you may want to retain/autorelease here too.
else
someClassObject = [[AClass alloc] init];
... //some code
if ([someClassObject isKindOfClass:[AClass class]])
[(AClass *) someClassObject methodOfAClass];
//
if ([someClassObject isKindOfClass:[AClass class]]) {
AClass *aClassTemp = (AClass *) someClassObject;
[aClassTemp methodOfAClass];
aClassTemp.propertyOfAClass = someValue;
}
if ([someClassObject respondsToSelector:#selector(methodOfBClass:))
[someClassObject perforformSelector:#selector(methodOfBClass:) withObject:[UIColor clearColor]];
Note the number of : following the selector name correlates to the number of parameters of the method. You may not find a suitable variance of performSelector for each possible method call. Especially as you can pass or return references to objects only.
Again, both of them I would only suggest if the common subclass is not suitable in your case.
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];
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 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