Objective C - How to instantiate a class saved as a variable - objective-c

I am new to Objective C and I'm getting confused by this concept.
The following method is supposed to create instances of the class that is passed as a parameter and edit the instance's variables.
- (void) createObject:(Class)objecttype atPoint: (CGPoint)position {
if ([objecttype isSubclassOfClass:[GameObject class]]){
id theobject = [[objecttype alloc] init];
theobject.x = position.x; //error
theobject.y = position.y; //error
}
}
I get an error message where indicated above : Property 'x' not found on object of type '__strong id'
It seems like it should be simple, but I don't understand whats wrong.

The dot notation will not call methods on objects of type id.
Change that allocation line to:
GameObject *theobject = (GameObject*)[[objecttype alloc] init];
Note that, because of the isSubclassOfClass: conditional, the above cast is precisely correct.

Related

I'm trying to get alloc init and nil to work within a method in ObjC

should hopefully be an easy question (teaching myself Objective C as I go).
I would like to send a method to an object (Cell) which returns a different object (Waste), and then deletes the reciever (Cell). Here is what I'm looking at so far:
#import "Cell.h"
#import "Waste.h"
#implementation Cell
#synthesize.......
other methods.....
// Send a method to an instance of class "Cell", causing a new object of
// class "Waste" to be made, then causing the Cell instance to "die"
- (Waste *) die {
// Create a new object, "newWaste", of class Waste
// ARC Semantic Issue: No known class method for selector 'alloc'
Waste *newWaste = [[Waste alloc] init];
// Set the energy of "newWa" to 10% what the Cell's energy is
newWaste.wasteEnergy = (0.1 * cellEnergy);
// Set the X coordinate of "r" to the Cell's X coordinate
newWaste.wasteXCoordinate = cellXCoordinate;
// Set the Y coordinate of "r" to the Cell's Y coordinate
newWaste.wasteYCoordinate = cellYCoordinate;
// Variable saying if the Waste is to be excreted set to "NO"
newWaste.wasteExcreted = NO;
// Return the new waste object
return newWaste;
// Have the Cell "die" ARC Semantic Issue:
// Cannot assign to 'self' outside of a method in the init family
self = nil;
}
I've put the two issues that come up behind the comments so you know where the problems are, but they're:
ARC Semantic Issue: No known class method for selector 'alloc'
and
ARC Semantic Issue: Cannot assign to 'self' outside of a method in the init family
Could someone tell me why this is happening and how to fix it? I had thought it was straightforward to do:
Class *newInstance = [[Class alloc] init];
and that this could be done inside a method so you can return the instance. And I had read that self = nil; was the normal way for releasing objects with ARC.

Printable type string from parameter

Using clang and objective-c I'm wondering if I can get a printable string describing the type of a potentially null parameter (i.e. compile time only type information.)
For example something like:
- (void)myFunc:(NSString *)aNilString {
NSLog(#"%s", __typeof__(aNilString));
}
Obviously this doesn't work because __typeof__ gets me the actual type, not a string. In C++ we have typeid which returns a std::type_info, but that name is mangled, e.g. "P12NSString*" and not "NSString*".
Ideally I'd like something that can be passed into functions like objc_getClass(). Is there a way to get what I want?
Edit: I'd like not to have to compile as C++, so this solution is out:
abi::__cxa_demangle(typeid(*aNilString).name(), 0, 0, 0));
To get a string that can be passed into functions like objc_getClass() just use the description of the class object.
In this example, I changed the type of your argument from NSString * to id since if you already knew that the argument was an NSString * then this question would be kind of moot. Note that I call the class method of the object being passed in and then the description method of the class in order to get an NSString * which describes the class and can be used with the methods that you reference.
Note also that if the object is nil then there is no class and calling description will simply return nil and the NSLog will print (null). You can't determine the type of pointer at runtime because it is simply a pointer to a class object (and that doesn't exist for nil).
- (void)myFunc:(id)aClassObject {
// Get the description of aClassObject's class:
NSString *classString = [[aClassObject class] description];
NSLog(#"%#", classString); // Prints __NSCFConstantString
// Bonus: Get a new object of the same class
if ([classString length] > 0) {
id theClass = objc_getClass([classString UTF8String]);
id aNewObjectOfTheSameType = [[theClass alloc] init];
NSLog(#"%#", [[aNewObjectOfTheSameType class] description]); // Prints __NSCFConstantString
}
}

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

Objective-C subclass and base class casting

I'm going to create a base class that implements very similar functions for all of the subclasses. This was answered in a different question. But what I need to know now is if/how I can cast various functions (in the base class) to return the subclass object. This is both for a given function but also a function call in it.
(I'm working with CoreData by the way)
As a function within the base class (this is from a class that is going to become my subclass)
+(Structure *)fetchStructureByID:(NSNumber *)structureID inContext:(NSManagedObjectContext *)managedObjectContext {...}
And as a function call within a given function:
Structure *newStructure = [Structure fetchStructureByID:[currentDictionary objectForKey:#"myId"]];
inContext:managedObjectContext];
Structure is one of my subclasses, so I need to rewrite both of these so that they are "generic" and can be applied to other subclasses (whoever is calling the function).
How do I do that?
Update: I just realized that in the second part there are actually two issues. You can't change [Structure fetch...] to [self fetch...] because it is a class method, not an instance method. How do I get around that too?
If I understand your question correctly I believe the key is the [self class] idiom.
As far as your update goes requesting a way to call a class method on the current class you can use [self class]. As in:
Structure *newStructure = [[self class] fetchStructureByID:[currentDictionary
objectForKey:#"myId"]];
inContext:managedObjectContext];
EDIT: I redid this to return id per #rpetrich's comment -- much cleaner and avoids the need for -isKindOfClass: as long as you're sure of the type of the instance you're calling -createConfiguredObject on.
As for the first part, you could just return an id (pointer to any object) and document that it will return an instance of the same class it's called upon. Then in the code you need to use [self class] anywhere you instantiate a new object in a method.
e.g. if you have a -createConfiguredObject method which returns an instance of the same class it's called on, it would be implemented as follows:
// Returns an instance of the same class as the instance it was called on.
// This is true even if the method was declared in a base class.
-(id) createConfiguredObject {
Structure *newObject = [[[self class] alloc] init];
// When this method is called on a subclass newObject is actually
// an instance of that subclass
// Configure newObject
return newObject;
}
You can then use this in code as follows:
StructureSubclass *subclass = [[[StructureSubclass alloc] init] autorelease];
subclass.name = #"subclass";
// No need to cast or use isKindOfClass: here because returned object is of type id
// and documented to return instance of the same type.
StructureSubclass *configuredSubclass = [[subclass createConfiguredObject] autorelease];
configuredSubclass.name = #"configuredSubclass";
For reference, what I was referring to with -isKindOfClass: and casting to the proper subclass is as follows:
Structure *structure;
// Do stuff
// I believe structure is now pointing to an object of type StructureSubclass
// and I want to call a method only present on StructureSubclass.
if ([structure isKindOfClass:[StrucutreSubclass class]]) {
// It is indeed of type StructureSubclass (or a subclass of same)
// so cast the pointer to StructureSubclass *
StructureSubclass *subclass = (StructureSubclass *)structure;
// the name property is only available on StructureSubclass.
subclass.name = #"myname";
} else {
NSLog(#"structure was not an instance of StructureSubclass when it was expected it would be.");
// Handle error
}

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