Using the value of a string to cast an object - objective-c

I have to cast an object to a type which is contained in a string and I don't know if it's possible.
My class is in a string :
NSString *myClass = #"User";
and I have an object that I want to cast with this type.
object = (myClass)object; // Doesn't work
object = (myClass *)object; // Doesn't work
How can I do that ?

There's no point to doing this — objects' static types only get checked at compile time, and this could only happen at runtime even if there were a way to do it (since objects such as strings don't actually exist until runtime).
If you're trying to change the object from one class to another, that wouldn't work anyway — casting from one pointer type to another only lies to the compiler about what type of data it points to, it doesn't actually change the data. In order to convert objects from one type to another, there has to be a method or function that takes the data from the old object and creates a new object of the desired type (such as NSString's dataUsingEncoding: to convert from NSString to NSData).
But if you have a dynamically selected class and you need some type to give the variables for instances of that class, you can just use id (possibly with a protocol if all the possible classes respond to the same messages).

Related

Xcode doesn't recognize properties after assigning to id

I have an NSMutableArray of two different objects inside. I am trying to recognize the first object of the array and assign it properly. Here is the example code with my idea of
// I should declare a variable here, firstly I thought about "id someObject;"
// and assigning to it in if statement.
id someObject;
if ([[someArray objectAtIndex:0] isKindOfClass:[firstOpponent class]]) {
someObject = (firstOpponent*)[someArray objectAtIndex:0];
} else {
someObject = (secondOpponent*)[someArray objectAtIndex:0];
}
[someObject method]; // this is OK
someObject.position; // property 'position' not found on object of type '__strong id'
With the idea of declaring "id someObject;" before if statement there is some problem with properties. I read that it isn't possible, thats why I'm asking for other solutions.
Your if statement achieves nothing in respect of typing.
In Objective-C a cast on a reference type, such as (firstOpponent *), does nothing at runtime; it simply allows the compiler to produce better error messages.
In your code you cast, which tells the compiler the type of the reference, and then you immediately assign to a variable of type id - which is the most general/least specific object reference type - and the compiler now knows nothing about the contents of someObject other than it contains a reference to some object.
So your code is equivalent to:
id someObject;
someObject = someArray[0];
[someObject method]; // this is OK
someObject.position; // compile time error
When calling a method on a reference typed as id, in your case someObject, the compiler does no checking and simply allows the method call. At runtime a check is done to verify the actual object references supports the method, and if not a runtime error will occur and the application will be aborted.
However the compiler will only call a property on a object whose type it knows. This is because it needs to know the type to determine what method call to translate the property access into. In general, but not always, the property access:
object.property // read a value
object.property = value // write a value
translate into the method calls:
[object property]
[object setProperty:value]
So you can access the property by doing the translation yourself and writing one of the second pair. If at runtime the reference object does not support the property then you will get an error and your application will abort.
Another option is to define a protocol, say OpponentProtocol, which declares the methods and properties all opponent classes should implement, and then have both your opponent classes implement it. You may then declare:
id<OpponentProtocol> someObject;
where the type means "any object reference as long as it implements the protocol OpponentProtocol". With such a type the compiler knows how to translate a property access into the appropriate method call, so you can access OpponentProtocol properties on someObject.
HTH
You should create a protocol that your two classes can confirm to and use id < MY_PROTOCOL > instead of just id so the compiler knows what the class is capable of responding to.

Objective-C setValue:forKey on c primitive types

I'm trying to teach myself objective-c using the big nerd ranch book, it is a really great book but certain aspects confuse me.
The current chapter is talking about using setValue:forKey function which I understand is a method defined in NSObject. The book says that you can use this on a c primitive like int or float and gives this example
I have a custom class called Appliance and in it is an integer instance variable called voltage that stores the voltage of the current appliance
I initialize a new appliance called a
appliance *a = [[appliance alloc]init];
[a setValue:[NSNumber numberWithInt:240] forKey:#"voltage"];
he then sets up a custom setter for voltage and logs the voltage when its called to prove it works
-(void)setVoltage:int(x) {
NSLog(#"setting voltage to %d",x);
voltage =x;
}
whats confusing me is that NSNumber numberWithInt returns a pointer to an NSNumber object thats stored on the heap correct? so then how does he log the integer stored in NSNumber using the %d token. I understand that would log an integer but isn't an object being passed in? furthermore I thought that since voltage was defined as an integer and not a pointer to something it couldn't hold the address to an object in its memory? or is NSNumber kind of forcing it to hold its memory address without actually having voltage being declared as a pointer?
sorry for the confusion this chapter basically kicked my butt.
The conversion between objects and scalar types is handled automatically by the Key-Value Coding methods. From the documentation:
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
Similarly, setValue:forKey: determines the data type required by the
appropriate accessor or instance variable for the specified key. If
the data type is not an object, then the value is extracted from the
passed object using the appropriate -<type>Value method.
So in your case, intValue is applied automatically to the passed NSNumber
object, and the resulting integer is passed to setVoltage:.
You are correct in that you are creating an NSNumber instance and passing that. But, you're passing it to setValue:forKey: and it's doing some work for you. It's finding the appropriate setter method for voltage (setVoltage:), checking the data type and unboxing the number into an int before calling the setter.

What is difference between dynamic binding Vs dynamic typing in Objective C

I am having hard time to understand what is difference between dynamic binding Vs dynamic typing in Objective C. Can someone explain this ?
Dynamic typing in Objective-C means that the class of an object of type id is unknown at compile time, and instead is discovered at runtime when a message is sent to the object. For example, in the following code, the class of foo isn't known until we attempt to send the message componentsSeparatedByString:.
id foo = #"One Two Three";
NSArray *a = [foo componentsSeparatedByString:#" "];
If instead of using the id data type we had done the following...
NSString *foo = #"One Two Three";
...then we'd be using static typing rather than dynamic typing.
Dynamic binding means that the compiler doesn't know which method implementation will be selected; instead the method implementation is looked up at runtime when the message is sent. It basically helps us with Polymorphism. So
[foo description]
results in invoking a different method implementation if, for example, foo is an instance of NSArray rather than an instance of NSString.
with dynamic typing you can have a variable of type id that can store any type of object. with dynamic binding you can do this: id obj; [obj doWhatever]; and as long as obj is of a type that implements -(void)doWhatever it will work.
From Apple Documentation
Dynamic typing
A variable is dynamically typed when the type of the object it points to is not checked at compile time. Objective-C uses the id data type to represent a variable that is an object without specifying what sort of object it is. This is referred to as dynamic typing.
Dynamic typing contrasts with static typing, in which the system explicitly identifies the class to which an object belongs at compile time. Static type checking at compile time may ensure stricter data integrity, but in exchange for that integrity, dynamic typing gives your program much greater flexibility. And through object introspection (for example, asking a dynamically typed, anonymous object what its class is), you can still verify the type of an object at runtime and thus validate its suitability for a particular operation.
The following example illustrates dynamic typing using a heterogeneous collection of objects:
NSArray *anArray = [NSArray arrayWithObjects:#"A string", [NSDecimalNumber zero], [NSDate date], nil];
NSInteger index;
for (index = 0; index < 3; index++) {
id anObject = [anArray objectAtIndex:index];
NSLog(#"Object at index %d is %#", index, [anObject description]);
}
The object pointed to by the variable at runtime must be able to respond to whatever messages you send to it; otherwise, your program throws an exception. The actual implementation of the method invoked is determined using dynamic binding.
Dynamic binding
Dynamic binding is determining the method to invoke at runtime instead of at compile time. Dynamic binding is also referred to as late binding. In Objective-C, all methods are resolved dynamically at runtime. The exact code executed is determined by both the method name (the selector) and the receiving object.
Dynamic binding enables polymorphism. For example, consider a collection of objects including Dog, Athlete, and ComputerSimulation. Each object has its own implementation of a run method. In the following code fragment, the actual code that should be executed by the expression [anObject run] is determined at runtime. The runtime system uses the selector for the method run to identify the appropriate method in whatever the class of anObject turns out to be.
NSArray *anArray = [NSArray arrayWithObjects:aDog, anAthlete, aComputerSimulation, nil];
id anObject = [anArray objectAtIndex:(random()/pow(2, 31)*3)];
[anObject run];
If you are familiar with Javascript, dynamic typing in objective C is pretty much similar to what 'var' is in Javascript, where the actual type of this variable will be figured out at run-time and once it's assigned. It can be reused and retyped as many times as you like which makes it risky to use if you don't know the type of the object being held at the time of the execution as you as it could through 'unrecognized selector' run time exceptions. In Objective-C, all objects are of type id and therefore a variable of type id can be used for dynamic typing.
Dynamic Binding on objects is the 'polymorphic' behavior where the right method is called based on the type of object it's being called on at run time.
I hope that helps

Incompatible Type for Argument 1 Objective-C

I have a function that sets an entity within a Core Data store. I used to have all values it would be storing as type double, however now I must make it accommodate NSStrings as well. Consequently, I changed the type of the parameter the function takes in, to an id type. However, now I get the error:
error: incompatible type for argument 1 of 'numberWithDouble:'
...at the following lines:
//...
[dfm setTimeStamp:[NSNumber numberWithDouble:value]];
//...
[[fetchedObjects objectAtIndex:0] setValue:[NSNumber numberWithDouble:value] forKey:#"timeStamp"];
//...
Apparently it doesn't like the [NSNumber numberWithDouble:value] segment of each line. I was contemplating making a container class that holds an NSNumber type (doesn't Apple already have a class like this?) to get around this problem, but I thought that there has to be an easier way I am not thinking of (besides duplicating the function and changing the type of the value parameter). Any ideas? Thanks in advance!
EDIT:
Here is the function declaration:
-(void)setItemInDFMWhilePreservingEntityUniquenessForItem:(attribute)attr withValue:(id)value
attribute is merely an enum which specifies which entity to store within. The problem is that the compiler is giving me problems with value being of type id, theoretically I can pass in anything I want, and I believe the way I have it I am implying that I will be passing it as an NSNumber, but the compiler doesn't like that as that is not actually a class instance I suppose?
The problem is that the compiler is
giving me problems with value being of
type id, theoretically I can pass in
anything I want, and I believe the way
I have it I am implying that I will be
passing it as an NSNumber, but the
compiler doesn't like that as that is
not actually a class instance I
suppose?
By declaring value as id, you can pass any object you want. But why do you "suppose" that NSNumber isn't an object, when it's clearly documented as being an object? The warning isn't about passing an NSNumber instance when you've declared value as an id - that's perfectly valid, because id means "any object," and an NSNumber instance is an object. The warning comes from calling +numberWithDouble:, a method that takes a double for its first argument, and passing it value, which is declared as id - i.e. an object. You can't pass an object to a method that expects a double.
Your proposed solution, typecasting value with (NSInteger)value will silence the warning, but it won't fix the problem. The typecast simply converts the memory address the object pointer targets to an integer value. If (as your edit suggests) value is already an NSNumber object, what do you hope to gain by creating another one, or by typecasting its memory address to an integer? Just do:
[dfm setTimeStamp:value];
The problem lies with the value variable. It should be declared as a double (primitive) for this call to succeed.
edit: after rereading your question, do a check in the function on the type of value, if it is an NSString (use [value isKindOfClass:[NSString class]]) store it as such, if its not then its a double (if thats the only two types you are passing) and store it as such.
Can't you just pass the NSNumber instead of double?
Just realized that the call I was making (numberWithDouble:) was having the compiler check for a primitive, i.e. double. Changing it to the following worked like a charm:
[dfm setTimeStamp:[NSNumber numberWithInteger:(NSInteger)value]];
Thanks to those that responded!

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.