How to treat NSArray of mixed content as of unified content? - objective-c

Class B is derived from Class A.
Class B overrides '- (NSString *) description', overridden in Class A too.
I made a new 'NSArray' of pointers to instances, both Class A and Class B.
Is it possible to treat all of them in a cycle as Class A instances, more precisely - is it possible to use Class A '- (NSString *) description' for all of them?
Currently, I use a check if it's a Class B instance, and call initializer that makes Class A instance in that case. But it seems unnecessary, I don't like this solution.

Yes you can do it.
for (ClassA item in myMixedArray)
NSLog("#%", item.description);
This is valid if myMixedArray has elements of ClassA, ClassB. In fact (although not correct and very confusing) it works for any class or object pointer, even if they are unrelated to Class A or B. The only issue is making sure that item implements description, otherwise it will fail.
In your specific case it is very safe, because any element would somehow be a ClassA object (either directly or inherited).
If some day you need to have totally unrelated objects you can use id as the iterator type.
for (id item in myMixedArray)
[id someMethodThatAllObjectsImplement];
EDIT (to clarify misunderstood question):
In Objective-C there's no easy way to force a call on the base class implementation, unlike C++, even type casting the pointer will prove useless. There's a tricky way to force calling the base class implementation explained on this question.

The following example may be a little easier on the eyes than the solution in the other thread mentioned in Merlevede's answer.
for ( id item in array )
{
if ( [item isMemberOfClass:[ClassA class]] )
{
NSLog( #"%#", item );
}
else
{
struct objc_super parent = { item, [item superclass] };
NSLog( #"%#", objc_msgSendSuper( &parent, #selector(description) ) );
}
}
The objc_msgSendSuper function can be used to call any method that returns an id
Edit - forgot to mention that you need to
#import <objc/message.h>
Edit 2 - based on feedback from OP in the comments, here is the ultimate solution
for ( id item in array )
{
struct objc_super baseClass = { item, [ClassA class] };
NSLog( #"%#", objc_msgSendSuper( &baseClass, #selector(description) ) );
}

Related

Objective C - How to use inheritance to limit the types of classes entered into a method or array, and read those objects at a later time? [duplicate]

This question already has answers here:
Is there any way to enforce typing on NSArray, NSMutableArray, etc.?
(11 answers)
Closed 9 years ago.
How can I limit the type of objects put in an array if the limited objects are all inherited from a superclass?
So for instance, I have a parent class called
parentObj
I then have 3 child classes that have parameters that are added and not available to the superclass (parentObj)
childClass1
childClass2
childClass3
Then I have some other classes that are not related but I want to stay out of the array I'm trying to build up
otherClass1
otherClass2
I have this mutable array
NSMutableArray *arrayOfChildren;
that I want built up primarily of the three child classes of parentObj and not be able to contain the otherClasses
I don't want to build a different method to read and write the child classes to the array for each child class, because there could very well be more child classes that I build!
How can I have one method to add those classes to the array, and one to read them, including the child's added parameters?
The primary way I handle this problem was actually taught to me by a PHP book I was reading. Obviously PHP is not as strict as Objective C, so I had to do some changes.
I feel that this is a very useful skill to know how to do, especially for game programmers, where an array might need to carry hundreds of objects, all restricted to a certain type.
The nice thing about inheritance, is that the child classes also take on the "type" of their parent/ grandparent classes (and as far as you can go up if your tree of classes is long).
For example, if I had a method that had a parameter of type parentObj, you could put any of its children in that parameter slot and the code will run.
- (BOOL) addObjectToArray: (parentObj *) obj {
[array addObjectToArray:obj];
return true;
}
BOOL worked = [self addObjectToArray:childClass1];
[self addObjectToArray:childClass2];
[self addObjectToArray:childClass3];
[self addObjectToArray:otherClass1];
the code will run all the way up to the last line, in which it won't work properly. otherClass1 is not of type parentObj, so it won't work. This has successfully allowed us to limit the types of classes that the array can hold in one easy method!
Now reading the parameters from the objects is our next step to tackle. I'm sure there are other easier ways to do it, but this is how I do it.
I put a parameter in the super class (an integer) that will hold a so called ID for the child class.
parentObj.h:
//...
- (id) initWithProperties...:() :() :()... andID: (int)idType;
#property(nonatomic) int type;
//...
parentObj.m:
//...
- (id) initWithProperties...:() :() :()... andID: (int)idType {
//...
self.type = idType;
//...
}
//...
childClass1.h:
//...
#property(nonatomic) int someOtherPropertyOfChild1;
//...
childClass1.m:
//...
- (id) init {
self = [super initWithProperties... ...andID:1];
if (self) {
}
return self;
}
//...
childClass2.h:
//...
#property(nonatomic) int someOtherPropertyOfChild2;
//...
childClass2.m:
//...
- (id) init {
self = [super initWithProperties... ...andID:2];
if (self) {
}
return self;
}
//...
etc...
You need to remember which ID correlates to which child class, otherwise you are bound to get errors.
So now say you had a for loop that cycled through all the objects in the array full of classes. And say we needed to print out that extra parameter in each child class, how would we do that? I will show you how.
let's assume the variable being iterated in the for loop is x.
switch([array objectAtIndex:x].type) {
case 1:
//remember that childClass1 is id of one
childClass1 *CC1 = [array objectAtIndex:x];
NSLog(#"%d", CC1.someOtherPropertyOfChild1);
break;
//...
if the default case is called, that means that the object it is getting from the array is a parentObj object, or a child class that is not ID'd correctly.
I hope that this helps you in your troubles, and I hope it helps you understand why inheritance is important, and why you should use it!
Create a wrapper method that you use to add objects to the array:
- (void)addObject:(id)object
{
if ([object isKindOfClass:[parentObj class]])
{
[self.arrayOfChildren addObject:object];
}
}
You could also add an isMemberOfClass check if you wanted to also exclude instances of the parentObj class itself.

How to get the class that the method is defined, not that of the instance that the method is called?

[self class] returns the Class of the instance of the method being called, but is there a way to get the Class that the method is defined? Suppose Class B extends A, and b is an instance of B, I want a method in A that returns A not B, even when called from b.
edited:
I trying to create a NSObject category that has -(void)releaseProperties method, which fetches all properties defined in that class and set nil for the non-readonly object properties.
- (void)releaseProperties {
unsigned int c = 0;
objc_property_t *properties = class_copyPropertyList([self class], &c);
for(unsigned int i = 0; i < c; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
NSString *propertyType = [NSString stringWithUTF8String:property_getAttributes(property)];
if([propertyType hasPrefix:#"T#"] // is an object
&& [propertyType rangeOfString:#",R,"].location == NSNotFound // not readonly
) {
[self setValue:nil forKey:propertyName];
NSLog(#"%#.%# = %#", NSStringFromClass(cls), propertyName, [self valueForKey:propertyName]);
}
}
free(properties);
}
I want to use this method in the dealloc method, but class_copyPropertyList([self class], &c) will not return properties defined in it's superclass, so the super-dealloc chain doesn't work well. So, instead of passing [self class], I wanted to pass the class that the specific dealloc method is being called.
I don't think there's a direct way of doing this, but you can call class_getMethodImplementation_stret with the current class to get the function pointer for the method that would be called. Then walk your superclasses, calling the same function with them until it returns something different. The previous superclass will be the one that is providing the implementation for your class.
Edit: Sorry, I may have misread the question. If you are looking for the first superclass in the hierarchy that defines a method with that signature, then you can just walk the superclasses calling respondsToSelector: until one of them doesn't. The method I describe above is to find the superclass providing the implementation that is inherited, not the definition.
For example, class A could define foo:, then class B (which is a subclass of A) could override it, then class C (which is a subclass of B) could ask where foo: comes from. If you want the class that provides the definition, you want A, and should use the second approach I describe. If you want the class that provides the implementation, you want B and should use the first approach I describe.
I have a feeling that [super class] might work for you.
And if you call "[super ..." (fill in the blank with whatever you want to call) within B, you'll be calling into a method that lives in A.
Class A can just provide a method like:
- (Class) classA
{
return [A class];
}
That's not generalizable, but your question insists on a non-generalizable answer.
It's a pretty bizarre thing to want. It suggests a problem with your design. May I ask why you want it? What problem are you trying to solve?

What is the best way output the name of the class who's method is being called?

I am aware of NSStringFromClass.
My question relates to the situation where the same method is implemented in multiple classes in an inheritance tree, and you want to debugging information as to which class it is executing from.
Example:
Let us have three classes: A-->B-->C , inheriting as displayed by the arrows.
If each of them has a method foo(), defined:
-(void) foo
{
// Do some work particular to the class it is in.
.....
//Each method ends with the debugging statement:
NSLog("In foo of class:%$",NSClassFromString([self class]));
}
The problem occurs when foo of Class B, calls [super foo]. When [super foo] (i.e. Class A) reaches the NSLog statement, [self class] returns class B, and not A.
Likewise if C called [super foo], the log statement in super would log class C.
What I want to do, is output the class whose method implementation is being executed - so if class B calls [super foo], then the log statement in [A foo] outputs Class A.
The simple way is to replace NSClassFromString with a hardcoded string representing the class name, but I was wondering if there is a better way to do this ?
You can use __PRETTY_FUNCTION__ to include both the class and the method name:
NSLog(#"Greetings from %s", __PRETTY_FUNCTION__);
I don't believe that there is a compiler-time macro for just the class name.
There might not be a macro for class, but there is __FILE__ and __LINE__ macros in the C language. They expand to the current file and line number. You can use them in NSLog. I use __PRETTY_FUNCTION__ when I remember it, but I remember __FILE__ and __LINE__ more.
Example:
NSLog( #"%s %d", __FILE__, __LINE__ );
Remember that __FILE__ isn’t an Objective-C string.
The problem occurs when foo of Class B, calls [super foo]. When [super
foo] (i.e. Class A) reaches the NSLog statement, [self class] returns
class B, and not A.
Sure. That's because self points to an object, and that object's class doesn't change just because you call a method of the superclass.
The simple way is to replace NSClassFromString with a hardcoded string
representing the class name, but I was wondering if there is a better
way to do this?
As others have pointed out, you can use a macro like __PRETTY_FUNCTION__, but I think the simple and obvious approach is the best. You know the name of the class when you're writing the code, so you can write:
NSLog("In foo of class: %#", #"ClassA");
Each of your classes has its own implementation of your method, so each one can print its own class name in the message. Something like __PRETTY_FUNCTION__ is useful when you're writing a debug macro that you're going to use in multiple functions. That's not the case here. Using the obvious approach makes it that much easier to see what's going on, and that's important during debugging (which I assume is what you're trying to do here).
i think you would have to walk up the class hierarchy using class_getSuperclass and class_getInstanceMethod, comparing differences in the methods. do that to determine the objc class, then use class_getName or NSStringFromClass to get its name.
This would look something like:
NSString* MONClassNameWhichImplementsMethod(id Self, SEL cmd);
and
- (void)method
{
NSLog(#"%# - %#",
MONGetClassWhichImplementsMethod(self, _cmd),
NSStringFromSelector(_cmd)
);
}
and
// NOT COMPILED -- FOR ILLUSTRATION ONLY
Class MONClassWhichImplementsMethod(Class cls, SEL cmd) {
assert(cls && cmd && "srsly?");
Class super = class_getSuperclass(cls);
Method m1 = class_getInstanceMethod(cls, cmd);
assert(m1 && "srsly?");
Method m2 = class_getInstanceMethod(super, cmd);
if (0 == m2) {
return cls;
}
else if (m1 != m2) {
return cls;
}
else {
return MONClassWhichImplementsMethod(super, cmd);
}
}
NSString* MONClassNameWhichImplementsMethod(id Self, SEL cmd) {
return NSStringFromClass(MONClassNameWhichImplementsMethod(Self.class, cmd));
}
if it blows up from deep recursion, you've another problem.

How to detect a property return type in Objective-C

I have an object in objective-c at runtime, from which I only know the KVC key and I need to detect the return value type (e.g. I need to know if its an NSArray or NSMutableArray) of this property, how can I do that?
You're talking about runtime property introspection, which happens to be something that Objective-C is very good at.
In the case you describe, I'm assuming you have a class like this:
#interface MyClass
{
NSArray * stuff;
}
#property (retain) NSArray * stuff;
#end
Which gets encoded in XML something like this:
<class>
<name>MyClass</name>
<key>stuff</key>
</class>
From this information, you want to recreate the class and also give it an appropriate value for stuff.
Here's how it might look:
#import <objc/runtime.h>
// ...
Class objectClass; // read from XML (equal to MyClass)
NSString * accessorKey; // read from XML (equals #"stuff")
objc_property_t theProperty =
class_getProperty(objectClass, accessorKey.UTF8String);
const char * propertyAttrs = property_getAttributes(theProperty);
// at this point, propertyAttrs is equal to: T#"NSArray",&,Vstuff
// thanks to Jason Coco for providing the correct string
// ... code to assign the property based on this information
Apple's documentation (linked above) has all of the dirty details about what you can expect to see in propertyAttrs.
Cheap answer: use the NSObject+Properties source here.
It implements the same methodology described above.
The preferred way is to use the methods defined in the NSObject Protocol.
Specifically, to determine if something is either an instance of a class or of a subclass of that class, you use -isKindOfClass:. To determine if something is an instance of a particular class, and only that class (ie: not a subclass), use -isMemberOfClass:
So, for your case, you'd want to do something like this:
// Using -isKindOfClass since NSMutableArray subclasses should probably
// be handled by the NSMutableArray code, not the NSArray code
if ([anObject isKindOfClass:NSMutableArray.class]) {
// Stuff for NSMutableArray here
} else if ([anObject isKindOfClass:NSArray.class]) {
// Stuff for NSArray here
// If you know for certain that anObject can only be
// an NSArray or NSMutableArray, you could of course
// just make this an else statement.
}
This is really a comment addressing an issue raised by Greg Maletic in response to answer provided by e.James 21APR09.
Agreed that Objective-C could use a better implementation for getting these attributes.
Below is a method I quickly threw together to retrieve attributes of a single object property:
- (NSArray*) attributesOfProp:(NSString*)propName ofObj:(id)obj{
objc_property_t prop = class_getProperty(obj.class, propName.UTF8String);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
return attrArray;
}
Partial list of attribute keys:
R Read-only
C Copy of last value assigned
& Reference to last value assigned
N Nonatomic property
W Weak reference
Full list at Apple
You can use isKindOfClass message
if([something isKindOfClass:NSArray.class])
[somethingElse action];
If you know that the property is defined :
id vfk = [object valueForKey:propertyName];
Class vfkClass = vfk.class;
And compare with isKindOfClass, isSubClass, etc.

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