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

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.

Related

Store Objective-C classes in array and use them

Say I have two classes, BulbDevice and FanDevice, both are subclasses of Device and has a method signature like this:
+ (BOOL)isMyId:(NSInteger)someId;
If I wanted to create a class I could test it out:
if ([BulbDevice isMyId:someId]) {
Device *dev = [BulbDevice alloc] initWithId:someId];
}
But what I really want is to create a factory method inside a factory class, with minimum fuss when new device are added:
+ (Device)createDevice:(NSInteger)someId {
// say I have an array registered
NSArray *arr = #[[BulbDevice class], [FanDevice class]];
// Loop through it.
Device *device;
for (Class *c in arr) {
// The idea is kind of like this but I'm not sure how to make it work
if ([c isMyId]) {
device = [[c alloc] init];
}
}
}
The idea is that I only need to update arr in the factory method. So I think it is good to have something like this. But I am not sure how to make it work.
EDIT:
I took out the asterisk, but it won't work:
for (Class c in arr) {
// Now I want to access the isMyId which is a static method,
// but I how do I cast to that class? I mean not an object of the class, but to that class itself.
if ([(Device)c isMyId:]) {
}
}
But I still need a way to access that class method. Error says Used type 'Device' where arithmetic or pointer type is required, and even if it works, I want to access class method, not sending message to an object.
Or shall I store NSString in the array instead? But it is hard to find way to access the class method as well.
If I understand correctly what you are trying to achieve, then your approach seems to be correct.
There is only one thing that needs to be fixed:
for (Class c in arr)
c variable is not a pointer - the asterisk should be removed. Your code works.
The Class type is not an NSObject type, and although it is a bit special it is object-like or object-equivalent, so you are able to send it messages and store it in collections like you're doing.
You don't use the asterisk as #MaxPevsner says, because Class isn't used as a normal pointer-to-object. Think of Class as a special type like id which also doesn't get the * when you use it to reference an object.

Instance variable initialized without using "init" method?

Iam in the first phase of Objective-C learning curve, so please bear with me :).
Iam reading Apple documentation and doing exercises there also. Ive come to a problem with initialization of particular object, because I get unexpected (In my opinion) behavior in my Xcode.
To the point (lets make it simple):
STEP 1:
I have declared simple variable from a my custom class named XYZPerson. It looks like this:
XYZPerson *randomPerson;
"If" check for initialized object returned out "Not Initialized" (as expected).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
STEP 2:
I have allocated the memory for this object using "alloc" word on this variable. As I understand in this phase, memory for this variable gets allocated. It also returns the pointer?
Line of code looks like this:
XYZPerson *randomPerson = [XYZPerson alloc];
Checking for "nil" surprised me: (It executes "else" statement).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
I can also send messages to this object and they are executed just fine, without calling "init" method.
How can this be? Am I missing something? How can "randomPerson" variable be initialized before calling "init" method on this instance? Why is "init" there in the first place then?
Furthermore, the accepted answer here Difference between ! and nil check on Objective-C object , says that ALL object are set to nil in the alloc method!
Iam coming from Java and there is one way of declaring an object using "new" keyword. Without using "new" the instance of object will be always "null", and calling methods on that instance will result "NULLPOINTEREXPECTION Error".
P.S Is "nil" in objective C, same as "NULL" in Java?
Whole project looks like this:
main method:
XYZPerson *randomPerson = [XYZPerson alloc];
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
**XYZ Person Class:**
#import "XYZPerson.h"
#implementation XYZPerson
-(void)sayHello {
//[self saySomething];
}
-(void)saySomething:(NSString *) greeting {
NSLog(#"%#", greeting);
}
#end
I post an answer additional to that one linked in the comment for two reasons:
A. Your Q is slightly different.
B. I do not confirm with the linked answer in details.
First of all to your additional Qs: Yes, nil is Objectice-C's NULL. But there are some differences:
A. In most cases (using ARC) a reference to a pointer (not the object itself) is initialized with nil.
XYZPerson *person; // Usually automatically set to nil.
B. A message to nil is allowed in Objective-C, does not throw an exception and returns NO, 0, 0.0, nil, or whatever the representation of null resp. zero (if it does not have a null representation like integers) is for that type. Sometimes this is done intentionally and you can rely on that.
Two your main Q:
After creating an instance object with +alloc all instance variable (ivars) are set to NO, 0, 0.0, nil, or whatever the representation of null resp. zero is for that type. You should not set such ivars explicitly to that value.
For example, if the instances of the class XYZPerson has an ivar for the name typed NSString*, the ivar will be nil. So, one might think that an naked -init does not have any meaning, because it does not take parameters and therefore does nothing. But you simply do not know: Maybe something else is done in -init. And, that's probably a surprise for a Java developer, -init returns an object reference, so you cannot know, whether -init replaces the instance object, for example for twin toning. So even you do not see any meaning in it, the first message to an instance object has to be init. (Yes, in many case you would not see any difference, but you do not know, whether there is one or not or will be in future. It is a part of the API contract, so do it.)
In disagree with the linked answer in one point:
Sending +new… instead of +alloc -init is the better way to do it.
A. If you use a simple +new it is correct that it sends +alloc -init in many cases. Therefore it is obvious that this is not better or worse than sending +alloc -init. It is what it does. You always have to have a look at the documentation, whether a naked initialization, using +new or +alloc -init is allowed. But in such a case you likely do not want to do a naked initialization, because it is meaningless.
B. Sometimes it is for the implementor of a class easier to receive a new message to the class object.

Using objc_msgSendSuper to invoke a class method

I was going through and replacing #synthesized(self) locks w/ this method
void _ThreadsafeInit(Class theClassToInit, void *volatile *theVariableItLivesIn, void(^InitBlock)(void))
{
//this is what super does :X
struct objc_super mySuper = {
.receiver = (id)theClassToInit,
.super_class = class_getSuperclass(theClassToInit)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL, NSZone *) = (void *)&objc_msgSendSuper;
// id (*objc_superAllocTyped)(id objc_super, SEL, NSZone *) = (void *)&objc_msgSend;
do {
id temp = [(*objc_superAllocTyped)(&mySuper /*theClassToInit*/, #selector(allocWithZone:), NULL) init];//get superclass in case alloc is blocked in this class;
if(OSAtomicCompareAndSwapPtrBarrier(0x0, temp, theVariableItLivesIn)) { //atomic operation forces synchronization
if( InitBlock != NULL ) {
InitBlock(); //only the thread that succesfully set sharedInstance pointer gets here
}
break;
}
else
{
[temp release]; //any thread that fails to set sharedInstance needs to clean up after itself
}
} while (*theVariableItLivesIn == NULL);
}
which while a bit more verbose exhibits significantly better performance in non-contested cases
along with this little macro (excuse poor formatting, it's very simple). To allow the block to be declared after the initial nil check, looks to help LLVM keep the "already initialized" path extremely fast. That's the only one I care about.
#define ThreadsafeFastInit(theClassToInit, theVariableToStoreItIn, aVoidBlockToRunAfterInit) if( theVariableToStoreItIn == nil) { _ThreadsafeInitWithBlock(theClassToInit, (void *)&theVariableToStoreItIn, aVoidBlockToRunAfterInit); }
So initially implemented it using the commented out sections for objc_superAllocTyped (actually first using [theClassToInit allocWithZone:NULL], which was definitely the best approach :) ), which worked great until I realized that most of the singletons in the project had overridden allocWithZone to return the singleton method... infinite loop. So I figured using objc_msgSendSuper should sort it out quickly, but I get this error.
[51431:17c03] +[DataUtils allocWithZone:]: unrecognized selector sent to class 0x4f9584
The error doesn't seem to be related to the actual problem, as...
(lldb) po 0x4f9584
$1 = 5215620 DataUtils
(lldb) print (BOOL)[$1 respondsToSelector:#selector(allocWithZone:)]
(BOOL) $2 = YES
So I'm definitely missing something... I compared to assembly generated by a [super allocWithZone:NULL] method in an empty class... almost identical except for the functions called have different names (maybe just using different symbols, no idea, can't read it that well).
Any ideas? I can use class_getClassMethod on the superclass and call the IMP directly, but I'm trying to be reasonable in my abuse of the runtime :)
Alright, this wasn't actually that tricky once I recalled that the meta class contains all of the method information for a Class instance obtained via -[self class] or +[self] -> thanks http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
This error occurred because I was asking the runtime to look up the method in NSObject's set of instance methods, which obviously doesn't contain allocWithZone: . The mistake in the error log presumably originated because the receiver was a metaclass instance, and Apple has their interns implement error logs.
so while with a normal instance method call via objc_msgSendSuper, you would pass a metaclass instance as objc_super.super_class, to invoke a class method, the metaclass itself is needed (everything is one level up).
Example, and a diagram that helped me understand this - (http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)
struct objc_super mySuper;
mySuper.receiver = theClassToInit; //this is our receiver, no doubt about it
//either grab the super class and get its metaclass
mySuper.super_class = object_getClass( class_getSuperclass( theClassToInit ) );
//or grab the metaclass, and get its super class, this is the exact same object
mySuper.super_class = class_getSuperclass( object_getClass( theClassToInit ) );
Then the message can be resolved correctly. Makes perfect sense now that I started paying attention :P
Anyways, now that I found my mistake I feel like I've leveled up my Objc runtime understanding. I was also able to fix an architectural mistake made two years ago by someone I never met without having to modifying and re-test dozens of classes across 3 projects and 2 static libraries (God I love Objective-C). Replacing the #synchronized construct with a simple function call also halved the compiled code size of those methods. As a bonus, all our singleton accessors are now (more) threadsafe, because the performance cost for doing so is now negligible. Methods which naively re-fetched the singleton object multiple times (or in loops) have seen a huge speedup now that they don't have to acquire and release a mutex multiple times per invocation. All in all I'm very happy it all worked as I'd hoped.
I made a "normal" Objective-C method for this on a category of NSObject, which will work for both instance and Class objects to allow you to invoke a superclass's implementation of a message externally. Warning: This is only for fun, or unit tests, or swizzled methods, or maybe a really cool game.
#implementation NSObject (Convenience)
-(id)performSelector:(SEL)selector asClass:(Class)class
{
struct objc_super mySuper = {
.receiver = self,
.super_class = class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
? object_getClass(class) //if we are a Class, we need to send our metaclass (our Class's Class)
: class //if we are an instance, we need to send our Class (which we already have)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
return (*objc_superAllocTyped)(&mySuper, selector);
}
so
[self performSelector:#selector(dealloc) asClass:[self superclass]];
would be equivalent to
[super dealloc];
Carry on runtime explorers! Don't let the naysayers drag you into their land of handwaving and black magik boxes, it's hard to make uncompromisingly awesome programs there*.
*Please enjoy the Objective-C runtime responsibly. Consult with your QA team for any bugs lasting more than four hours.

Objective-C & KeyValueCoding: How to avoid an exception with valueForKeyPath:?

I've got an object of type id and would like to know if it contains a value for a given keyPath:
[myObject valueForKeyPath:myKeyPath];
Now, I wrap it into a #try{ } #catch{} block to avoid exceptions when the given keypath isn't found. Is there a nicer way to do this? Check if the given keypath exists without handling exceptions?
Thanks a lot,
Stefan
You could try this:
if ([myObject respondsToSelector:NSSelectorFromString(myKeyPath)])
{
}
However, that may not correspond to the getter you have, especially if it is a boolean value. If this doesn't work for you, let me know and I'll write you up something using reflection.
For NSManagedObjects, an easy solution is to look at the object's entity description and see if there's an attribute with that key name. If there is, you can also take it to the next step and see what type of an attribute the value is.
Here's a simple method that given any NSManagedObject and any NSString as a key, will always return an NSString:
- (NSString *)valueOfItem:(NSManagedObject *)item asStringForKey:(NSString *)key {
NSEntityDescription *entity = [item entity];
NSDictionary *attributesByName = [entity attributesByName];
NSAttributeDescription *attribute = attributesByName[key];
if (!attribute) {
return #"---No Such Attribute Key---";
}
else if ([attribute attributeType] == NSUndefinedAttributeType) {
return #"---Undefined Attribute Type---";
}
else if ([attribute attributeType] == NSStringAttributeType) {
// return NSStrings as they are
return [item valueForKey:key];
}
else if ([attribute attributeType] < NSDateAttributeType) {
// this will be all of the NSNumber types
// return them as strings
return [[item valueForKey:key] stringValue];
}
// add more "else if" cases as desired for other types
else {
return #"---Unacceptable Attribute Type---";
}
}
If the key is invalid or the value can't be made into a string, the method returns an NSString error message (change those blocks to do whatever you want for those cases).
All of the NSNumber attribute types are returned as their stringValue representations. To handle other attribute types (e.g.: dates), simply add additional "else if" blocks. (see NSAttributeDescription Class Reference for more information).
If the object is a custom class of yours, you could override valueForUndefinedKey: on your object, to define what is returned when a keypath doesn't exist.
It should be possible to graft this behavior onto arbitrary classes reasonably simply. I present with confidence, but without warranty, the following code which you should be able to use to add a non-exception-throwing implementation of valueForUndefinedKey: to any class, with one, centralized line of code per class at app startup time. If you wanted to save even more code, you could make all the classes you wanted to have this behavior inherit from a common subclass of NSManagedObject and then apply this to that common class and all your subclasses would inherit the behavior. More details after, but here's the code:
Header (NSObject+ValueForUndefinedKeyAdding.h):
#interface NSObject (ValueForUndefinedKeyAdding)
+ (void)addCustomValueForUndefinedKeyImplementation: (IMP)handler;
#end
Implementation (NSObject+ValueForUndefinedKeyAdding.m):
#import "NSObject+ValueForUndefinedKeyAdding.h"
#import <objc/runtime.h>
#import <objc/message.h>
#implementation NSObject (ValueForUndefinedKeyAdding)
+ (void)addCustomValueForUndefinedKeyImplementation: (IMP)handler
{
Class clazz = self;
if (clazz == nil)
return;
if (clazz == [NSObject class] || clazz == [NSManagedObject class])
{
NSLog(#"Don't try to do this to %#; Really.", NSStringFromClass(clazz));
return;
}
SEL vfuk = #selector(valueForUndefinedKey:);
#synchronized([NSObject class])
{
Method nsoMethod = class_getInstanceMethod([NSObject class], vfuk);
Method nsmoMethod = class_getInstanceMethod([NSManagedObject class], vfuk);
Method origMethod = class_getInstanceMethod(clazz, vfuk);
if (origMethod != nsoMethod && origMethod != nsmoMethod)
{
NSLog(#"%# already has a custom %# implementation. Replacing that would likely break stuff.",
NSStringFromClass(clazz), NSStringFromSelector(vfuk));
return;
}
if(!class_addMethod(clazz, vfuk, handler, method_getTypeEncoding(nsoMethod)))
{
NSLog(#"Could not add valueForUndefinedKey: method to class: %#", NSStringFromClass(clazz));
}
}
}
#end
Then, in your AppDelegate class (or really anywhere, but it probably makes sense to put it somewhere central, so you know where to find it when you want to add or remove classes from the list) put this code which adds this functionality to classes of your choosing at startup time:
#import "MyAppDelegate.h"
#import "NSObject+ValueForUndefinedKeyAdding.h"
#import "MyOtherClass1.h"
#import "MyOtherClass2.h"
#import "MyOtherClass3.h"
static id ExceptionlessVFUKIMP(id self, SEL cmd, NSString* inKey)
{
NSLog(#"Not throwing an exception for undefined key: %# on instance of %#", inKey, [self class]);
return nil;
}
#implementation MyAppDelegate
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[MyOtherClass1 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
[MyOtherClass2 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
[MyOtherClass3 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
});
}
// ... rest of app delegate class ...
#end
What I'm doing here is adding a custom implementation for valueForUndefinedKey: to the classes MyOtherClass1, 2 & 3. The example implementation I've provided just NSLogs and returns nil, but you can change the implementation to do whatever you want, by changing the code in ExceptionlessVFUKIMP. If you remove the NSLog, and just return nil, I suspect you'll get what you want, based on your question.
This code NEVER swizzles methods, it only adds one if it's not there. I've put in checks to prevent this from being used on classes that already have their own custom implementations of valueForUndefinedKey: because if someone put that method in their class, there's going to be an expectation that it will continue to get called. Also note that there may be AppKit code that EXPECTS the exceptions from the NSObject/NSManagedObject implementations to be thrown. (I don't know that for sure, but it's a possibility to consider.)
A few notes:
NSManagedObject provides a custom implementation for valueForUndefinedKey: Stepping through its assembly in the debugger, all it appears to do is throw roughly the same exception with a slightly different message. Based on that 5 minute debugger investigation, I feel like it ought to be safe to use this with NSManagedObject subclasses, but I'm not 100% sure -- there could be some behavior in there that I didn't catch. Beware.
Also, as it stands, if you use this approach, you don't have a good way to know if valueForKey: is returning nil because the keyPath is valid and the state happened to be nil, or if it's returning nil because the keyPath is invalid and the grafted-on handler returned nil. To do that, you'd need to do something different, and implementation specific. (Perhaps return [NSNull null] or some other sentinel value, or set some flag in thread-local storage that you could check, but at this point is it really all that much easier than #try/#catch?) Just something to be aware of.
This appears to work pretty well for me; Hope it's useful to you.
There's no easy way to solve this. Key Value Coding (KVC) isn't intended to be used that way.
One thing is for sure: using #try-#catch is really bad since you're very likely to leak memory etc. Exceptions in ObjC / iOS are not intended for normal program flow. They're also very expensive (both throwing and setting up the #try-#catch IIRC).
If you look at the Foundation/NSKeyValueCoding.h header, the comment / documentation for
- (id)valueForKey:(NSString *)key;
clearly states which methods need to be implemented for -valueForKey: to work. This may even use direct ivar access. You would have to check each one in the order described there. You need to take the key path, split it up based on . and check each part on each subsequent object. To access ivars, you need to use the ObjC runtime. Look at objc/runtime.h.
All of this is vary hacky, though. What you probably want is for your objects to implement some formal protocol and then check -conformsToProtocol: before calling.
Are your key paths random strings or are those strings under your control? What are you trying to achieve? Are you solving the wrong problem?
I don't believe this is possible in a safe way (i.e. without mucking with -valueForUndefinedKey: or something similar on other peoples' classes). I say that because on the Mac side of things, Cocoa Bindings—which can be set to substitute a default value for invalid key paths—simply catches the exceptions that result from bad key paths. If even Apple's engineers don't have a way to test if a key path is valid without trying it and catching the exception, I have to assume that such a way doesn't exist.

How to declare a class that conforms to a protocol as parameter type?

Is there a way to give, as a parameter, a class that conforms to a certain protocol?
What I tried at first, with a bit of hope, was this:
-(NSString *) getKeyForMyProtocolClass(Class<MyProtocol>)aClass
But that causes
[aClass superclass];
to give the warning "Instance method 'superclass' found instead of class method 'superclass'". I get the same sort of warning for conformsToProtocol:.
Since it gives no such warnings when the parameter is (Class)aClass, it seems Class< MyProtocol> is not actually of the Class type.
I should not be sending NSObject< MyProtocol>, since I need to determine the right key according to the class as well as its superclasses, and only create and add a new object if nothing is set to that key yet.
I could check with conformsToProtocol, but then I'd have to return a nil value which is just messy. I'd prefer to stop the issue at compile time.
So in short, is there a type declaration for a class that conforms to a protocol?
You can just typecast your class object to prevent the compiler warning. I was able to do the following:
- (void)tempMethod:(Class<NSObject>)klass {
id a = [(Class)klass superclass];
NSLog(#"%#", a);
}
Since you know the type of the object(Class object) you're passing this should work fine.