ObjC: differentiating between NSValue and NSNumber - ios

I have some code where I will be receiving an object of unknown type. It could be a NSString, NSNumber, a scalar wrapped in a NSValue or some other class:
-(void) doSomethingWith:(id) value {
if ( <test-for-NSValue> ) {
// Do something with a NSValue
} else {
// Do something else
}
}
I need to identify where there is a scalar type inside a NSValue.
The problem is identifying a NSValue wrapped scalar vs a NSNumber. As NSNumber inherits from NSValue and both are class clusters, I'm haing trouble sorting them out.
So:
[value isKindOfClass:[NSValue class]] ... sees NSNumbers as NSValues.
[value isMemberOfClass:[NSValue class]] ... doesn't recognise NSValues because the instances are concrete subtypes.
Anyone got any idea how to do this?

first we need about the diffrenciation between iskindofClass and isMemberOfClass
isKindOfClass
Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
YES if the receiver is an instance of aClass or an instance of any class that inherits from aClass, otherwise NO.
isMemberOfClass
Returns a Boolean value that indicates whether the receiver is an instance of a given class.
YES if the receiver is an instance of aClass, otherwise NO.
Then very importantly
NSValue
An NSValue object is a simple container for a single C or Objective-C data item. It can hold any of the scalar types such as int, float, and char, as well as pointers, structures, and object id references. Use this class to work with such data types in collections (such as NSArray and NSSet), key-value coding, and other APIs that require Objective-C objects. NSValue objects are always immutable.
NSNumber
NSNumber is a subclass of NSValue that offers a value as any C scalar (numeric) type. It defines a set of methods specifically for setting and accessing the value as a signed or unsigned char, short int, int, long int, long long int, float, or double or as a BOOL. (Note that number objects do not necessarily preserve the type they are created with.) It also defines a compare: method to determine the ordering of two NSNumber objects
if ([value isKindOfClass:[NSValue class]]) //It will return YES because NSNumber value subclass or inherits from NSValue
{
..........
}
if ([value isMemberOfClass:[NSValue class]]) //It will return NO because NSNumber value is not a member of the NSValue
{
.........
}
Class objects may be compiler-created objects but they still support the concept of membership. Thus, you can use this method to verify that the receiver is a specific Class object.

What about:
-(void) doSomethingWith:(id) value {
if ([value isKindOfClass:[NSValue class]] && ![value isKindOfClass:[NSNumber class]]) {
// NSValue but not instance of NSNumber
} else {
...
}
}

Related

Objective c set value to a property if not null [duplicate]

I have this code
if ([args valueForKey:#"showSetupScreen"]) {
BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"];
NSLog(showSetupScreen ? #"YES" : #"NO");
// meetingConfig.showSetupScreen = showSetupScreen;
}
Where args is NSMutableDictionary.
args value in my dictionary is NO but when I set to BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"]; it changes into YES
Can someone help me in comprehending why this could be happening.
Attached Screenshot for your reference
A NSDictionary (or NSMutableDictionary) cannot directly contain a primitive C type, such as BOOL. Primitive numeric types (including Boolean) in NSDictionary are wrapped in NSNumber objects. See Numbers Are Represented by Instances of the NSNumber Class and Most Collections Are Objects.
Thus, use NSNumber method boolValue to extract the Boolean from the NSNumber, e.g.,
BOOL showSetupScreen = [[args valueForKey:#"showSetupScreen"] boolValue];
Or, more simply:
BOOL showSetupScreen = [args[#"showSetupScreen"] boolValue];
E.g., examples with primitive C types, including BOOL, NSInteger, and double:
NSDictionary *args = #{
#"foo": #NO,
#"bar": #YES,
#"baz": #42,
#"qux": #3.14
};
BOOL foo = [args[#"foo"] boolValue]; // NO/false
BOOL bar = [args[#"bar"] boolValue]; // YES/true
NSInteger baz = [args[#"baz"] integerValue]; // 42
double qux = [args[#"qux"] doubleValue]; // 3.14
For what it's worth, if you expand the values contained within args, that will show you the internal types for those values, and you will see that that the value associated with showSetupScreen (or foo in my example), is not a BOOL, but rather a pointer to a __NSCFBoolean/NSNumber:
[args valueForKey:#"showSetupScreen"] statement returns pointer (address in memory) and it has two options: some address (non zero value) and NULL (zero). For C programming language true is any non zero value (any address in memory in our case). And for this reason you get true in if operator and in showSetupScreen variable. But it only tells you that there is some object in the dictionary for the specified key, but not the value of this key (the value wrapped in this object). To get this value (BOOL in our case), you must call the boolValue.

#encoding for type id where id is actually any object

I have a following method.
- (void)someObject:(id)obj {
char* encoding = #encoding(typeof(obj));
NSString *s = [NSString stringWithCString:encoding encoding:NSUTF8StringEncoding];
NSLog(s);
}
this method always return #"#" whether I pass a variable of type NSNumber, NSArray, NSDictionary or NSString in obj. I assume it is checking the obj pointer type.
What do I have to do so that it returns the actual type encoding of the variable that I have passed?
#encode() is a compile-time construct; it only knows about the variable's type, not the class of the object that will be contained in the object at runtime. Any object-typed variable will encode to #.
You will have to use runtime checks, asking the objects for their classes, to accomplish your goal.

Comparing in objective C - Implicit conversion of 'int' to 'id' is disallowed with ARC

I i'm getting the error "Implicit conversion of 'int' to 'id' is disallowed with ARC" at the line marked with "faulty line". I guess it have something to do with that i'm checking for an integer in an array, that contains objects instead of integers.
#import "RandomGenerator.h"
#implementation RandomGenerator
NSMutableArray *drawnNumbers;
-(int) randomNumber:(int)upperNumber {
return arc4random_uniform(upperNumber);
}
-(NSMutableArray*) lotteryNumbers :(int)withMaximumDrawnNumbers :(int)andHighestNumber {
for (int i = 1; i <= withMaximumDrawnNumbers; i++)
{
int drawnNumber = [self randomNumber:andHighestNumber];
if ([drawnNumbers containsObject:drawnNumber]) { //faulty line
//foo
}
}
return drawnNumbers;
}
#end
NSArrays can only contain objective-c objects. So actually the method containsObject: is expecting an object, not an int or any other primitive type.
If you want to store number inside an NSArray you should pack them into NSNumber objects.
NSNumber *someNumber = [NSNumber numberWithInt:3];
In your case, if we assume that drawnNumbers is already an array of NSNumbers, you should change the randomNumber: generation to:
-(NSNumber*) randomNumber:(int)upperNumber {
return [NSNumber numberWithInt:arc4random_uniform(upperNumber)];
}
And then when picking it up on the lotteryNumbers method, you should:
NSNumber *drawnNumber = [self randomNumber:andHighestNumber];
Another note would go for the method you defined for lotteryNumbers. You used a really strange name for it, I think you misunderstood how the method naming works in objective-c. You were probably looking for something more like:
-(NSMutableArray*) lotteryNumbersWithMaximumDrawnNumbers:(int)maximumDrawnNumbers andHighestNumber:(int)highestNumber;
Late edit:
Objective-C now allows a way more compact syntax for creating NSNumbers. You can do it like:
NSNumber *someNumber = #(3);
And your method could be rewritten as:
-(NSNumber*) randomNumber:(int)upperNumber {
return #(arc4random_uniform(upperNumber));
}
You are using an int where an object (presumably NSNumber) is expected. So convert before use:
if ([drawnNumbers containsObject:#( drawnNumber )])

Struct isKindOfClass

How do I find out if a struct is of a specific type? In other words, if I get an object, how do I know that the underluying type is a struct?
+(BOOL)isPrimitive:(id)input
{
return [input isKindOfClass:[NSNumber class] ] || [input isKindOfClass:[NSDate class]] || [input isKindOfClass:[NSString class]]
|| __IS_THIS_A_STRUCT__ (specifically SEL);
}
What should I put in place of IS_THIS_A_STRUCT?
Based on your comments, it looks like you know a property and want to act in a certain way if it returns a struct. If so then you could do something like:
if(!strcmp([[self class]
instanceMethodSignatureForSelector:#selector(propertyName)].methodReturnType,
#encode(SEL)))
#encode returns the type encoding for the named type, which is a C string. instanceMethodSignatureForSelector returns an NSMethodSignature which can nominate the return type of that method as an encoded type.
The two type encodings are not guaranteed to have the same identity but will have the same value. So you can use the C function strcmp to check that they're the same.
You can use NSSelectorFromString if the selector name is not known at compile time.

Checking if an array contains a certain object (iOS)

I need to check if a certain array contains a certain object, and if it does, delete that object. If it hasn't got that object, the funciton is suposed to add it into the array. The problem is that the object is always added because the checking statement always return false.
Here's my current function:
- (void) myFunction:(NSString *)parameter {
if (![myMutableArray containsObject:parameter]) {
[myMutableArray addObject:parameter];
NSLog(#"%# added", parameter);
} else {
[myMutableArray removeObject:parameter];
NSLog(#"%# deleted", parameter);
}
}
containsObject is calling isEqual on each of the object in the arrays. What type of object are you checking for? If it's a custom object, override and implement the method isEqual.
I'm guessing you're trying to check the value of the object, but containsObject is actually calling isEqual which is comparing the reference to the object, and not its actual value.
if (![arrList containsObject:arrObj]) {
// do something
}
containsObject:
First you need to check which type data or object you are adding in this myMutableArray. According to your method you are checking in mutable array for string type that you have passed argument parameter. It may be possible that you are containing int or float array.
There may be issue of type casting in your array.If your is STRING type of data then you can use another method like this.
- (void) myFunction:(NSString *)parameter {
for (int i = 0 ; i < [myMutableArray count ] ; i++) {
if (![[myMutableArray objectAtIndex:i] isEqualToString:parameter]) {
[myMutableArray addObject:parameter];
NSLog(#"%# added", parameter);
}
else{
[myMutableArray removeObject:parameter];
NSLog(#"%# deleted", parameter);
}
}
}
Hope this will help you. If your object is not type of NSString then you need to convert.
You should implement isEqual: in your custom class. By default two objects are only identical if they share the same reference.
Also make sure to initialize your mutable array before using it.
EDIT:
It seems that your array's variable name are most probably mistyped.
myMutableArray
myMutbaleArray
You probably forgot to initialize your NSMutableArray. If not initialized, you are sending addObject messages to a nil object, which has no effect, and the array never contains what you previously added...
Of course, if the array is nil, then the contains check will always return false. According to the Objective-C docs:
If the method returns an object, any pointer type, any integer scalar
of size less than or equal to sizeof(void*), a float, a double, a long
double, or a long long, then a message sent to nil returns 0.
And 0 is false

Resources