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.
Related
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.
What is the difference of using:
+ (id) myMethod;
// Rather than
- (id) myMethod;
Using a + declares the method as a class method, or a method that can be called directly on the class, where the class is the object. So when you have this:
#implementation Foo
+(NSString*)method1 {
return #"Foo";
}
-(NSString*)method2 {
return #"Foo";
}
#end
The methods are called in different ways:
[Foo method1]; //=> #"Foo"
Foo* f=[[Foo alloc] init];
[f method2]; //=> #"Foo"
One other thing to note is that class methods don't have access to an instance, which means they can't access any kind of instance variables.
#Linuxios pretty much summed up the concept of class and instance method. However, since you mentioned getters and setters in your title, I want to point out that in Objective-C you can use properties instead of writing your own accessor methods. For example,
In the header file, you will have something like this:
#interface MyObject : NSObject
#property (nonatomic,retain) NSSet* mySet;
#end
In the m file, you wil have something like this:
#implement MyObject
#synthesize mySet;
#end
To access the set in another class you can do it like this:
myObject.mySet; // assuming myObject is an instance of the MyObject class
The top one is a class method (no instance required)
The second one is a instance variable (attached to a specific instance).
This answer explains the methods quite well:
Method Syntax in Objective C
[MyObject myMethod]; // did not have to create an instance
MyObject* myNewObject = [[MyObject alloc] init] autorelease];
[myNewObject myMethod]; // had to create an instance
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 am working a big iOS project, the design is not a nice as I would like it to be, but I must stick to it. (life can be a bitch sometimes).
The thing is that we have a Library that basically let's you browse a catalog. You have a filter, where you specify a certain search criteria, and you are presented with a list were you can press on the items that you are interested. When you press an item, you can see a more detailed description of it.
The company were a work for, sells this same software to many different companies that have different catalogs. The idea is that the Library has all the main functionality, and the project that use it, might in some way extend or completely override some of the given interfaces.
To give you an example, imagine my library has 2 classes that manages 2 views. They would be "FilterViewController" and "DetailsViewControllers". In some place of the code this classes gets instantiated. It would look something like this
My approach is something like this:
ProjectA side
// Here I configure the library
Library.FilterViewClass = ProjectAFilterViewController;
Library.DetailsViewClass = ProjectADetailViewController;
ProjectB side
Library.FilterViewClass = ProjectBFilterViewController;
Library.DetailsViewClass = nil;
Library side
// Did the user configure the library?
if(self.FilterViewClass == nil){
// I alloc the default ViewController
myFilterViewController = [[FilterViewController alloc] init];
}else{
// Here I would like to alloc the custom ViewController
myFilterViewController = [[Library.FilterViewClass alloc] init]; // DaProblem!
}
The problem with that approach is that I actually don't know if it's possible to instantiate object programmatically. Or at least I don't know how. Maybe I am using the wrong approach, some direction would be appreciated. Txs in advance!
To get class from string you can use this function
Class cl = NSClassFromString(#"MyClass");
To get class of existing variable just call class method.
Class cl = [obj class]; // assuming obj1 is MyClass
Now you can create instance of MyClass
MyClass *myClass = (MyClass*)[[cl alloc] init];
...
[myClass release];
Use
myFilterViewController = [[[Library.FilterViewClass class] alloc] init];
You can also instantiate from a class name, should that be useful to you:
id obj = [[NSClassFromString(#"MyClass") alloc] init];
Class someClass = [Foo1 class];
Foo * someObject = [[someClass alloc] init];
[someObject bar];
Class someClass2 = [Foo2 class];
Foo * someObject2 = [[someClass2 alloc] init];
[someObject2 bar];
Interface+Implementation:
#interface Foo : NSObject
- (void)bar;
#end
#interface Foo1 : Foo
#end
#interface Foo2 : Foo
#end
#implementation Foo
- (void)bar {
NSLog(#"abstract foo");
}
#end
#implementation Foo1
- (void)bar {
NSLog(#"foo1bar");
}
#end
#implementation Foo2
- (void)bar {
NSLog(#"foo2bar");
}
#end
Output:
2011-11-24 11:24:31.117 temp[21378:fb03] foo1bar
2011-11-24 11:24:31.118 temp[21378:fb03] foo2bar
I have a class "ClassA" with "MethodA", i have also a "ClassB" and I want to call "methodA" from "ClassB"; I write
#classA;
#property(nonatomic, retain) ClassA *classA;
//and also #synthesize...
then I call method with
[self.classA method];
but it don't call the method....then I write in viewdidload in classB
self.classA = [[ClassA alloc]init];
but this thing reset varaibles in ClassA.
How can I solve this situation?
EDIT: I have decided to rewrite my answer as I don't think the original was well worded.
I think you are failing to understand what the Objective-C 2.0 dot notation does. It is confusing, especially if you program in C or C++, as it's syntactically equivalent to the struct field or class variable access operator, but semantically different.
When you use:
self.classA = newClassA;
You are actually doing the same as:
[self setClassA: newClassA];
And when the #property classA is defined with the retain attribute, the compiler generates the setter method as something like:
- (void) setClassA:(ClassA *)newClassA
{
if (classA != newClassA)
{
[newClassA retain];
[classA release];
classA = newClassA;
}
}
In the code you have given:
[self.classA method];
Actually expands to:
[self setClassA: method];
Which is not what you intended.
The simplest way to avoid this confusion is to not use dot notation at all, and especially not within an instance method of the same class that deals with allocation or deallocation of the variable.
You are creating a new instance of ClassA with the alloc and init. You need to set the property to your existing instance of classA, it is difficult to advise how without more context, but perhaps when you are creating class b, do
classB.classA = self;
This assumes that class A creates class B in the first place.