Objective-C Bool Literals and Macros - objective-c

Disclaimer: limited knowledge of Objective-C (passing curiosity).
if (#YES)
vs
if (YES)
What's the difference?
From what I understand, #YES is a bool object literal, and YES is a macro that expands to 1? Is this correct? If so, why use #YES instead of YES or vice versa?

#YES is a shortcut for [NSNumber numberWithBool:YES], so this:
if (#YES)
actually means
if ([NSNumber numberWithBool:YES] != nil)
Note, that it doesn't matter what number (or bool value) that is. You just test that it's a valid object. Even #NO will evaulate to YES if you test it like that, because it's a non-nil NSNumber instance:
if (#NO) NSLog(#"test");
Output:
2013-12-07 21:02:49.828 MyApp[37512:70b] test
Long story short: Don't use #YES or #NO like that, they will not behave as you would expect.

The # symbol is a recent addition to Objective-C, which changes literals into their object representation in the form of an NSNumber instance.
This is especially useful if you need to store literals in arrays and dictionaries, which can only store objects (type id).
The way you use it, there is not need to create an object for the literal, so you should just use the literal directly.
The # symbol has always been used for denoting NSString objects, and only 2-3 years ago this syntax was extended for literals and expressions.

#YES, #1, #1.5...these are all literal syntax that are the equivalent of an NSNumber ie:
[NSNumber numberWithBool:YES], [NSNumber numberWithInt:1], [NSNumber numberWithDouble:1.5].
Using this means you are creating an NSNumber object.
YES is simply the BOOL type, it's a primitive type in Obj-C, not an object like NSNumber. The BOOL type is used in Objective-C to hold true or false values. This is what you would normally use. You would only use #YES in cases where you need to hold the primitives in something that only accepts objects, perhaps if you wanted to hold them in an NSArray for example.

Related

What is the difference between 4 and #4?

I just started learning this, and the tutorial started off by using the # symbol before all number literals, and string literals, and I thought "Okay, Objective-C uses the # symbol before literals," but then next thing you know they used some numbers without the # sign and I was at a total loss.
(I'm saying 'number' because I'm afraid to say int since Objective-C / C has so many types of number.)
What's the difference?
Objective C is an extension of C, so it uses regular literals in places where C uses literals - i.e. for providing values of primitive types int, long, etc.
In addition, Objective C has support for NSNumber class of the Cocoa framework. Objects of this class are used to wrap primitive values for uses where an object is required - for example, to be put in a collection. Cocoa collections do not accept values of primitive types, so you need to provide an object wrapper before placing a numeric value into a collection.
Objective C did not have support for making literals of type NSNumber, so you needed to wrap literals manually, like this:
[NSNumber numberWithInt:4]
This is too much typing, especially when you need to define multiple such values to put into a collection. That is why Objective C added an alternative syntax for creating NSNumbers - the one with the # sign. So when you write #4, that's the same as writing [NSNumber numberWithInt:4], but is a lot less typing. For example, an initialization that looked like this in the old syntax
NSArray *oneTwoThree = [NSArray arrayWithObjects: [NSNumber numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3], nil];
now looks like
NSArray *oneTwoThree = #[#1, #2, #3];
which is a lot more readable.
Note: the second code snippet uses the new syntax for array initialization as well.
There is a difference between a int type and an NSNumber object. An int is a data type used to store integers. You create an int like this: int i = 4;
An NSNumber is an Objective-C object that can store any type of number, and int, double, float, short, long, etc. This is created like NSNumber *num = #4;, which actually becomes NSNumber *num = [NSNumber numberWithInt: 4];
The main reason to use #4 instead of 4 is in NSArrays and NSDictionary's. You can only store objects, not types like int or float, in these objects. So if you want an array of numbers, it would have to be like [NSArray arrayWithObjects: #1, #2, #3, #4, nil];
The # has other uses as you have realized. Just like how a number becomes an object by adding the # to it, a C-String "Hello World" becomes an Objective-C object (NSString) with #"Hello World". A C-Array [1,2,3,4] becomes an Obj-C object (NSArray) with #[#1,#2,#3,#4]. Same with dictionaries: #{key,value}
If you want to learn more about literals, you can use this link, or this link.
One is a number (an int, a scalar). The other is an NSNumber (an Objective-C object; on this notation, see http://clang.llvm.org/docs/ObjectiveCLiterals.html).

Objective-C NSNumber numberWithLongLong creates integer

When I attempt to create an NSNumber using the numberWithLongLong with a number greater than -2 and less than 13 it returns a number that is casted as an (int).
I see this if I look at the Xcode debugger after stepping over my line.
NSNumber* numberA = [NSNumber numberWithLongLong:-2]; //Debugger shows as (long)-2
NSNumber* numberB = [NSNumber numberWithLongLong:-1]; //Debugger shows as (int)-1
NSNumber* numberC = [NSNumber numberWithLongLong:12]; //Debugger shows as (int)12
NSNumber* numberD = [NSNumber numberWithLongLong:13]; //Debugger shows as (long)13
To put my problem in context, I am using a long long value for an epoch date that I will end up serializing using BSON and sending across the wire to a webservice. The webservice requires the date to be a java Long.
Thanks in advance
You have discovered that NSNumber (actually, its CFNumber counterpart) has a cache for integers between -1 and 12 inclusive. Take a look at the CFNumberCreate function in CFNumber.c to see how it works.
It looks like you can force it not to use the cache by passing your own allocator to CFNumberCreate. You'll need to look at the CFAllocator documentation.
But note that the CFNumberCreate manual says this:
The theType parameter is not necessarily preserved when creating a new CFNumber object.
So even if you bypass the cache, you might not get back an object whose objCType is q (which means long long). It looks like the current implementation will return q but that could change in a future version.
You are allowed to write your own NSNumber subclass if you need to guarantee that objCType returns q. Read “Subclassing Notes” in the NSNumber Class Reference.
You can use your webservice without concern.
NSNumber wraps a numeric value (of primitive type) as an object. How NSNumber stores that value is not really your concern (but there is a method to find it out), it is an opaque type. However NSNumber does maintain an internal record of the type used to create it so its compare: method can follow C rules for comparison between values of different types precisely.
For integral types the integral value you get back will be exactly the same, in the mathematical sense, as the one you created the NSNumber with. You can create an NSNumber with a short and read its value back as a long long, and the mathematical value will be the same even though the representation is different.
So you can store your integral date value as an NSNumber and when you read it back as a long long you will get the right value. No need to be concerned how NSNumber represents it internally, and indeed that could potentially change in the future.
(At least one implementation of NSNumber can store values as 128-bit integers, which helps ensure correct semantics for signed and unsigned integers. Also I stressed integral types as with the vagaries of real numbers talking about mathematical exactness is somewhat moot.)
Wait. I think I know what your asking. Try it this way:
NSNumber* numberA = [NSNumber numberWithLongLong:-2LL];
NSNumber* numberB = [NSNumber numberWithLongLong:-1LL];
NSNumber* numberC = [NSNumber numberWithLongLong:12LL];
NSNumber* numberD = [NSNumber numberWithLongLong:13LL];
BTW: it won't matter what the type of the constant is, it will be coerced into a long long when passed to [NSNumber numberWithLongLong:]
UPDATE
Based on #robmayoff's answer, I don't think NSNumber is reliable for your. How are you packing your BSON? is there a way to use NSValue instead of NSNumber?

Errors in trying to cast to NSInteger * and NSMutableArray *

I'm a newbie in obj c. So I have a simple question.
I have a matrix of NSInteger values. It is called "curBoard". I want to update value at (x,y) coordinates with value "curStep". I have an arror "operand of type void where arithmetic..."
What am I doing wrong ?
[curBoard replaceObjectAtIndex:x withObject:(NSMutableArray *)[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:(NSInteger *)[NSNumber numberWithInt:curStep]]];
Update:
NSMutableArray *board;
board = [NSMutableArray new];
for(NSInteger i = 0; i<boardSize; i++) {
NSMutableArray *row = [NSMutableArray new];
for(NSInteger j = 0; j < boardSize; j++)
[row addObject:(NSInteger *)[NSNumber numberWithInt:0]];
[board addObject:row];
}
This withObject:(NSInteger *)[NSNumber numberWithInt:curStep]] part is what causing an issue. If you are storing as NSNumber objects, you should just use:
... withObject:[NSNumber numberWithInt:curStep]]
Edit:
From the code posted above, you should add it as:
[row addObject:[NSNumber numberWithInt:0]];
NSInteger is not of pointer type and you should use NSNumber itself to add to array.
Objective-C is basically just a bunch of object syntax strapped to C. The overall effect is something like strapping a jetpack to a horse: sometimes the two parts don't really work together very well. In this case, you're trying to go faster by telling the horse to giddy up, when you should really be opening up the throttle.
NSMutableArray is part of the jetpack—it's an Objective-C object and is only equipped to handle arrays of Objective-C objects. But NSInteger is part of the horse—it's a primitive C integer type, not a real object.*
I know NSInteger is capitalized like a class and has an NS prefix like a class, but it's really a creature of C. You can confirm this yourself—type Cmd-O in Xcode and type "NSInteger" into the Open Quickly dialog that pops up, and you'll be able to jump to its definition. In my current Mac project, that's typedef long NSInteger;; long is one of the primitive C types.
NSNumber exists to bridge the two. It's an object specifically designed to hold the C numeric types inside it. Since NSNumber is an object, NSMutableArray and other Objective-C things can deal with it.
But you can't just cast between NSNumber and NSInteger. NSNumber holds an NSInteger inside it, but that doesn't mean it's actually an NSInteger itself. If you put a sandwich in a plastic bag, you can't eat the bag.
Instead, you have to use NSNumber's +numberWithInteger: method to construct an NSNumber, and -integerValue to get the integer back out of it. (+numberWithInt: and -intValue will usually work, but they may behave differently with very large values, depending on whether your app is running on a 32-bit or 64-bit processor.) Actually, nowadays you can say [NSNumber numberWithInteger:foo] as #(foo) instead, which is a lot shorter.**
So when you add a number, you should be saying:
[row addObject:#(0)];
And when you later want that number back, you'll want to say something like:
n = [[row objectAtIndex:y] integerValue];
The -replaceObjectAtIndex:withObject: error is a different story. -replaceObjectAtIndex:withObject: doesn't return anything at all, so you can't use it as an argument. Luckily, you don't need to in this case. -replaceObjectAtIndex:withObject: doesn't create a new array; it alters the array that's already inside [curBoard objectAtIndex:x], so you don't need to do anything to curBoard. Instead, you can just write:
[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:#(curStep)];
* You actually used NSInteger *, which is slightly different. The * means "pointer to", so NSInteger * is a pointer to a primitive integer. This is sort of like NSNumber *, a pointer to an NSNumber object, so the compiler allows you to cast it.
Note that casting a pointer doesn't convert the data at the other end of the pointer; it just makes the compiler interpret the same data in a different way. If you actually tried to use the NSInteger * pointer to get data, you would either get garbage data or (for reasons too large to fit within this margin) crash.
In this case, though, once you've Jedi mind-tricked the compiler into thinking that value is a pointer to an NSInteger, you try to pass it to to -addObject:. -addObject: expects a pointer to an object, so the compiler balks at passing a pointer to an NSInteger instead.
** This syntax will work as long as you're using the iOS 6 SDK Xcode 4.4 or later, even if you actually run the app on an older iOS. It will also automatically use the right +numberWithWhatever: method for you, so you don't have to worry about picking the best one. When you're using a numeric literal like 0, the parentheses are optional, but they're required when you use a variable or constant. Of course, you can still do it the wordy way if you want, but there's little point nowadays.

Objective-C for Dummies: How do I get a value out of NSDictionary?

so I'm having the most difficult of time pulling values out of an NSDictionary. Right now I just have a dictionary that is populated from a JSON call and it only contains a key named 'Success' with a value of 0 or 1.
How do I do a conditional on that value to check if its 0 or 1? I've tried a bunch of things, but I'm not getting anywhere. Here's my current code:
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber:1]
I'm getting passing argument 1 of 'isEqualToNumber:' makes pointer from integer without a cast' as a warning, and the app crashes when it hits that line anyway.
And a subquestion, what's the difference between objectForKey and valueForKey? Which one should I use by default?
Anyway, this noob in Objective-C would truly appreciate some help on this. Thanks in advance!
Since dictionaries contain Objective-C objects, an entry containing a number is an NSNumber instance. NSNumber provides a convenience method, -intValue, for extracting its underlying int value:
if ([[jsonDictionary objectForKey:#"Success"] intValue] == 1) { … }
Note that NSNumber has other convenience methods for extracting its underlying value as other C data types.
In most cases, you should use -objectForKey: instead of -valueForKey:. The former is the canonical method to obtain an entry in the dictionary and is declared in NSDictionary. The latter is declared in NSObject and is used in Key-Value Coding contexts, where the key must be a valid KVC key, and there’s additional processing — for instance, if you’re using -valueForKey: in a dictionary with a key that starts with #, that character is stripped from the key and [super valueForKey:key] is called.
The number 1 is not an object pointer. Use an NSNumber instance instead if you want to use a number in an NSDictionary.
[[jsonDictionary objectForKey:#"Success"]
isEqualToNumber:[NSNumber numberWithInteger:1]]
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber: [NSNumber numberWithInt:1]]
Number and Value Programming Topics: Using Numbers
NSNumber: What is the point ?
You can get the value of dictionary in different ways like checking
the value first.
Solution 1: Using simple if statement.
int value = 0;
if ([[jsonDictionary objectForKey:#"Success"]intValue]==1){
value = [[jsonDictionary objectForKey:#"Success"]intValue];
}
Solution 2: Using ternary operator
value = ([[jsonDictionary objectForKey:#"Success"]intValue]==1) ? 1:0;

How do you wrap up a BOOL for KVC in Cocoa/Obj-C?

I'm using KVC to iterating through a few views. Having trouble setting BOOL properties:
[self setValue:YES forKeyPath:[NSString stringWithFormat:#"myView%d.userInteractionEnabled", n]];
I get: warning: passing argument 1 of 'setValue:forKeyPath:' makes pointer from integer without a cast.
There is no [NSValue valueWithBool:YES] or similar that I can find.
What to do?
The compiler is generating a warning because the first argument of -setValue:forKeyPath: expects and object. YES is not an object.
The answer is right there in "NSValue.h":
[NSNumber numberWithBool: aBool]
Later versions of Xcode allow you to use the literal syntax:
[foo setValue:#YES forKey:#"bar"]
You also can use Literal expression
#(YES)
For More Info
llvm Objective-C Literals