1

I always thoughts that sending a message to a nil pointer would normally return 0. So the same would apply to properties. But then this code snippet seems to contradict my assumptions

NSArray *testArray;
NSInteger i = 0;
NSLog(@"testArray.count-1=%ld", testArray.count-1);
NSLog(@"i<testArray.count-1=%d", i<testArray.count-1);

The output is

2013-05-22 11:10:24.009 LoopTest[45413:303] testArray.count-1=-1
2013-05-22 11:10:24.009 LoopTest[45413:303] i<testArray.count-1=1

While the first line makes sense, the second does not. What am I missing?

EDIT: thanks to @JoachimIsaksson and @Monolo for pointing (pun intended) me to the right direction. The problem is actually signed v. unsigned and the following code shows it:

NSArray *testArray;
NSInteger i = 0;
unsigned ucount = 0;
int count = 0;

NSLog(@"testArray.count-1=%ld", testArray.count-1);
NSLog(@"i<testArray.count-1=%d", i<testArray.count-1);
NSLog(@"i<ucount-1=%d", i<ucount-1);
NSLog(@"i<count-1=%d", i<count-1);

And the output is

2013-05-22 11:26:14.443 LoopTest[45496:303] testArray.count-1=-1
2013-05-22 11:26:14.444 LoopTest[45496:303] i<testArray.count-1=1
2013-05-22 11:26:14.444 LoopTest[45496:303] i<ucount-1=1
2013-05-22 11:26:14.445 LoopTest[45496:303] i<count-1=0
Wolfy
  • 1,445
  • 1
  • 14
  • 28

2 Answers2

5

Return values from a nil receiver

When accessing properties or reading return values from a nil object, you will get their default value. This is usually 0 for any numeric return type. So getting the count of an array when it is nilled will yield 0. Other possible values from nil receivers are NO for BOOLs, and nil for object return types. Returned structures have all members initialized to zero.

Array counts are unsigned...

Now, you need to remember that an array count returns an NSUInteger. With this being unsigned, if you subtract from 0, you will underflow, and get a very large number.

Why does NSLog print -1 for the first statement then?

It's because you have used @"%ld", which specifies a long signed integer. As such, the value is interpreted as signed, and this results in -1. The type of the variable actually states it's an unsigned long, whose format specifier should be @"lu". When using this, it results in 18446744073709551615 for me (could vary for you, depending on platform).

How does this affect the second NSLog statement?

Taking into account what's going on in the first statement, the second statement may now make more sense. You may have thought it was comparing 0 < -1, which results in NO, and shouldn't produce a result of 1. What's actually being compared, is 0 < 18446744073709551615, which results in YES. This is why you're getting a result of 1.

It all boils down to using the incorrect format identifier in NSLog, which caused confusion on how to interpret the value.

WDUK
  • 18,870
  • 3
  • 64
  • 72
  • 1
    but count is not a property ;) – Bastian May 22 '13 at 15:43
  • 2
    @Bastian, well the dot-notation in Objective-C is just syntactic sugar, after all ;) – Wolfy May 22 '13 at 15:47
  • 2
    @Wolfy, I know.. and technically you could write things like `NSArray *myarray = NSArray.alloc.init;` but you shouldn't ;) – Bastian May 22 '13 at 15:53
  • "Other possible values from nil receivers are ..." and don't forget to mention that for struct types the return value is undefined (in fact, it leaves the variable unaltered) – newacct May 22 '13 at 22:35
  • *"Struct types do not have this defined behaviour"* seems not entirely true. See http://stackoverflow.com/a/2696909/1402846, and also https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW22 which says *"Returned structures have all members initialized to zero"*. – Pang Dec 07 '16 at 09:20
3

It is always some version of nothing: nil, zero, NO.

The return type of count is NSUInteger as also pointed out by Joachim Isaksson in the comments. testArray.count-1 is expected to be -1, which in binary is encoded ...111111 (the exact number of bits depends on the platform, and whether the code is compiled as 32-bit or 64-bit). However, since the expression is unsigned it will be interpreted as a very large number instead - in fact the largest possible number that can be represented as an unsigned integer.

This very large number, when compared with the variable i is much bigger. Hence the output.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • Which is why messing about with types is a bad idea. – Abizern May 22 '13 at 15:18
  • @Wolfy 0 as unsigned integer - 1 is (most likely) overflowing to MAXUINT. – Joachim Isaksson May 22 '13 at 15:23
  • @Joachim, exactly, `count` returns an NSUInteger, so testArray.count-1 produces a very large number. Hence the apparently mysterious result. I'll update the answer. – Monolo May 22 '13 at 15:24
  • @JoachimIsaksson and Monolo, yes! That's the problem, signed v. unsigned. – Wolfy May 22 '13 at 15:30
  • Minor remark: The result of `testArray.count - 1 = - 1` will also be "all ones" in binary if count is a signed integer. The point is whether the *comparison* is done as signed or unsigned number. – Martin R May 22 '13 at 15:32
  • count is technically not a property but a function (it's older then the properties) and therefor should not be used with property dot syntax. – Bastian May 22 '13 at 15:41
  • "It is always some version of nothing:" Not always. Not for struct return type – newacct May 22 '13 at 22:36
  • @newacct Thanks for the reminder, I always forget that. A bit of googling brought [this post](http://vgable.com/blog/2008/05/31/messages-to-nowhere/), which looks well-informed, and points out that the exact behaviour is more complex than that, and even processor dependent (or, I'd guess, ABI dependent to be precise). – Monolo May 23 '13 at 10:25