Why is ([str length] - 2) larger than 0 when str is nil? - objective-c

When the follow code is run it goes inside the for loop and run NSLog. Why does this happen?
NSString *aString = nil;
for (int i=0; i<([aString length]-2); i++) {
NSLog(#"Inside loop.");
}
As i figure [aString length]-2 results in -2 and that's less then 0?

To be more precise, -[NSString length] returns an unsigned integer, so subtracting two from zero (remember, calling any method on nil gives you zero) doesn't give you -2, it gives you a very, very large number. Cast it to an int (or an NSInteger) to get the results you want.

You're asking the for loop to run if i<-2; as [NSString length] returns an unsigned integer, this value will wrap round to max int - 2.
You're passing a message to a nil object there too: [aString length]. I'm not sure this is defined behaviour, and may return a strange range of values, though I suspect it may be clever enough to return 0. My commentators say that this suspicion is true.

Related

Objective C: 18 is not greater than -1?

I have a very peculiar problem here. I'm building a spider to grab hyperlinks from a webpage and put them into a table and I'm using NSRanges to parse the HTML document, but I've run into an issue.
I have the following line of code:
NSLog(#"%lu", [dataString rangeOfString:#"contents.asp?year1" options:0 range:NSMakeRange(index, dataString.length - index)].length);
This echoes 18 to the log, as it should, but if I put that into a boolean statement, seeing if that length is greater than -1:
NSLog(#"%d", ([dataString rangeOfString:#"contents.asp?year1" options:0 range:NSMakeRange(index, dataString.length - index)].length > -1));
This echoes 0, or false. 18 is clearly greater than -1, so what's the problem? If I switch it to < -1, it returns true. Does this have something to do with type-casting the unsigned long?
Here's the definition of NSRange:
typedef struct _NSRange {
NSUInteger location;
NSUInteger length;
} NSRange;
Notice that both fields are of type NSUInteger, an unsigned type. In fact, NSUInteger is unsigned long.
Since there is no wider integer type than unsigned long, the compiler promotes -1 to unsigned. I can't recall whether this is undefined behavior, but on iOS and Mac OS X it has the effect of treating the 2's complement bit pattern of -1 as an unsigned integer. That bit pattern, as an unsigned integer, is the maximum unsigned integer value.
Thus your comparison can never be true.
If you think -1 means "not found", you are mistaken. The correct way to check whether rangeOfString:options:range: failed to find the target is to check whether the location of the returned range is NSNotFound:
NSUInteger location = [dataString rangeOfString:#"contents.asp?year1"
options:0 range:NSMakeRange(index, dataString.length - index)].location
BOOL foundIt = location != NSNotFound;

int or NSInteger to NSUInteger for index

I'm trying to (in Xcode5) use the 'removeObjectAtIndex' for an 'MutableArray' which takes an NSUInteger but the variable I'm using is an integer so I casted with (NSUInteger *) but I get a warning that says cast to 'NSUInteger *' (aka unsigned long *) from smaller integer type. I have not casted the variable 'second' in the code to keep the warning there but it is also an integer
-(void) moveObjectAtIndex:(NSUInteger *)oldIndex toNewIndex:(NSUInteger *)newIndex{
id *member = [self.array objectAtIndex:*oldIndex];
[self.array removeObjectAtIndex:*oldIndex];
if ((NSInteger)newIndex >=(self.array.count)) {
newIndex--; //i casted newIndex because I got a warning about ordered comparison of NSUInteger with NSInteger (I'm not sure if this is best solution)
}
[self.array insertObject:member atIndex: *newIndex];
}
-(void)moveObjectInArray:(NSMutableArray *)array{
[array moveObjectAtIndex:(NSUInteger *) first toNewIndex:second];
}
Your use of pointers is all wonky. id* should just be id and NSUInteger* should just be NSUInteger — you don't want a pointer to a pointer to an object or a pointer to an integer here.
What's problem for you of using just NSUInteger rather than NSUInteger* ?
-(void) moveObjectAtIndex:(NSUInteger)oldIndex toNewIndex:(NSUInteger)newIndex{
id member = [self.array objectAtIndex:oldIndex]; //Here is id, id* is wrong
[self.array removeObjectAtIndex:oldIndex];
newIndex = newIndex >= self.array.count ? : self.array.count - 1; // Here should be self.array.count - 1, not newIndex-1
newIndex = MIN(_cloudListArray.count, newIndex);
[self.array insertObject:member atIndex:newIndex];
}
In the following statement
if ((NSInteger)newIndex >=(self.array.count))
you are typecasting pointer to NSInteger. It should be
if ((NSInteger)*newIndex >=(self.array.count))
Still, you should be careful with typecasting and be wary of their consequences due to any signed/unsigned conversion or data loss.
Also, in this line
[array moveObjectAtIndex:(NSUInteger *) first toNewIndex:second];
type of first should be NSUInteger * or of same size. Please note that long is 64-bit in 64-bit environment and typecasting as smaller pointer type to larger pointer type will yield undefined behaviour. Same applies to second. One solution is that use temporary variable and then copy back the result.

How to compare size of string in Objective-C?

I have an NSString in Objective-C that I want to find the size of and then compare it to some number; however, I'm not sure what the proper way to do this is?
What I have so far is a property called display which is a UILabel * and what I'm thinking of doing is the following:
NSUInteger size = [self.display.text length];
However, now I want to compare this size with a 0. But I'm not sure what the proper way to do this? I've thought of doing the following, but for some reason this doesn't seem right:
if(size == 1) { doSomething};
Any help? Thanks
Yes, that is the proper way to do it. If you want to check if there is a string whose length is greater than 0 i.e. not just #"", compare it with zero like this
NSUInteger size = [self.display.text length];
if(size>0) {
//do something
};
You can just use -[NSString length]. This just returns the number of characters in the string. NSString abstracts the encoding of its underlying buffer, but length should abtract that from you! This is different from std::string.size() or strlen, which will return the byte count, independent of encoding.
Thus, you may write 0 == str.length to determine if the string is empty, or 1 == str.length to determine if it is one character (encoding-safe).

Converting NSNumber to NSString is not realy a string

I have got a problem with converting an NSNumber value to an NSString
MyPowerOnOrNot is an NSNumber witch can only return a 1 or 0
and myString is an NSString..
myString = [NSString stringWithFormat:#"%d", [myPowerOnOrNot stringValue]];
NSLog(#"%#",myString);
if(myString == #"1") {
[tablearrayPOWERSTATUS addObject:[NSString stringWithFormat:#"%#",#"ON"]];
}
else if(myString == #"0") {
[tablearrayPOWERSTATUS addObject:[NSString stringWithFormat:#"%#",#"OFF"]];
}
What is wrong with this?
The NSLog shows 0 or 1 in the console as a string but I can't check it if it is 1 or 0 in an if statement?
If doesn't jump into the statements when it actually should.. I really don't understand why this doesn't works..
Any help would be very nice!
A couple of problems
myString = [NSString stringWithFormat:#"%d", [myPowerOnOrNot stringValue]];
-stringValue sent to an NSNumber gives you a reference to a string. The format specifier %d is for the C int type. What would happen in this case is that myString would contain the address of the NSString returned by [myPowerOnOrNot stringValue]. Or, on 64 bit, it would return half of that address. You could actually use [myPowerOnOrNot stringValue] directly and avoid the relatively expensive -stringWithFormat:
if(myString == #"1")
myString and #"1" are not necessarily the same object. Your condition only checks that the references are identical. In general with Objective-C you should use -isEqual: for equality of objects, but as we know these are strings, you can use -isEqualToString:
if ([[myPowerOnOrNot stringValue] isEqualToString: #"1"])
Or even better, do a numeric comparison of your NSNumber converted to an int.
if ([myPowerOnOrNot intValue] == 1)
Finally if myPowerOnOrNot is not supposed to have any value other than 0 or 1, consider having a catchall else that asserts or throws an exception just in case myPowerOnOrNot accidentally gets set wrong by a bug.
"myString " is a reference to a string, not the value of the string itself.
The == operator will compare the reference to your string literal and so never return true.
Instead use
if( [myString isEqualToString:#"1"] )
This will compare the value of myString to "1"
In Objective C; you can't compare strings for equality using the == operator.
What you want to do here is as follows:
[tablearrayPOWERSTATUS addObject:([myPowerOnOrNot integerValue]?#"ON":#"OFF"])];
Compact, fast, delicious.

Cocoa: while(index >= 0) continuing, even though index == -1

I've got the following code:
-(void)removeFilesWithPathIndices:(NSIndexSet*)indexSet {
NSInteger index = [indexSet firstIndex];
while(index >= 0) {
[self removeFileWithPathIndex:index];
index = [indexSet indexGreaterThanIndex:index];
}
}
Which should iterate through an NSIndexSet. However, the while loop does not stop, even though index = -1 according to
NSLog(#"%d", index);
Anyone able to solve this mistery for me? :)
Don't assume NSInteger to be an int. In fact it's not. So, %d in
NSLog(#"%d", index);
is deceiving you if you compile in 64 bit mode. See NSInteger documentation.
You shouldn't have even assumed that indexGreaterThanIndex to return -1.
The documentation explicitly says it returns NSNotFound. By following the documentation, you eventually find NSNotFound is NSIntegerMax, the maximal possible value in an NSInteger. When NSInteger is long and casted into an int, his becomes -1. But it's an implementation detail, and you shouldn't rely on that. That's why they defined a symbolic constant NSNotFound to start with.
You should have followed what the documentation says, and write a code like
while(index != NSNotFound) {
[self removeFileWithPathIndex:index];
index = [indexSet indexGreaterThanIndex:index];
}
In a sense you shouldn't have even declared
NSInteger index;
because the indices in Foundation are all NSUInteger.
indexGreatherThanIndex: returns NSNotFound when there's nothing greater than the specified index. Apple Documentation
NSNotFound is defined as NSIntegerMax, which is >= 0. Apple Documentation
Your NSLog statement is just giving you a deceptive result. Instead of:
while(index >= 0)
Use:
while(index != NSNotFound)