Objective-C: How to check if a class gets extended by a category? - objective-c

In Java, you can use instanceof to check if a class extends another class or implements an interface.
In Objective-C, you can use isKindOfClass to check if a class extends another class:
if ([myObject isKindOfClass:[AnClass class]]) { }
But how can I check if a class gets extended by a category?
EDIT 2
My code of the first EDIT was unfortunately a bit confusing and nonsensical, sorry! Now, here is my new code:
I'll explain the whole problem:
I've got a class ViewCustomerCreate thats extends UITableViewController. ViewCustomerCreate gets extended by the category ICheckBox. This is my code that doesn't work:
- (void)closeModalView {
UINavigationController *parent = (UINavigationController *)self.navigationController.parentViewController;
UIViewController *parentViewContr = parent.topViewController;
if ([parentViewContr isKindOfClass:[id<ICheckBox> class]]) { // ERROR-MESSAGE see below
id<ICheckBox> parent2 = (id<ICheckBox>)parentViewContr; // works fine :-)
[parent2 setSelectedElementId:checkedIndex]; // works fine :-)
}
[self.navigationController dismissModalViewControllerAnimated:YES];
}
The error message is: "error: 'id' is not an Objective-C class name or alias"
I think that I can't use isKindOfClass to check if a class gets extended by a category, isn't it?
PS: What do I want? I have a general modal view with checkboxes and if I close this view, the parent-view should get informed what the user choose.
EDIT 3
OMG, I confounded Category with Protocol!! Aaaaahhhhh ^^
THE SOLUTION:
if ([parentViewContr conformsToProtocol:#protocol(ICheckBox)]) {

There is no way to check if a class is extended by a category, but you can check whether or not an instance responds to a particular selector with:
- (BOOL)respondsToSelector:(SEL)sel;
In Objective-C you should worry less about what an object is, and worry more about what an object can do.
If it walks like a duck, sounds like a duck and looks like a duck, then it can probably fly, you know what I mean?
You should use this as such:
if ([myObject respondsToSelector:#selector(myMethod:)])
{
// do whatever you need to do
}
Just a quick note, since you mentioned Java interfaces. You can check if an object implements a protocol (similar to Java interfaces, but not exactly the same) by using:
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

If you have defined a category on UIViewController, there are no instances of UIViewController that it is not applied to. Hence, a runtime check does not make sense.
Let's look at your actual problem:
parent.setMySpecialValue = 1; // DOES NOT WORK :-(
What does "DOES NOT WORK" actually mean? Do you get a compiler error or a runtime error. If the former, there are a couple of possible issues to loo at:
You haven't included the header file containing the category in the source file that uses that method
It is a property that you have named incorrectly. If the property is called mySpecialValue, that line of code should read:
parent.mySpecialValue = 1;
or
[parent setMySpecialValue: 1];
As of now, categories cannot define instance variables, so having a synthesized property might be an issue, so that might also be your problem, but you need to give more information about what "DOES NOT WORK" means.

Related

Create a subclass of a class using parent's init - from another class

Edit: This downvoting syndrom here sucks big time. I asked a question where I in my opinion showed that I did my homework, and asked for advice. The upvoted answers implied going with compile time warnings, whereas my own and probably most clean OOP way didn't receive any interest.
Brief overview in order to understand why I need this and what I try to do: I'm writing an ORM that implements the data mapper pattern. A mapper (i.e. for SQLite results) must create subclasses of an entity class - using the initializer of the base entity class. So there is the problem.
The mapper does not, and should not, know about specific classes. Mapping descriptions and specific mappers for different data sources are abstracted away from the entity class, and by design part of the entity description.
Entities are similar to NSManagedObject, though the ORM follows a different pattern. The description any entity is created with is similar to NSEntityDescription (but also following a different pattern and purpose).
So my goal is to create entities that I know are subclasses of ManagedEntity, using the init method of ManagedEntity.
So the init of my mapper looks like this:
- (id)initWithEntityClass:(Class)EntityClass entityDescriptor:(EntityDescription*)entityDescriptor
{
self = [super init];
if (self)
{
_EntityClass = EntityClass;
_entityDescription = entityDescription;
... (assert that class is of subclass of ManagedEntity)
}
And some time later in my mapper I then want to create the concrete entity:
-(void)createEntityWithSQLiteResultSet:(sqlite3_stmt*)resultSet
{
// Problem: How to init a class known to be a subclass of ManagedEntity?
ManagedEntity *newEntity = [[_EntityClass] alloc] initWithEntityDescription:_entityDescription];
}
So how do I create this child class of ManagedEntity, using the init of ManagedEntity?
Sure, I could use respondsToSelector() for initWithEntityDescription and invoke that. But something tells me there should be a more elegant way where the class kind is already known. Also, respondsToSelector and selector invocation will do a runtime check only. Even though the entity initializer should not change, it seems a bad choice to lose compile time checking if this method exists.
As part of your mapping, you must know what subclass you need. Then use
ManagedEntity *newEntity = [[NSClassFromString(className) alloc] initWithEntityDescription:_entityDescription];
EDIT:
I was building out this in a GitHub project as I promised and realized why it may not compile. You must have -initWithEntityDescription: declared in a known class that is accessible within the scope. In this case, it would mean that you must declare and implement ManagedEntity -initWithEntityDescription: and have have `#import "ManagedEntity.h" at the top of your file.
To reinforce Neal's correct answer that the OP claims cant work because he knows objC :)
#import <CoreData/CoreData.h>
#interface TestMapper : NSObject
- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx;
#end
#import "TestMapper.h"
#implementation TestMapper
- (NSDictionary*)entityToClassMap {
return nil; //TODO ;)
}
- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx {
NSString *className = self.entityToClassMap[entity.name];
assert(className);
return [[NSClassFromString(className) alloc] initWithEntity:entity insertIntoManagedObjectContext:ctx];
}
#end
alternative using the runtime you want
id cls = NSClassFromString(className);
id alloced_cls = objc_msgSend(cls, #selector(alloc));
id newEntity = objc_msgSend(alloced_cls, #selector(initWithEntity:insertIntoManagedObjectContext:), entity, ctx);
return newEntity;

Is it safe to store id into real class pointer before checking type

I have a lot of code that looks like this:
id myObjectRaw = getObject();
if(![myObjectRaw isKindOfClass:[MyClass class]]) return nil;
MyClass * myObject = myObjectRaw;
...
Here id getObject() can return several kinds of object. However the above code feels clunky to me. Is it safe to write this?
MyClass * myObject = getObject();
if(![myObject isKindOfClass:[MyClass class]]) return nil;
...
The compiler doesn't complain, but I'm not sure that I'm not treading on undefined behaviuor if getObject returns an object not related to MyClass.
(And no, I can't use a super class, or interface, since I dont actually have control over all the classes that get returned.)
You can do it. Nothing undefined. The only danger is that if the type is wrong and you forget to check the type, it may crash due to unrecognized selector exception.
In compiled code, id, MyClass * and NSString * have no difference, they just a pointer to a ObjC object.
Both versions will work. The first feels clunky, but there are problems with the second one as well: Putting something into a variable of a specific type implies knowledge of its type, and checking the class of something that seems to be known already looks redundant. If someone (it might be you) looks at that code next year, he may find the class check superfluous and remove it.
I've been in a similar situation, and I went with a helper method that gives a properly typed result or nil, i.e.
-(Rectangle)getRectangleObject {
id data = getObject();
if ([data isKindOfClass:[Rectangle class]]) return data;
return nil;
}
This simplifies code and communicates the intention clearly.
If you need several different type checks, you can go with several methods, or pass the class to this helper method.
As long as all types of returned objects conform to NSObject protocol (Classes that inherit from NSObject class do) it is safe to use isKindOfClass: method.
So make sure getObject() method only returns objective-c classes that inherit from NSObject
EDIT
While compiler is fine with it, as #Eiko mentions someone reading the code will probably think the isKindOfClass: check is unnecessary. It is better to use the former code to let the reader know that getObject() might also return other types of objects.
When you use id myObjectRaw you are NOT defining what kind of object myObjectRaw is, thus the compiler won't know if MyClass * myObject = getObject(); is a valid operation or not. THe compiler assumes you know what you are doing. If getObject() returns an object that is different than MyClass or it's not a subclass of it your app may crash. This is a runtime error.
If getObject() returns different objects, you should be expecting at least one object of the kind of objects that can be returned. If need to handle different objects, you can always use if-else-if instructions like:
id myObjectRaw = getObject();
if([myObjectRaw isKindOfClass:[MyClass1 class]])
{
MyClass1 objectClass1 = myObjectRaw;
}
else if([myObjectRaw isKindOfClass[MyClass2 class]])
{
MyClass2 objectClass2 = myObjectRaw;
}
However, if the object returned is a MyClass2 object, and this class is a subclass of MyClass1 the first condition will be true. Therefore, the object will be saved as a MyClass1 object. If that's the case you need to establish priorities and put them accordingly in the nested if-else-if statement.

Objective C custom class methods not being called

So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.

What's the best way to create an object factory in objective-c?

I have a hierarchy of classes like this:
MyBox
|
|->ImageBox
|->GalleryBox
|->MovieBox
|-> ...
Each one identified by a string like #"image-box" or #"gallery-box" coming from an xml block like this:
<image-box><property1>value1</property1>...</image-box>.
So i decided to create a factory class, which can create for me the correct object given the xml block:
[BoxFactory.h]
#interface BoxFactory {
NSMutableDictionary* stringToClassMapping;
}
+(BoxFactory)sharedBoxFactory;
-(void)registerClass:(Class)clazz withKey:(NSString*)key;
-(MyBox*)getBoxFromXml:(NSString*)xmlBlock;
#end
[.m]
#import "BoxFactory.h"
#import "MyFantasticXmlLibrary.h"
BoxFactory* gInstance=nil;
#implementation BoxFactory
+(BoxFactory*)sharedBoxFactory {
if (gInstance==nil)
gInstance=[[BoxFactory alloc]init];
return gInstance;
}
-(id)init {
self=[super init];
stringToClassMapping=[[NSMutableDictionary alloc]initWithCapacity:10];
return self;
}
-(void)registerClass:(Class)clazz withKey:(NSString*)key {
[stringToClassMapping setObject:clazz forKey:key];
}
-(MyBox*)getBoxFromXml:(NSString*)xmlBlock {
NSString* key=[MyFantasticXmlLibrary getRootNodeName:xmlBlock];
return [[[stringToClassMapping objectForKey:key]alloc]initWithXml:xmlBlock];
}
#end
Now the problem is: where should the concrete classes call the registerClass:withKey: method? it seems like the correct place would be in the init method of the BoxFactory, but this means that the Factory must be modified for each class added, which is not scalable for big hierarchies.
I really would like to find a way to put the registration call in the concrete class itself, keeping a better organized code and less dependencies. But until now i didn't find a way to execute code when a class is loaded, without initing an instance of it.
Ok, i found it out 5 minutes after posting the question, but i still think it can help somebody so i'll leave it here:
There is a method +(void)load in NSObject called exactly when the class is loaded in the image, at the very start of the execution. So the best place to register the classes with their factory is the +(void)load method of the concrete class itself. For example:
#implementation ImageBox
+(void)load {
[[BoxFactory sharedBoxFactory]registerClass:[ImageBox class] withKey:#"image-box"];
}
-(id)initWithXmlBlock:(NSString*)xmlBlock {
[...]
I've got a few ideas:
Option 1: Change the XML
If you have control over the file format, you can then use NSClassFromString to get the Objective-C class to work with that way.
Option 2: Store you registration information in a .plist
If you store the mapping of Tag -> Class or Class -> Tag in a .plist, you can simply load that up at boot time with NSDictionary dictionaryWithContentsOfFile rather than have it all over the place in the code
Option 3: Attribute oriented approach
Decorate your classes with an property either via a category, or by protocol, this way you can query the object model at boot time and use Type Introspection to discover what classes are available and what their tags are. This avoids having to manage two things (plist and classes) and also avoids having to change the xml (sometimes a no-go because it isn't yours to muck with). It's a bit more heavy handed at boot time, but it may be an ok trade of depending on what you are doing.

Calling a static method on a class

Given a method like the one below, that returns a Class...
-(Class)getClass
{
return [MyAwesomeClass class];
}
...how do I call a static method on that class? I tried this, but it didn't work...
Class theClass = [anInstance getClass];
[theClass someStaticMethod];
How should I call a static method on theClass?
Edit to add:
It seems I was doing the right thing, and something else was causing the crash. Now I need to figure out how to get rid of the warning that the method someStaticMethod isn't found. What should I cast theClass to?
You do it exactly the way you've written it, assuming the class in question responds to someStaticMethod.
If it isn't working correctly, then one of these is most likely the case:
You don't have the class you think
The class doesn't respond to the message
You declared the method incorrectly
You haven't imported the header where the method is declared
The method itself is buggy
It is an old question but i answer it for completeness. if you use id instead of Class it will work
id theClass = [anInstance getClass];
[theClass someStaticMethod];
Compiler will be happy with this dynamic typing but you must be sure that Class will respond to +someStaticMethod or it will crash at runtime