How to tell if a Class inherits from NSObject (Objective-C) - iphone

I'm working in Objective-C on the iPhone and need to know whether a 'Class' inherits from 'NSObject'.
I tried checking if it responds to an NSObject selector:
bool success = [myClass respondsToSelector:#selector(class)];
but you can guess what happened... it didn't even respond to "respondsToSelector:" so it throws a "does not implement doesNotRecognizeSelector:" exception.
I tried to catch that exception, but it looks like it can't be caught with a #try-#catch.
Any ideas?

Go direct to the Objective-C runtime:
#import <objc/runtime.h>
/* originally posted version — works because eventually class_getSuperclass(class)
returns nil, and class_getSuperclass(nil) does so also. */
BOOL classDescendsFromClass(Class classA, Class classB)
{
while(1)
{
if(classA == classB) return YES;
id superClass = class_getSuperclass(classA);
if(classA == superClass) return (superClass == classB);
classA = superClass;
}
}
/* shorter version; exits straight after classA turns into nil */
BOOL classDescendsFromClassShorter(Class classA, Class classB)
{
while(classA)
{
if(classA == classB) return YES;
classA = class_getSuperclass(classA);
}
return NO;
}
...
if(classDescendsFromClass(classToTest->isa, [NSObject class]) ...
class_getSuperclass does what it says, and it's safe to compare metaclasses by pointer in the Objective-C runtime because there is only exactly one instance of the metaclass for each class. The isa pointer is the only thing that's definitely in struct objc_object.
EDIT: additionally, there are known bugs in the iPhone simulator that cause some exceptions not to be caught by try/catch blocks. I've reported them as a bug to Apple and been told that mine was a duplicate, so they are definitely aware. Did you try your code on a real device or just in the simulator?
EDIT2: from the wider context given elsewhere in this conversation, something like this might be smarter:
#import <objc/runtime.h>
BOOL classRespondsToSelector(Class classA, SEL selector)
{
return class_getInstanceMethod(classA, selector) ? YES : NO;
}
....
if(classRespondsToSelector(instance->isa, #selector(respondsToSelector:))
{
// great, we've got something that responds to respondsToSelector:; do the
// rest of our querying through there
}

You can use the methods isKindOfClass: and isMemberOfClass: to determine whether a class is a subclass of another class or if it is a particular class.

respondsToSelector: is itself an NSObject-defined selector, so you can't use it. I don't believe there's a way to do this without getting very deep into the internals of Objective-C.
May I ask why you have objects that aren't descendants of NSObject? Apple very strongly recommends you don't attempt to create them, and with good reason.

The class 'Class' does not inherit from NSObject. That means methods defined by NSObject (such as isKindOfClass or respondsToSelector) cannot be used on it.
What are you trying to do with it in the first place?

Related

Crash when calling optional protocol method

I use a protocol with some optional methods.
#protocol PhotoDropDownDelegate <NSObject>
#optional
- (void)getEditResult:(NSString *)status;
- (void)getImageForDiagram:(UIImage *)image andImagePath:(NSString *)imagePath;
- (void)dismissPhotoDropDown;
#end
I assign this for a class
photoDropDownViewController.photoDropDownDelegate = self;
I use only one method
- (void)getImageForDiagram:(UIImage *)image andImagePath:(NSString *)imagePath
{
// Make a Image on center of screen
PhotoLayer *photoLayer = [PhotoLayer nodeWithLengthOfShape:300 andHeight:200 andPathToImage:imagePath];
photoLayer.position = ccp(400, 500);
photoLayer.nameObject = [self makeNewName_ForShape];
photoLayer.isSelected_BottomRightElip = YES;
photoLayer.isSelected = YES;
[photoLayer doWhenSelected_Elip_BottomRight];
[photoLayer show_Elip];
[list_Shapes addObject:photoLayer];
[self addChild:photoLayer];
photoLayer = nil;
// Set Button Delete
selected_GerneralShapeLayer = (GerneralShapeLayer *) [list_Shapes lastObject];
[self updateStatusForButtonDelete];
}
Then the compiler show error:
[AddDiagramLayer dismissPhotoDropDown]: unrecognized selector sent to instance 0xb2a8320'
when I implement the others methods the error is disappear
-(void)getEditResult:(NSString *)status {
}
-(void)dismissPhotoDropDown {
}
As I've known, if a method in #option we can use it or not.
I don't understand what happened here. Can anyone explain to me
All the #optional directive does is suppresses compiler warnings if the optional methods are not implemented. However, if you call a method that the class does not implement, the app will still crash, as the selector (method) you tried to call is not recognised by the class, since it's not implemented.
You can work around this by checking whether the delegate implements a method before calling it:
// Check that the object that is set as the delegate implements the method you are about to call
if ([self.photoDropDownDelegate respondsToSelector:#selector(dismissPhotoDropDown)]) {
// The object does implement the method, so you can safely call it.
[self.photoDropDownDelegate dismissPhotoDropDown];
}
This way, if the delegate object implements an optional method, it will be called. Otherwise, it won't, and your program will continue running as normal.
Note that you should still use the #optional directive to denote methods that are optional to implement, in order to avoid compiler warnings when you don't implement them. This is particularly important for open source software or libraries that will be distributed to clients, as this directive tells the developers that haven't read your implementation, but can only see the header, that they don't need to implement these methods, and everything will still be fine.

Objective-c class initializer executes multiple times

Unless I misunderstood it, this LINK from Apple's documentation clearly states that the class initializer, "+ (void)initialize)", only executes once per class.
Here is the excerpt:
Special Considerations
initialize it is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.
However, I'm getting a weird behavior on my project and the initializer is being executed twice. So I have to check if _classContext is null. I only got one class that has this method. What are the possible reasons why this is happening?
I'm using XCode 4.5.2 and OS X 10.8.2. I got multiple iOS simulators, iPhone 5.1 and 6.0.
+ (void) initialize
{
num++;
NSLog([NSString stringWithFormat:#"Times: %i", num]);
if(_classContext == nil)
_classContext = [[myClass alloc] init];
}
This will happen if you have a subclass of this class. The initialize method will be called for the class and each subclass.
The proper way to code the initialize method is:
+ (void)initialize {
// Replace ThisClass with the actual class name
if (self == [ThisClass class]) {
// do initialization here
}
}
+initialize can be called more than once and one should use caution when overriding +initialize.
Please read the blog post +initialize Can Be Executed Multiple Times (+load not so much) at bbum's weblog-o-mat.

Objective-C - Overriding method in subclass

I am having some trouble figuring out hour to accurately override a method in one of my subclasses.
I have subclass (ClassB) of another customclass (ClassA):
#interface ClassB : ClassA {
}
and within ClassA, there is a method called:
-(void)methodName;
which fires correctly.
However, I need this method to fire in ClassB.
I've tried implementing (in ClassB):
-(void)methodName {
[super methodName];
}
but it still won't fire in ClassB.
How can I override methodName so that it will fire in ClassB?
You just add your custom code in methodName in classB :
- (void)methodName
{
// custom code
// call through to parent class implementation, if you want
[super methodName];
}
First, make sure your init method creates a ClassB object and not a ClassA (or something else) object.
Then, if you want to create a completely different classB (void)methodName: method than the one found in classA, this is the way to go:
Super is the superclass. By calling [super methodName] you're asking ClassA to execute it's own methodName.
If you want to completely override methodName from classA, just don't call super.
So, basically, in your classB's implementation of methodName:
-(void)methodName {
// Remove [super methodName]
// Insert the code you want for methodName in ClassB
}
Feel free to read Messages to self and super in Apple's The Objective-C Programming Language document.
By writing:
-(void)methodName {
[super methodName];
}
You tell the compiler: When executing methodName of Class B, call methodName of its superclass (Class A). So if you want Class B to do something different you have to write code that results in a different behavior. Like this:
-(void)methodName {
NSLog(#"Hello, world!");
}
Now by calling methodName of Class B "Hello, world!" will be printed on the console.
-(void)methodName {
[super methodName];
}
Wanna call methodName (in ClassB), just remove [super method] then you can fire it.
Cause super is call back to ClassA
Although this question is too old, but there are sill some learners as every expert was,
The following is quoted from Apple documentation.
"The new method must have the same return type and take the same number and type of parameters as the method you are overriding."
full answer can be found in Apple method overriding documentation
Hope this helps someone.

calling optional delegate methods

i created a delegate for a class
#protocol gameDelegate <NSObject>
#optional
-(void)gameStarted;
#required
#end
now in my game object i called this method:
[self.delegate gameStarted];
so now, if i initiate this object anywhere and set the delegate everything works fine until the gameStated gets called, because its not implemented in the main object where the game object is created (because its optional).
i tried some variations of this
if(![self.delegate respondsToSelector: #selector(gameStarted)]) {
//[self.delegate gameStarted];
}
but this is not working for me.
any ideas how to make this "really" optional?
thanks in advance
Omit the negation from your if statement:
if ([self.delegate respondsToSelector:#selector(gameStarted)]) {
...
}
To accomplish this in swift, I recommend the following:
#objc protocol MyDelegate {
optional func optionalMethod()
}
class MyClass : MyDelegate {
// optionalMethod() does not have to be declared
}
Then to call the optional on your delegate object, simple use if delegate.optionalMethod?(){}
Checking if a delegate implements an optional method and then calling it is such a common pattern that I use a preprocessor macro SAFE_CALL that checks respondToSelector: and then calls the method.
The macro is:
#define SAFE_CALL(obj,method) \
([obj respondsToSelector:#selector(method)] ? [obj method] : nil)
and it is used like this:
SAFE_CALL(sourceDelegate, refresh)
// or
NSString *response = SAFE_CALL(object, responseValue)
Note this version works only with methods with no parameters.
Originally it was implemented as a C function, but that causes warnings with performSelector leaks when using ARC. As a preprocessor macro it works exactly as expected.

Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

When implementing an +initialize or +load method in one of your Objective-C classes, should you always start with this kind of guard?:
#implementation MyClass
+ (void)initialize {
if (self == [MyClass class]) {
...
}
}
...
#end
Seems like code in +load and +initialize usually only wants to be executed once. So this would help avoid dupe execution when subclasses load/initialize.
I guess I'm just wanting some reinforcement from some ObjC wizards that this is necessary/common practice...
What's the common wisdom on this? would you recommend always doing this?
Is your advice the same for both +load and +initialize, or is there a difference in they way they should be handled?
thanks.
The quick answer is: No.
An in-depth discussion of this matter can be found on the Apple developer mailing list.
The gist of it is that:
The runtime will actually call +initialize on super classes before it is called on subclasses.
If you do include the guard, subclasses of your class that have their own +initialize method will not trigger dependent KVO notifications.
For an example of point #2, be sure to read this post in the thread mentioned above.
Yes, you should do this in your intialize and load methods if you are initializing globals that should only be initialized once.
That said, there are a number of cases where you may avoid it...
You shouldn't wrap with this conditional if the work needs to be performed on every inheritant of every class:
For example, adding all inherited class names for each class to a set.
edited addition: or you're establishing KVO dependencies (as mentioned by eJames)
There are also situations where you just needn't bother:
If the actions you perform are idempotent (don't change values if repeated)
The class is "sealed" (has no descendants by design)
The "idempotent" part is relevant. An initializer should just be setting the initial state (which should be the same each time). In a good initializer, repetition shouldn't matter. Although I suppose that if you forget to wrap the method in the conditional when it does matter, this might be annoying.
edited addition: A different approach, that properly reflects any initialize-only-once requirements would be to test if your properties to initialize are initialized already. i.e.
id myGlobalObject = nil;
+(void)initialize
{
if (myGlobalObject == nil)
{
myGlobalObject = [[MyGlobalClass alloc] init];
}
}
YES!!!!
Because the initialize method of a class may be invoked many times. e.g. when you implement initialize in parent class, and don't implement in sub class, then you call sub class first, the initialize of parent will invoked twice.
#implementation BaseClass
+ (void)initialize
{
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
#end
#interface SubClass : BaseClass
#end
#implementation SubClass
// don't implement the initialize method
#end
==================
now when you call SubClass first, just like
[SNSBaseSubLogic alloc]
look the debug console, output:
BaseClass initialize self=BaseClass, class=BaseClass
BaseClass initialize self=SubClass, class=BaseClass
so, you must use
+ (void)initialize
{
if (self == [BaseClass class]) {
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
}
to ensure the method body execute once.