iOS Obj-C: Variable object that can be assigned as a double or a string? - iphone

I'm pretty new to iOS development, and I want to figure out if there's a good way to handle this issue. Basically, I'm making a technical calculator that returns some product specifications based on user input parameters. The product in question has specs for some, but not all user parameters, so I . In a constants file, I have a bunch of ATTEN_SPEC_X variables which are const double or const NSString *. Now, it's perfectly okay to be missing a spec, so my plan was to leverage NSArray's ability to hold different types and use introspection later to handle strings vs doubles before I report the returned specs.
Here's an incomplete example of one method I'm implementing. It's just a big conditional tree that should return a two-element array of the final values of spec and nominal.
- (NSArray *)attenuatorSwitching:(double *)attenuator{
double spec, nominal;
{...}
else if (*attenuator==0){
spec=ATTEN_SPEC_3; //this atten spec is a string!
nominal=ATTEN_NOM_3;
}
{...}
return {array of spec, nominal} //not actual obj-c code
So instead of making spec and nominal doubles, can I make them some other general type? The really important thing here is that I don't want to use any special handling within this method; another coder should be able to go back to the constants file, change ATTEN_NOM_3 to a double, and not have to retool this method at all.
Thanks.

The problem you'll run into is that NSArrays can't directly handle doubles. However, you can get around this if you start using NSNumber instances instead - you can return an NSArray * containing an NSString * and an NSNumber * with no problems. If you need even more general typing, the Objective-C type id can be used for any object instance (though still not with primitives; you can't make a double an id).
Later, when you get an array, you can use the NSObject method -isKindOfClass: to determine the type of object you're pulling out of the array, and deal with the string or number depending on the resultant type. If you need to convert your NSNumber back to a double, just use the NSNumber instance method -doubleValue to unbox your double. (+[NSNumber numberWithDouble:] goes the other way, giving you an NSNumber out of a double.)
If you're using a recent enough version of Xcode, you can even make these things literals, rather than having to litter calls to +numberWithDouble: all over the place:
return #[ #3, #"number of things" ]

Related

String compare vs Class compare in objective-C

I'm writing an objective-C game and I'm at the stage where i should start optimising some of my code in the game loops.
I have an extensive amount of class compare methods used,
if ([obj isMemberOfClass:[SomeClass class]])
etc.
I heard this sort of checking is quite expensive, because I choose only to have 1 array populated with multiple classes, I need some sort of class check.
I was thinking perhaps that adding a property to NSObject subclassing NSObject to contain a string property, that during initialisation i would make equal to the class name of that particular subclass. Then simply doing a
if ([obj.klass isEqualTo:#"SomeClass"])
Would this be beneficial?
I'm trying to keep as much dynamic coding out of the game loops as possible.
Thanks!
Short answer: no. String comparison is prohibitively more expensive compared to other methods of comparing (or: classifying, categorizing) objects.
Long answer: don't optimize what you haven't analyzed, measured and compared. What you really want to do before you start optimizing is to get a clear picture of how your app behaves and what its performance bottlenecks are. The change you're attempting is unlikely to lead to any noticeable change in performance, so I suggest to first find the real bottlenecks.
In this particular case, sending isEqual to an NSString is 4 times slower than isMemberOfClass if the test fails. And such conditional tests fail most of the time, which is why you should ignore the results of the test succeeding.
The successful string comparison is fast because it's a simple pointer comparison, if two strings are equal it is likely that they point to the same memory address. If they're not equal, then each character in the string will be compared for equality, probably by using the hash method.
Here are the results of the Object Comparison tests that I added to my performance test project. You can use that project to make further tests.
This is not really a direct answer to your question but is an answer in a broader sense.
In Objective-C the philosophy is more like that of Smalltalk in which you send the message and let the object decide what to do with it. If you find yourself having to do lots of tests to see what class an object is, you need to rethink your design.
For instance, if you have an array of objects and you want to convert each one to an integer to do some maths on it, you can do something like this:
for (id anObj in array)
{
int anInt = [anObj intValue];
// do something with anInt
}
It doesn't matter what the class of each anObj is, you can send -intValue to it. If your array is full of NSStrings and NSNumbers, for example, it doesn't matter, the code above will do what you expect.
Many classes do not define a method for the selector -intValue. For example, if you send that message to an instance of NSData it will respond by throwing an exception. There are a couple of ways to resolve this depending on circumstances.
ignore objects that don't respond to the selector by testing to see if the object knows about the selector
for (id anObj in array)
{
if ([anObject respondsToSelector: #selector(intValue)])
{
int anInt = [anObj intValue];
// do something with anInt
}
}
Define the selector for all classes you know will be put in the array. This is done by declaring a category. This way you can extend any Objective-C class without subclassing. For instance, you can define an intValue method for NSData that returns its length, or the sum of its bytes or some other appropriate value.
Formalise the requirement by declaring a protocol. You can then test for conformance to the protocol, or rely on compile time checks to make sure the objects you put in the array conform to the protocol.
There are lots of things you can do, but you need to get away a bit from the C++/Java model of class hierarchies. Objective-C is much more flexible in that respect.

objective c 2.0 - keyword casting vs non-keyword casting

I come from a C# background and I am learning Objective-C. I was wondering if I could get some clarification on syntax. Thanks in advance for your help.
What is the difference between the lines of code shown below? Given that I know for sure that the type in the "tempItem" dictionary is an (NSString *) type and newsItem.pictureUrl is also an (NSString *):
Scenario 1:
newsItem.pictureUrl = [tempItem objectForKey:#"picture"];
Scenario 2:
newsItem.pictureUrl = (NSString *)[tempItem objectForKey:#"picture"];
I know what you mean. I also started out preferring to cast, since it clearly shows what you are doing. But after a while, it gets terribly tedious and you learn to omit it.
The fact that there are no generics, and that the containers only store objects, is a bit of a bummer, especially if you come from a language with generics. It means you are constantly and explicitly converting between objects and simple types (e.g. between NSNumber and int) and that there is no way, except to query [object class], to ensure you only get an NSString or an exception you can handle.
But the cast will not make any difference. If the object returned is not an NSString, and you cast it to one, it will make no difference. The cast does no implicit type checking, nor a conversion. It merely reinterprets the return value.
Casting between object types can basically only affect two things:
What warnings the compiler emits (e.g. "the class for this variable doesn't appear to have the method you're trying to call")
What properties the object has and how they work (e.g. the equivalent getter for self.awesome might be [self awesome] or [self isAwesome])
It emphatically does not affect what kind of object you get. The static types at compile time are just hints for the compiler. If you cast an object to a type that it isn't, you're just lying to the compiler.
In that particular case, it doesn't have any effect at all. Some people do write code like that, but AFAIK that's just because they find it comforting to just act like they're using a statically typed language (even though Objective-C isn't).
There's no difference between the two lines of code; it's purely stylistic.
The method objectForKey: here returns an object of type id, which is a generic object pointer. In Objective-C, an id can be implicitly converted to any Objective-C object type without a cast. The following two lines are equivalent:
id someId = ...;
NSString *someString = someId; // #1
NSString *someString = (NSString *)someId; // #2
This is similar to how in C, a pointer of type void* can be implicitly converted to a pointer to any other type without a cast (that is also true of Objective-C, but void* pointers are discouraged in Objective-C; that is not true in C++).
As far as type safety goes, both are equivalently unsafe. If the runtime type of the object is in fact the type you're casting it to (whether the cast is explicit or implicit) or a subclass thereof, then everything will work as intended. If the runtime type is not what you're expecting, then most likely an NSException will be thrown with the common object does not response to selector error, due to calling a function that doesn't exist for that type. It's also possible you might crash with a segmentation fault due to accessing an ivar that doesn't exist or has an unexpected value (since the object really isn't that type).
If you're unsure of that object's runtime type, you should check its runtime type with the -class or -isKindOfClass: methods, and then only take action if it's a particular type. Prefer using-isKindOfClass:`, since that still works with subclasses, as opposed to comparing the class for exact equality with a particular class. For example:
id someId = ...;
if ([someId isKindOfClass:[NSString class])
{
// It's an NSString
NSString *someString = someId;
// Do stuff with someString...
}
The type of an Objective-C instance is really only useful for determining the appropriate amount of memory to allocate for creation of the instance, and for static analysis (code completion, compilation etc). At run time the instances are all represented by id's and the actual type of the object means much less. This dynamic behavior is by design, and allows a great amount of flexibility when designing ObjC applications.
You will see very little typecasting in the typical ObjC program.
Casting is only really necessary when you want to have the compiler understand the type for a call, so that it doesn't give "may not respond" warnings.

String comparison failing to function properly in Objective-C

I'm all quite new to Objective-C and pointers and whatnot, so go easy on me.
Basically, i have a place in my code where i extract NSDictionaries from an NSArray based on their date key.
I check for equality by doing this:
if ([[dictItem valueForKey:#"Date"] isEqualToString: date])
Strangely though, it only becomes true for one of the many objects, namely the one with the same pointer value.
How can i explicitly and beyond any doubt compare the VALUE of two strings and NOT the pointer address?
Thanks.
Edit: Perhaps i should mention that for all the comparisons on which it fails, the date has been inserted into the dictionary from a textFields text-property, if that matters.
Your code should work. Are you sure [dictItem valueForKey:#"Date"] is not nil?
Have you tried comparing without the dictionary, that is, storing one of the strings in some variable directly, just to check if that works?
Also, you might want to consider using actual NSDate objects. You can convert String to NSDate and vice versa with NSDateFormatter.

String declarations and assignments: 3 methods

For non-retained string declarations, are these three lines the same?
NSString *list2 = self.map;
NSString *list2 = [NSString stringWithFormat:#"%#", self.map];
NSString *list2 = [NSString stringWithString:self.map];
They all create an autoreleased string object, right? Is there a preferred method among these, or are there any differences in the memory usage or behavior of "list2" depending on these methods?
For some reason, I find the manipulation of strings in objective-C the most confusing transition from other languages.
The simple fact, You don't own the object in the above three cases,
So you could use either,
This is more related to choice of developer then performance.
Go through the Memory Management Programming Guide
They all create an autoreleased string object, right?
No, the first one merely assigns the pointer returned by string.map to list2. The second and third ones theoretically create new NSStrings that you don't own and assign them to list2. However, if string.map returns an immutable string, the third one will probably give you the same pointer (possibly retained and autoreleased).
In all cases you do not own the (new) string. That's actually all you need to know. They may be autoreleased, but it is not relevant to you using them.

How do I generically use KVC to update fields?

Say I have the following Objective-C class:
#interface Foo {
int someNumber;
NSString *someString;
}
and for reasons I won't get into here, I want to use KVC to update, in a generic fashion, the values for those variables:
[f setValue:object forKey:#"someNumber"];
or
[f setValue:object forKey:#"someString"];`
If object is a string and I'm updating the someNumber variable, it seems that I need to know to use an NSNumberFormatter to get an NSNumber and then Cocoa automatically converts that to an int inside setValue:forKey:.
Is there any way to avoid this custom code and have Cocoa infer the conversion to an int from a string, or do I need to catch this situation each time and handle it myself?
The following code should handle the conversion automatically, as long as the object parameter is a member of a class that implements integerValue.
Note that both NSNumber and NSString implement this method.
- (void)setValue:(id)object forKey:(NSString *)key
{
if ([key isEqual:#"someNumber"])
{
someNumber = [object integerValue];
}
//...
}
edit: (side note):
You can avoid writing the method yourself by using objective-c properties. The default implementation of setValue:forKey: will do the work for you, as long as you create properties for someNumber and someString.
You should do the conversion yourself with a number formatter, it gives you finer control than anything that the framework might consider to be appropriate. It is also, probably, not a good idea to use a single instance of an object to update the values for your ivars. More appropriately, you could perform your update based on the class of the object (providing you are not storing in an id by querying the runtime as to the class of the object by means of object_getClassName. More information is available in the Objective-C 2.0 Runtime Reference. But in general, you will likely find bugs in your code as a result of doing things that way.
I'm with wisequark on this one. I think of setValue:forKey: as equivalent to calling the setter directly. If you had a method:
- (void)setSomeValue:(NSInteger)aValue
You wouldn't expect to be able to pass in an NSString and hope the method can figure it out.
Put another way, a key value gives you access to a property, and that property has a type. When using KVC, numbers just get wrapped in an NSNumber object so that there's only one setValue:forKey: method. Therefore, I say it's always the calling code's responsibility to package up the string in an NSNumber object.
It's times like these I lament that Cocoa Touch doesn't support bindings. Bindings would allow you to add a value transformer that could handle the transformation from string to number automatically for you. Maybe in the next version!