Convert NSDictionary value (id?) to NSString or NSNumber to compare it - objective-c

pls help me with my trouble:
I can't compare to value to know successful result was or not.
I fetch json-object as NSDictionaty:
NSDictionary *returnDictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
And after that I get value for key: code
[returnDictionary objectForKey:#"code"];
If returning value is equal 1 so that's OK, but problem is that I don't know the type value of key "code". I tried to compare id, NSString, NSNumber but all were fail. What type of object should I compare ?

Did you use the comparison operator (==) to compare your objects?
If so, you didn't compare the values of your objects but their memory addresses.
If the object returned by [returnDictionary objectForKey:#"code"] is of type NSString you should use NSString's isEqualToString:
If it returns a NSNumber instance, you could compare the intValue of that object to 1.
[[returnDictionary objectForKey:#"code"] isEqualToString:#"1"]
or
[[returnDictionary objectForKey:#"code"] intValue] == 1

You can use
[[returnDictionary objectForKey:#"code"] isKindOfClass:[NSString class]]
to check the class of the object pulled from the dictionary.
Hope it helps!

If you expect the output to be an integer, you can call intValue.
[[returnDictionary objectForKey:#"code"] intValue] == 1
Also, you can log the type of the returned object by + class; method.
NSLog(#"%#", [[returnDictionary objectForKey:#"code"] class]);

put in a breakpoint and print out the classname...
type into the debugger: po [[returnDictionary objectForKey:#"code"]className]
that will give you the class, something like _NSCFString will be a string etc...

id is something of a "cheat" in Objective-C, though one that is officially "blessed". Generally, where a method returns id you can directly use that value to invoke a method on the returned type, without having to first cast to the appropriate type -- the compiler knows to suppress "can't find that method name" type warnings/errors when the call is on an id.
Of course, if you don't know the class of the object, you don't know whose methods to call. As Pablo suggests you can use isKindOfClass to test, if you have a suspicion. Alternatively, you can log the class name of an object with NSLog(#"The class is %s", object_getClassName(someId)); or one or two other ways.

Related

Return something other than nil from a method sent to nil

This is probably impossible with a category, but just in case it is doable, I wanted to find out.
I wrote a category on NSString and I have a category method that parses a comma delimited string into an NSArray cleaning up extra commas and spaces, and trimming the ends... so more comprehensive than the built-in method of similar functionality. Here's the rub though... what if the string is nil? Is there any way I can return an initialized NSArray with 0 objects instead of returning nil?
The scenario would go something like this...
NSArray *parsed = [someString parseStringIntoArray];
... assume someString is nil. Can I somehow get an initialized array out of this?
Obviously there are ways to work AROUND this, but keeping it clean and succinct, and using the category method... is it possible?
No. But yes, if you make some changes:
Since you call a instance method, this won't work. When you send a message to nil (aka call a method on a nil object) you will always get nil.
This is the key concept of nil itself.
You can for example add a class method and in this class method, you can then test against nil and return an empty array instead:
+ (NSArray *)parseStringIntoArray:(NSString *)string {
return [string componentsSeparatedByString:#","] ?: #[];
}
or you can simply use, what NSString has built in:
NSArray *parts = [#"foo,bar,la,le,lu" componentsSeparatedByString:#","] ?: #[];
EDIT
No, there's no way to return anything from a message sent to nil. That is baked into the very core of the runtime: objc_msgSend() is responsible for this behavior.
If the receiver is nil, objc_msgSend() resolves the expression to the appropriate 0 value for the return type of the method.
You will have to test for nil either before or after and change the value of the array manually.
(Incidentally, the fact that this is a category method is irrelevant.)

different values for NSNumber long in Objective-C

I have a variable which is a NSNumber of type long. It should hold the value -1.
When I log it to the Console it shows the expected value -1.
NSLog(#"myVariable %#", self.myVariable);
But the following expression in the if-clause is false.
if (myVariable == [NSNumber numberWithInt:-1]) {
...
}
The debugger shows the value 72057594037927935. Does anybody know what's wrong with it?
Thanks!
When you compare NSNumber to other objects, you have two options that do different things:
You can use == to check if two objects represent the same exact object instance, or
You can use isEqual: method to check if two objects represent the same value.
In your case the safest approach would be to use the second alternative
if ([myVariable isEqual:[NSNumber numberWithLong:-1]]) {
...
}
The == approach may or may not work, depending on the way in which you produced myVariable.
NSObject has a method: (NSString *)description
This method will define which is printed out when you put object on NSLog method.
NSNumber inherits NSObject and description method is implemented in the way that primitive value will be printed out, and that's why you saw expected value using NSLog(#"myVariable %#", self.myVariable);
Operator "==" will compare 2 objects in this case (compare both pointer and value) ==> it will return false in your case
If you want to compare primitive value of 2 NSNumbers, use following method instead (BOOL)isEqualToNumber:(NSNumber *)number;

unrecognized selector sent to instance when trying to retrieve string value

I have a NSMutablearray wich contain NSArrays(each array contain int and String values).
When i try to retrieve and display the data from the first array:
That was ok with the int value, it was displayed correctly.
NSLog(#"%i",[[[lesQuestions objectAtIndex:0] objectAtIndex:0] intValue]);
But when i try to display the String value:
NSLog(#"%#",[[[lesQuestions objectAtIndex:0] objectAtIndex:1] stringValue]);
I got exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString stringValue]: unrecognized selector sent to instance
I am definitely sure that the int value is the first item (index 0) and the String value is the second (index 1).
When i log the MutableArray which holds the NSArrays, i got the values correctly, so the problem is on the reference of the item i guess.
The object in your array is already an NSString, so the call to stringValue is unnecessary. NSString doesn't implement a method called stringValue, hence the exception you're seeing. Just do this:
NSLog(#"%#",[[lesQuestions objectAtIndex:0] objectAtIndex:1]);
-intValue is a method implemented by NSNumber, to get an integer primitive out of the NSNumber instance, and your use of intValue is correct assuming the first object in the array is an NSNumber (or an NSString, which also implements -intValue).
All that said, I don't generally think it's a great idea to store instances of different classes in the same array like you're doing. You'd probably be better off using an NSDictionary where each value is stored with a unique key, say #"index" for the number and #"name" for the string.
NSLog respectively the ability of standard objects do display themselfs is more powerful than you may think.
NSLog(#"%#",[[lesQuestions objectAtIndex:0] objectAtIndex:0]);
NSLog(#"%#",[[lesQuestions objectAtIndex:0] objectAtIndex:1]);
You could even find this useful unless you have a really high number of array elements:
NSLog(#"%#",[lesQuestions objectAtIndex:0]);
or
NSLog(#"%#",lesQuestions);
Give it a try!

sortedArrayUsingSelector what is it doing?

I am still new to objective-c and am trying to figure out what this statement is doing exactly.
[names allKeys] sortedArrayUsingSelector:#selector(compare:)];
I know that allKeys is getting all the keys from my dictionary. I know that sortedArrayUsingSelector is sorting my array im creating. Then im calling the compare method, that is where I am lost what is this doing? From the document on apple it says that "Returns an NSComparisonResult value that indicates whether the receiver is greater than, equal to, or less than a given number." I dont understand how it is sorting based of that method.
NSArray * sortedKeys = [[names allKeys] sortedArrayUsingSelector:#selector(compare:)];
The above code returns a sorted array of the dictionary keys using the selector you provide. The selector is actually the function that will be called on the object that is being sorted in your array. In this case your array contains strings so in the actual NSArray sorting code the following would be happening,
//...
[key1 compare:key2];
//..
If you passed in a different selector lets say #selector(randomFunction:) then in the sorting code the following would happen
//..
[key1 randomFunction:key2];
//..
Since NSString does not respond to the selector randomFunction you would get an error. If you wanted to create your own type of comparison function you would need to add a category to the class that the array contains (in your case a category to NSString).
A better way to sort an array is to use a block statement.
id mySort = ^(NSString * key1, NSString * key2){
return [key1 compare:key2];
};
NSArray * sortedKeys = [[names allKeys] sortedArrayUsingComparator:mySort];
The reason it's a better way is sorting any objects is very easy to do.
id mySort = ^(MyObject * obj1, MyObject * obj2){
return [obj1.title compare:obj2.title];
};
NSArray * sortedMyObjects = [myObjects sortedArrayUsingComparator:mySort];
- (NSComparisonResult)compare:
{
// if (value of firstObject) < (value of secondObject)
// return NSOrderedAscending
// else if (value of firstObject) == (value of secondObject)
// return NSOrderedSame
// else
// return NSOrderedDescending
}
The -sortedArrayUsingSelector: method in your example calls the -compare: method on the objects in the array. For some objects Apple has already implemented a -compare: method, for example if you read through the NSString documentation, you'll find a -compare: method implemented. You can also call custom comparison methods on your own custom objects if you've implemented a comparison method in these objects. Please note the comparison method doesn't have to be called -compare:, of importance is only the return value (NSComparisonResult) and the object the method receives.
The new array contains references to the receiving array’s elements, not copies of them.
The comparator message is sent to each object in the array and has as its single argument another object in the array.
For example, an array of NSString objects can be sorted by using the caseInsensitiveCompare: method declared in the NSString class. Assuming anArray exists, a sorted version of the array can be created in this way:
NSArray *sortedArray =
[anArray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
You should probably be using caseInsensitiveCompare: in this situation since you are most likely ordering an array of strings (keys from a dictionary).
You've assumed that sortedArrayUsingSelector is somehow separate to the compare: part of the code. This is not the case. compare: is the selector that is used to sort the array.
The sorting method gives you back an array where each element, when sent the specified selector and the next element in the array, gives the correct sort order.
How NSArray achieves this is not public but at root, the selector you define is used to compare pairs of objects from within the array, the result of which informs their placement in the sorted array.

How to use #encode() to get #"NSArray" in Objective-C

I'm using the runtime functions to get the type of a property (thanks to eJames for helping me to figure out this way).
The attribute string of the property looks like this:
T#"NSArray",&,Vstuff
I need to check if the property type is an array, at the moment I'm doing it like this:
- (BOOL)valueForKeyIsArray:(NSString *)key fromTagret:(id)target
{
NSString *lowerCaseKey = [self convertToKVCKey:key];
objc_property_t property = class_getProperty([target class], [lowerCaseKey UTF8String]);
NSString *propertyAttrs = [NSString stringWithUTF8String:property_getAttributes(property)];
NSString *encodedType = #"#\"NSArray\"";
NSRange range = [propertyAttrs rangeOfString:encodedType options:NSLiteralSearch];
return range.location != NSNotFound;
}
But since Apple can change the type definition string at any time, I would like to generate this #"NSArray" type string. I tried it with #encode(), but it did not work:
NSString *encodedType = [NSString stringWithUTF8String:#encode(NSArray *)];
So how can I generate this type string? Or is there a better way to check if this property attributes contain the array type?
There is no way to check this. In Objective-C source code the variables being typed as NSArray * is only there for the compiler to issue warnings. It has no meaning, and does not exist at runtime. If you mis-typed an NSArray as an NSString, you would get lots of warnings when compiling, but your code would behave exactly the same when run. At runtime all that is known is that the ivar/property is "an object".
Another way to think of it, is that once Objective-C is compiled, all object references are id references.
Just accept that if the runtime changes, your code will break, and move on. However, I think you might be miscategorizing ivars of type NSMutableArray *, CFArrayRef, or CFMutableArrayRef. You also seem to be assuming all keys correspond directly to a declared property.
The cleanest solution might be to assert that the sample object being used for the test (the target) must have a non-nil value for that key, and just grab the value and test that [[target valueForKey:key] isKindOfClass:[NSArray class]].