I'm writing a project and I would like to get the superclass' instance of a certain object at runtime, for development purposes.
For example, if I have this method in a class called SomeClass:
+(id)generateSuperclassInstanceOfObject:(id)object {
if (object) {
NSLog(#"The class of the object is %#", [object class])
Class klass = [object superclass];
if (!klass)
NSLog(#"Klass not available");
else {
//NSLog(#"The superclass of the object is %#", NSStringFromClass(klass));
id superObject = (klass *)object;
//NSLog(#"Correctly generated the superclass instance!");
return superObject;
}
}
return NULL;
}
And, if I have:
#interface A : NSObject
...
#end
#interface B : A
...
#end
#interface C : B
...
#end
I would like to do something similar, if it has at least 2 superclasses (as in my case):
C *object = [[C alloc] init];
id superInstance = [SomeClass generateSuperclassInstanceOfObject:object];
NSLog(#"Generated correct superInstance of class: %#", [superInstance class]);
id superSuperInstance = [SomeClass generateSuperclassInstanceOfObject:superInstance];
NSLog(#"Generated correct superInstance of class: %#", [superSuperInstance class]);
Where NSLog prints:
The class of the object is C
Generated correct superInstance of class: B
The class of the object is B
Generated correct superInstance of class: A
In my code, it looks
id superObject = (klass *)object;
is not correct because klass is not a type.. I need something that allows me to cast an object in this particular situation, using a "cast generated at runtime", because I don't know from the beginning the hierarchy of the object.
The most important part is that: I know "super" exists, but I need to create a new object deleting any reference to its subclass. This must be valid for any object having at least 2 superclasses (so in my case, I shouldn't be able to use the generateSuperclassInstanceOfObject method with argument superSuperInstance, because it has NSObject as superclass).
Any help please?
In general you cannot do what you are trying to do. An object is an instance of a particular class and that never changes.
When an reference to an object of one class is cast to be a reference to an object of a different class the reference object is not changed in anyway, what changes is how the compiler treats the reference.
From your question it is not clear for what purpose you wish to do this. If your intent is to call methods that are inaccessible because they are overridden then the super mechanism exists for that purpose.
If this answer doesn't answer your question you can ask a new question, or edit the current one, detailing the purpose of what you are trying to do, what you have tried, etc. and someone will probably help you along. (I.e. don't try to expand this question in comments.)
HTH
Casting one object as another doesn't change the underlying object at all. So this doesn't change anything.
id superObject = (klass *)object;
If you want an instance of the super object then you need to alloc/init it like any other object.
id superObject = [[klass alloc] init];
Not sure why you want to do this though... but that is your answer.
Related
I have a class like so:
#interface Foo : NSObject <FooDataProvider>
...
#end
and somewhere along the line in another class I have a method with the interface:
-(void) doStuff:(id<FooDataProvider>)fooProvider{
...
}
And yet somwhere else I have
-(void) passMeClasses:(Class)theClass
{
<do stuff based on the class>
}
Usually I pass things to this method simply like
Foo* f = [[Foo alloc] init];
...
[bar passMeClasses:[f class]];
But Im not sure how to pass things I only have the id.. like so
id<FooDataProvider> f = [[Foo alloc] init];
....
[bar passMeClasses:[f ?????]];
or alternatively how to do it from the doStuff method
-(void) doStuff:(id<FooDataProvider>)fooProvider{
Class c = [fooProvider howDoIGetMyClass];
bar passMeClasses:c];
}
Can someone help me in determining the class from the id?
Sorry for the long winded explanation, but hopefully its clear!
[f class]
class is a method. It's called on the object. The object will return its class (or it should; it can technically return something else and sometimes does). The type of the variable is completely irrelevant at runtime. At runtime all object pointers are id. That's why method signature type encodings only designate "an object goes here." (See #.) They can't express the specific type of the object.
I have a bunch of simple NSManagedObjects I create in a unit test. They just have a single name attribute of type NSString *. I always give my NSManagedObject the same entityName and Class name.
I want to avoid having to write the following code 30 times to set up a unit test:
#interface FooTest : GHTestCase {
Foo *foo;
}
#end
#implementation FooTest
- (void) setUp {
[super setUp];
foo = [NSEntityDescription insertNewObjectForEntityForName:#"Foo"
inManagedObjectContext:managedObjectContext];
foo.name = #"foo";
}
#end
Since foo is an ivar, I would think I should be able to write a macro to grab the type of foo (Foo), and use to create my Foo:
#define InsertManagedObjectByVariable(variable) \
do { \
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])]; \
variable.name = (NSString *) CFSTR(#variable);
} while(0)
However, this causes the following warning in clang:
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])];
^
Expected expression
I also thought I could try to determine the type using the objective-c runtime IVar from Ivar class_getInstanceVariable(Class cls, const char* name), but the only IVar type information available from the type encoding from ivar_getTypeEncoding is id, which isn't enough.
Can someone think of a way to obtain the type information of an IVar either at compile time or runtime?
I haven't tried obtaining class information from an ivar, but I know that #property declarations do encode information about the class. For instance, this property declaration:
#property (copy) NSString *normalString;
results in this attribute string (retrieved using property_getAttributes()) at runtime:
T#"NSString",C,VnormalString
I've written some open source parsing code for this information.
Once you have the class name, you can convert it into an actual Class object using NSClassFromString(), and message the result from there.
Disclaimer: This probably shouldn't be depended upon for production applications, as it is undocumented.
An id is an id. At runtime, all Objective-C objects have the same type (objc_object). This is tied up in the dynamic nature of ObjC. For example, an object can change classes at runtime, new classes can be created, and the class hierarchy can change. You can ask a specific instance what its type is (since this is stored in objc_object), but a pointer to an object is just a pointer to an object. Even less than that: it's really just a pointer to a C struct that happens to have extra memory allocated at the end (to hold subclass ivars).
Your macro seems interesting, but you'll probably need to pass the classname as the second parameter rather than autodetecting it.
Maybe i misunderstand what you are trying to achieve.
To get the class of an iVar, can't you use the class method of the iVar?
like:
NSString *aString = #"random string";
NSLog(#"%#",NSStringFromClass([aString class]));
I'm quite a newbie in Objective C, though I have some background in Java reflection.
Here, I have a classic class method findAll that find all the domain objects from the database. The class Univers directly inherits from DomainObject
#interface DomainObject : NSObject
- (NSString *) execute : (NSString*) method withJson:(NSString*)json;
+ (NSString*)findAll: (NSString*)json;
#end
#implementation DomainObject
- (NSString *) execute: (NSString*) method withJson:(NSString*)json{
method = [NSString stringWithFormat:#"%#%#", method, #":"];
//method is 'findAll:'
NSString* result = [ self performSelector:
NSSelectorFromString(method) withObject:json];// Error here
return result;
}
#end
The code was working when findAll was NOT a class method (ie -findAll declaration), but now I have the error : NSInvalidArgumentException -[Univers findAll:]
It clearly seems that the runtime is looking for an instance method.
Any idea to find my class method ?
Instead of calling
NSString* result = [self performSelector:NSSelectorFromString(method) withObject:json];
you need to call
NSString* result = [[self class] performSelector:NSSelectorFromString(method) withObject:json];
for class methods.
After all it's the object instance's class that supposed to be calling the method, not the instance itself.
Short explanation: NSObject implements - (Class)class; (not to be mistaken with + (Class)class of similar effect, which NSObject implements, too!) which returns the Class object of your instance object. Keep in mind that in Objective-C in addition to plain instance objects, Classes are actual objects, too: objects of type Class, that is (vs. id, NSObject, …).
See the documentation for the -class method here.
Btw, you should probably wrap your method call into an conditional block to prevent exceptions caused by calls to missing methods.
SEL selector = NSSelectorFromString(method);
if ([[self class] respondsToSelector:selector]) {
NSString* result = [[self class] performSelector:selector withObject:json];
}
In general it's a common pattern in Objective-C to call an object's class method by receiving the class object via [object class].
Consider this case of a class called Foo implementing a convenience method for returning an autporeleased instance of itself (to be called via: Foo *newFoo = [Foo foo];):
While it would certainly be possible to implement said method like this (after all we know the object's class name, right?):
+ (id)foo {
return [[[Foo alloc] init] autorelease];
}
the correct way is this:
+ (id)foo {
return [[[self alloc] init] autorelease];
}
As the first one would cause problems with polymorphism in subclasses (Such as a subclass called FooBar, for which it should clearly be [FooBar alloc] …, not [Foo alloc] …. Luckily [[self class] alloc] solves this dynamically).
While this is clearly not the right place for a thorough explanation of this (rather offtopic one might say) it's certainly worth noting/warning about, imho.
I always confusing to when i used of instance method and class method in programming. Please tell me difference between instance method and class methods and advantages of one another.
All the other answers seem to have been caught out by the incorrect tag that has now been fixed.
In Objective-C, an instance method is a method that is invoked when a message is sent to an instance of a class. So, for instance:
id foo = [[MyClass alloc] init];
[foo someMethod];
// ^^^^^^^^^^ This message invokes an instance method.
In Objective-C, classes are themselves objects and a class method is simply a method that is invoked when a message is sent to a class object. i.e.
[MyClass someMethod];
// ^^^^^^^^^^ This message invokes a class method.
Note that, in the above examples the selector is the same, but because in one case it is sent to an instance of MyClass and in the other case it is sent to MyClass, different methods are invoked. In the interface declaration, you might see:
#interface MyClass : NSObject
{
}
+(id) someMethod; // declaration of class method
-(id) someMethod; // declaration of instance method
#end
and in the implementation
#implementation MyClass
+(id) someMethod
{
// Here self is the class object
}
-(id) someMethod
{
// here self is an instance of the class
}
#end
Edit
Sorry, missed out the second part. There are no advantages or disadvantages as such. It would be like asking what is the difference between while and if and what are the advantages of one over the other. It's sort of meaningless because they are designed for different purposes.
The most common use of class methods is to obtain an instance when you need one. +alloc is a class method which gives you a new uninitialised instance. NSString has loads of class methods to give you new strings, e.g. +stringWithForma
Another common use is to obtain a singleton e.g.
+(MyClass*) myUniqueObject
{
static MyUniqueObject* theObject = nil;
if (theObject == nil)
{
theObject = [[MyClass alloc] init];
}
return theObject;
}
The above method would also work as an instance method, since theObject is static. However, the semantics are clearer if you make it a class method and you don't have to first create an instance.
If we don't want to create the object of class then we use the class method
if we want call the method through object of a class then we use the instance method
I don't know if we can talk of any advantage, this is rather a matter of what you are implementing.
Instance methods apply on instances of classes, so they need an object to be applied on and can access their caller's members:
Foo bar;
bar.instanceMethod();
On the other hand class methods apply on the whole class, they don't rely on any object:
Foo::classMethod();
Static member functions are informally called class methods (incorrectly). In C++ there are no methods, there are member functions.
Read up on the static keyword, that pretty much covers it.
MSDN:
http://msdn.microsoft.com/en-us/library/s1sb61xd.aspx
Google search:
http://www.google.ch/search?aq=f&sourceid=chrome&ie=UTF-8&q=static+keyword+c%2B%2B
Class methods are used with classes but instance methods are used with objects of that class i.e instance
//Class method example
className *objectName = [[className alloc]init];
[objectName methodName];
//Instance method example
[className methodName];
instance methods use an instance of a class, whereas a class method can be used with just the class name. + sign is used before the Class Method where as single desh (-) is used before the instance variable.
#interface MyClass : NSObject
+ (void)aClassMethod;
- (void)anInstanceMethod;
#end
They could also be used like so,
[MyClass aClassMethod];
MyClass *object = [[MyClass alloc] init];
[object anInstanceMethod];
or another example is:
[
NSString string]; //class method
NSString *mystring = [NSString alloc]init];
[mystring changeText]; //instance Method
Like most of the other answers have said, instance methods use an instance of a class, whereas a class method can be used with just the class name. In Objective-C they are defined thusly:
#interface MyClass : NSObject
+ (void)aClassMethod;
- (void)anInstanceMethod;
#end
They could then be used like so:
// class methods must be called on the class itself
[MyClass aClassMethod];
// instance method require an instance of the class
MyClass *object = [[MyClass alloc] init];
[object anInstanceMethod];
Some real world examples of class methods are the convenience methods on many Foundation classes like NSString's +stringWithFormat: or NSArray's +arrayWithArray:. An instance method would be NSArray's -count method.
In Objective-C there is the Alloc/Init metaphor. They've also added a shared convenience method called 'new' that internally just calls both in succession. And if I create a subclass of NSObject called FooClass, FooClass picks up those shared methods, including 'new'.
BUT... how the heck is that implemented??
It can't simply delegate to the base class because that would just instantiate an instance of NSObject, not your derived class FooClass, yet it still works! So how would someone write something similar?
In other words, the base class shouldn't be this...
+ (id) somethingLikeNew{
return [[NSObject alloc] init];
}
But rather this...
+ (id) somethingLikeNew{
return [[<SomethingThatMapsToFooClassType> alloc] init];
}
...where 'SomethingThatMapsToFooClassType' is the type of the derived class that inherits from NSObject and which needs to pick up the shared method 'somethingLikeNew'.
Basically I'm adding a category off of NSObject and I have shared methods that need to know the type, but the implementations are all generic, hence going in a category on NSObject and not all over the place in my class files (the same way you don't have 'new' all over the place. It's just there.)
Anyone? Bueller? Bueller?
M
Within any Objective-C method, the self (implicit) parameter refers to the receiver. In the case of a class method, self is the Class object. So polymorphic class methods can be implemented like
+ (id)somethingLikeNew
{
return [[self alloc] init];
}
Ha! Found it myself two seconds after posting this! You can use 'self' in a shared method to represent the class type for the derived class, so the above would simply be...
+ (id) somethingLikeNew{
return [[self alloc] init];
}
Damn, that was easy! Hope this helps someone else!
M