Objective C - determine class type at runtime - objective-c

In the interface I have this:
Animal* myPet;
At runtime I may want myPet to be a cat or a dog, which are subclasses of Animal:
id newPet;
if(someCondition) {
newPet = [[Cat alloc] initWithNibName:#"Cat" bundle:nil];
} else {
newPet = [[Dog alloc] initWithNibName:#"Dog" bundle:nil];
}
self.myPet = newPet;
Obviously this is incorrect, but I hope it's enough to show what I'm trying to do. What is the best practice for doing this?

isKindOfClass is your friend:
[newPet isKindOfClass:Dog.class] == NO

Strongly type newPet as Animal * instead of id. id can hold a reference to an instance of any class, but properties cannot be used with it (the dot syntax requires a strongly typed lvalue.) Since both Cat and Dog inherit from Animal, this will be perfectly correct and valid.
If you're using two classes that don't share a common ancestor (past NSObject), then you should take a step back and rethink your design--why would instances of those two classes need to occupy the same variable?

NSString *className = #"Cat";
Animal *myPet = [[NSClassFromString(className) alloc] init];
It's unclear what you are after, but if you want to create an instance of a class named by a string, this should do it.

For anyone arriving from Google based on the title: "Determine class type at runtime", here are some useful things to know:
You can call the class method on an NSObject* at run time to get a reference to its class.
[myObject class];
Take a look at these methods too:
isKindOfClass: - check if an object belongs to a class anywhere in its hierarchy.
isMemberOfClass: - check if an object belongs to a specific class.

Related

Newbie. Custom class

CustomClass *variableName = [[CustomClass alloc] init];
variableName.propertyName = #"Some text";
Could anyone explain this code step by step in human language?
Why if I want to send data to a property in CustomClass I am accessing it throught varibaleName.propertyName , but not through CustomClass.propertyName.
I can not understand it.
If I want to send some data to a varibale in CustomClass wouldn't it be logically to show the path to that property = CustomClass.propertyName = #"Some text"; ?
*variableName - what is it for?
I am confused.
There seems to be some confusion on the difference between an instance and a class. It's generally better to try and link complex ideas like this to real-world examples.
A Class could, for example, be Cars. Thus, you have a Car class. It will include information shared by all Cars. For example, instead of having propertyName it could have a "model" name. To access data about any given car you must first create it. That is what you do in the first line: CustomClass *variableName = [[CustomClass alloc] init];
In our example, we would write Car *myCar = [[Car alloc] init]; which creates a new Car object that we call myCar. Then, you can say myCar.model = "Civic". We do not want to make all cars be a "Civic", but specifically the myCar that we created.
Do not be confused between a Class, which describes a general kind of object, and an Instance, which is the object itself.
Hopefully you now understand the last part of your question:
*variableName - what is it for?
This means that you have a reference to an instance of your CustomClass which is called variableName. In our example, this is myCar which you can then manipulate or change.
You access variableName.propertyName instead of CustomClass.propertyName because variableName is an instance of the class, while CustomClass is the class itself, not the object that you use.
For instance, you have 2 CustomClass objects, lets say variable1 and variable2. variable1.propertyName will be different from variable2.propertyName because they are different instances of the class, not the class itself.

Objective-C: a Pointer That Points to Difference Classes at Different Time

I'm not really experienced with Objective-C. Here is a problem I encountered.
When I want to define a pointer for a particular instance of a class, I can
NSString* foo;
But is it possible to define pointers for instances of classes like this?
x* hotdog; //"x" is the type of pointer hotdog is
hotdog = NSString; //now points to NSString
hotdog* foo; //an instance of NSString is created
hotdog = UIView; //now points to UIView
hotdog* foo; //an instance of UIView is created
How to define the class-pointer hotdog? (what should I replace x with?)
what should I replace x with?
You should replace x with the name of the most specific common ancestor of the classes that you are planning to use with this pointer. In your example, that would be NSObject, because both NSString and UIView inherit it, and there are no other common ancestors. In the worst case, the common ancestor is id.
In general, tricks like that should be avoided in most situations, because reusing a pointer for something really different is bad for readability.
If you want a pointer to an object of a type that's not yet known at compile-time (similar to dynamic in C#), use id:
id hotdog;
hotdog = [[NSString alloc] init];
hotdog = [[NSArray alloc] init];
Only do this when you really need it. If you use it everywhere, your code can easily become a mess since you'll lose track of the type of the variable.
At first I misunderstood your question. I'll leave my old answer here just in case future visitors need it.
The type of pointers to classes is Class and to get an object of that type use +[NSObject class].
Class hotdog = [NSString class]; // now points to NSString
NSString *myString = [[hotdog alloc] init]; // create instance of NSString
hotdog = [NSArray class]; // now points to NSArray
NSArray *myArray = [[hotdog alloc] init]; // create instance of NSArray
You can use either NSObject* or id as the pointer type. NSObject* will accept any subclass of NSObject, while id will accept other Objective-C objects as well.
Note that, to avoid compiler warning messages, you must cast the pointer type back to the (presumably known) actual type before applying any sort of dereferencing operation (other than methods of NSObject).
You can, to be sure you have the expected type of object, use isKindOfClass to check the type:
if ([genericPointer isKindOfClass:[NSArray class]]) {
NSString* arrayElement = [(NSArray)genericPointer objectAtIndex:x];
}
But is it possible to define pointers for instances of classes like this?
I suppose you're asking for the equivalent of C++ templates.
You can't do it and you don't need it, just use the id type:
id foo= #"some text";
If you are working on an instance class the pointer to the class itself is simply self.
If you are working on a class pointer you could just use the id type since it is a generic type. Make sure then the object you are working on is of the expected type by using the isKindOfClass method if you want to invoke some methods of this class.

Casting down with Objective C

Say I have a subclass of NSManagedObject called MBManagedSquare and MBManagedCircle. An MBManagedSquare and MBManagedCircle define a method prepareFromDictionary:(NSDictionary*)dic, and both of their implementations are different.
Say I have this code:
NSString *type = // could be #"MBManagedSquare" or #"MBManagedCircle"
NSEntityDescription *desc = [NSEntityDescription entityForName:type inManagedObjectContext:_context];
NSManagedObject *object = [[NSManagedObject alloc] initWithEntity:desc insertIntoManagedObjectContext:_context];
So the type of entity it will be with Core Data is determined dynamically via a type string. So all I know is that it is an NSManagedObject.
What I want to do is call the prepareFromDictionary: method for the right class.
So if the type is "MBManagedSquare", I want to cast the object to an MBManagedSquare, and then call
[castedObject prepareFromDictionary:dic];
What I tried doing is:
Class class = NSClassFromString(type);
class *castedObject = (class*)object;
but I get an expected expression error. I'm not sure if this is even possible. How would I do this?
You don't need to worry about calling the right class if the selectors and their parameters match -- ObjC has plenty of dynamic dispatch powers.
As far as an implementation, it's pretty common to either:
create a common base with the interface you want
or create a protocol which both classes adopt:
MONProtocol.h
#protocol MONManagedShapeProtocol < NSObject >
- (void)prepareFromDictionary:(NSDictionary *)pDictionary;
#end
then (since you know it is one of the two types, MBManagedSquare or MBManagedCircle) either derive from the base or adopt the protocol and declare your variable like:
// if subclass
MBManagedShape * castedObject = (MBManagedShape*)object;
or
// if protocol
NSManagedObject<MONManagedShapeProtocol>* castedObject =
(NSManagedObject <MONManagedShapeProtocol>*)object;
no need for a cast there. the object can be either or and the function is only there once.
checking if it is there is good: respondsToSelecctor:#selector(prepareFromDictionary:)

How do I properly override a class method in an Objective-C in a subclass?

In the second chapter of his iOS Programming book, Joe Conway describes using 'self' in class methods in the event of subclassing. I understand this concept and have a question about the issue of subclassing.
Background: We created a Possession class whose class method +randomPossession looks like this:
+(id)randomPossession
{
NSArray *randomAdjectiveList = [NSArray arrayWithObjects:#"Fluffy", #"Rusty", #"Shiny", nil];
NSArray *randomNounList = [NSArray arrayWithObjects:#"Bear", #"Spork", #"Mac", nil];
unsigned long adjectiveIndex = rand() % [randomAdjectiveList count];
unsigned long nounIndex = rand() % [randomNounList count];
NSString *randomName = [NSString stringWithFormat:#"%# %#", [randomAdjectiveList objectAtIndex:adjectiveIndex], [randomNounList objectAtIndex:nounIndex]];
int randomValue = rand() % 100;
NSString *randomSerialNumber = [NSString stringWithFormat:#"%c%c%c%c%c",
'0' + rand() % 10,
'A' + rand() % 10,
'0' + rand() % 10,
'A' + rand() % 10,
'0' + rand() % 10];
Possession *newPossession = [[self alloc] initWithPossessionName:randomName valueInDollars:randomValue serialNumber:randomSerialNumber];
return [newPossession autorelease];
}
I am aware that the return value should really be of type id such that id newPossession = ...
I subclassed Possession and created a class called BallGlove that included a new iVar, brandName, an NSString *
I overrode the +randomPossession in BallGlove as follows:
+(id)randomPossession
{
BallGlove *myGlove = [super randomPossession];
NSArray *brandNames = [NSArray arrayWithObjects:#"Rawlings", #"Mizuno", #"Wilson", nil];
unsigned long randomNameIndex = rand() % [brandNames count];
[myGlove setBrandName:[brandNames objectAtIndex:randomNameIndex]];
NSLog(#"myGlove is of type class: %#", [self class]);
return myGlove;
}
My question is this: Is the manner in which I overrode this class method appropriate and acceptable by the community (i.e. parallel the -init format by capturing the super implementation in a variable, manipulate the variable accordingly and then return it? My output shows that the object returned is an instance of BallGlove however, I was interested in the acceptable implementation. Thanks in advance.
Yep, that's a perfectly sensible way to do it. There's nothing particularly different between class methods and normal methods — just that one is performed by a class and the other is performed by an instance.
The moment you override a class method you can decide to implement it with the help of the super implementation or without it. It is totally up to you. Overiding init is a completely differeent story, not only because it is an instance method but because it has a convention/contract associated with it. Keep in mind that for instance methods the Liskov Subtitution principle should not be violated.
Your overrided of the class method is perfectly fine, although I would consider overiding class methods a design smell. Although it's very well possible in Objective-C, it's not in other languages and that for a very good reason. Polymorphism as a concept is better bound to instances that can be used as substitutes for each other, whereas using class methods breaks the concept (i.e. no real subtitution). It's clever, but not necessesarily intuitive and flexible.
Yes, it is acceptable to do your initialization like that. In fact that is how it is done in most cases. I mean, that is the reason for inheriting from a super class in the first place. You want stuff in addition to what is present in the super class. So, you insert code to whatever is specific to the inherited class and it should be done that way.
I think how you want the BallGlove object initialized is also a factor in how you define your inherited method. The question arises on calling the Possession init or calling the BallGlove init (Not that creating an instance of a class is the only place to use a class method). So it comes down to the logic of creating your objects i.e. how you well you are describing BallGlove object-are you making sure that your class method describes it in ways that fits the BallGlove object criteria and does not become generic Possession object. My answer is that if you can implement it right, using a parallel line of class methods is acceptable.
Also, it doesn't matter if you are returning of type Possession in your super class because, id can point to an object of any class type
It's technically ok.
I'd propose an alternative, though. If you already have a designated public initializer on your base class (which you might want to create and call from that factory class method anyway), and then use that very initializer (or even a new one from your subclass) in your subclasses' class method.
It's not much more code, but in my opinion easier to follow and future-proof. The initializer might come handy as well at one point, but of course it's not a solution for every application.

How can I pass a class name as an argument to an object factory in cocoa?

I am working on an object factory to keep track of a small collection of objects. The objects can be of different types, but they will all respond to createInstance and reset. The objects can not be derived from a common base class because some of them will have to derive from built-in cocoa classes like NSView and NSWindowController.
I would like to be able to create instances of any suitable object by simply passing the desired classname to my factory as follows:
myClass * variable = [factory makeObjectOfClass:myClass];
The makeObjectOfClass: method would look something like this:
- (id)makeObjectOfClass:(CLASSNAME)className
{
assert([className instancesRespondToSelector:#selector(reset)]);
id newInstance = [className createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
Is there a way to pass a class name to a method, as I have done with the (CLASSNAME)className argument to makeObjectOfClass: above?
For the sake of completeness, here is why I want to manage all of the objects. I want to be able to reset the complete set of objects in one shot, by calling [factory reset];.
- (void)reset
{
[managedObjects makeObjectsPerformSelector:#selector(reset)];
}
You can convert a string to a class using the function: NSClassFromString
Class classFromString = NSClassFromString(#"MyClass");
In your case though, you'd be better off using the Class objects directly.
MyClass * variable = [factory makeObjectOfClass:[MyClass class]];
- (id)makeObjectOfClass:(Class)aClass
{
assert([aClass instancesRespondToSelector:#selector(reset)]);
id newInstance = [aClass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
I have right a better tutorial on that , please checkout
https://appengineer.in/2014/03/13/send-class-name-as-a-argument-in-ios/
It's pretty easy to dynamically specify a class, in fact you can just reference it by it's name:
id string = [[NSClassFromString(#"NSString") alloc] initWithString:#"Hello!"];
NSLog( #"%#", string );
One other tip, I would avoid using the nomenclature 'managed object' since most other Cocoa programmers will read that as NSManagedObject, from Core Data. You may also find it easier to use a global NSNotification (that all your reset-able objects subscribe to) instead of managing a collection of different types of objects, but you're more informed to make that decision than I am.
The bit of the answer missing from the other answers is that you could define a #protocol containing your +createInstance and +reset methods.
It sounds like you want something like:
- (id)makeObjectOfClassNamed:(NSString *)className
{
Class klass = NSClassFromString(className);
assert([klass instancesRespondToSelector:#selector(reset)]);
id newInstance = [klass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
This would assume a class method named +createInstance. Or you could just use [[klass alloc] init].
To call it:
MyClass *variable = [factory makeObjectOfClassNamed:#"MyClass"];
Depending on what you're trying to do, it might be better to pass around class objects than strings, e.g.:
MyClass *variable = [factory makeObjectOfClass:[MyClass class]];