Does -[NSOrderedSet indexesOfObjectsPassingTest:] really return either an NSIndexSet or NSNotFound? - objective-c

I have this code:
NSIndexSet *indexes = [anOrderedSet indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if([self testObj: obj]) return YES;
else return NO;
}];
if(indexes == NSNotFound) NSLog(#"No objects were found passing the test");
And this causes a warning from Xcode stating "Comparison between pointer and integer ('NSIndexSet *' and 'int')". And I totally agree with Xcode on this one.
However, the return value of the function is of type NSIndexSet* and the Apple documentation (http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSOrderedSet_Class/Reference/Reference.html) states:
Return Value
The index of the corresponding value in the ordered set that passes the test specified by predicate. If no objects in the ordered set pass the test, returns NSNotFound.."
So what's the deal with returning NSNotFound (which is an NSInteger) when the return type of the function is a pointer to NSIndexSet? Is it safe to suppress the warning by changing the comparison to:
if(indexes == (NSIndexSet*) NSNotFound) NSLog(#"No objects were found passing the test");
Because, theoretically, indexes could be a valid returned NSIndexSet pointer that just happens to point to a memory address equal to NSNotFound, correct?

I’m afraid the documentation is wrong.
You should check against an empty NSIndexSet:
if ([indexes count] == 0) NSLog(#"No object were found passing the test");
You should probably open a bug report with Apple.

Apparently the Apple documentation is incorrect.
In my tests, I am finding that if no objects in the ordered set pass the test, then the returned object is an NSIndexSet of count equal to zero.
Thus, the correct test is:
if(indexes.count == 0) NSLog(#"No objects were found passing the test");

Related

null check for dictionary object before call to intvalue still leads to intvalue calls on null object

I get an array of dictionaries back from reading json off a web server and use the following to make sure I got a particular key in the first dictionary in the array before getting its int value:
if([jsonObject[0] objectForKey:#"votes"]!= nil)
{
int votes = [[jsonObject[0] objectForKey:#"votes"] intValue];
[[UserObject userUnique] updateVotes:votes];
}
However, my app still occasionally crashes saying I have called intValue on Null. I have also tried structuring the control statement as
if([jsonObject[0] objectForKey:#"votes"])
but this also leads to the same error/app crashing. My syntax seems in line with accepted answers on SO (Check if key exists in NSDictionary is null or not). Any suggestions for what else/how else I should check the existence of key-value pair for applying intvalue?
Thank you for any advice.
There is a difference between nil and null. nil is not an object: it's a special pointer value. null (as retuned by [NSNull null]) is an object: it's needed because it can be stored in containers like NSDictionary.
NSString *votesString = [jsonObject[0] objectForKey:#"votes"];
if (votesString != nil && votesString != [NSNull null])
{
int votes = [votesString intValue];
[[UserObject userUnique] updateVotes:votes];
}
EDIT: An answer to #SunnysideProductions question
The post you mentioned recommends a way of turning null values into nil values by creating a -safeObjectForKey: method. You are not using -safeObjectForKey:, you are using the default -objectForKey: method.
Be consecutive in your code. Don't run with methods. It would be better add more null- and type-checks in particular in working with json. Let's do it:
if (jsonObject && [jsonObject isKindOfClass:[NSArray class]])
{
NSArray *jsonArray=(NSArray *)jsonObject;
if (jsonArray.count>0)
{
id firstObject=jsonArray[0];
if ([firstObject isKindOfClass:[NSDictionary class]])
{
NSDictionary *jsonDict=(NSDictionary *)firstObject;
id votesNumber=jsonDict[#"votes"];
if (votesNumber && [votesNumber isKindOfClass:[NSNumber class]])
{
int votes=[votesNumber intValue];
[[UserObject userUnique] updateVotes:votes];
}
}
}
}
Now the code is more safe. Does it still crash?
When you call objectForKeyin nullable dictionary, app gets crashed so I fixed this from following way.
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
id object = dictionary;
if (dictionary && (object != [NSNull null])) {
self.name = [dictionary objectForKey:#"name"];
self.age = [dictionary objectForKey:#"age"];
}
return self;
}

Objective C NSPredicate predicateWithBlock removing nil/null values

I am trying to populate an array by taking an existing array and removing nil values from it. The array was populated from a the JSON response of an http call. Sometimes the array has a null value at the end, and the easiest way to remove that value so I wouldn't have to handle it everywhere in my code would be to use NSArray's filteredArrayUsingPredicate: to assign the variable into the instance variable I use throughout my class.
NSArray *respAgencyList = (NSArray*) [JSON valueForKeyPath:#"xml.path.to.data" ];
NSLog(#"data before filter: %#", respAgencyList);
// prints: ( { domain: "foo.com", name:"foobar"}, "<null>" });
if (respAgencyList != nil && respAgencyList.count > 0) {
agencies = [respAgencyList filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSLog(#"Evaluated object is %#", evaluatedObject); //prints <null> for the null value
BOOL ret = evaluatedObject != nil;
return ret;
}]];
}
In the above code the return value is always YES. However, when I put the debugger on and step through it I see:
evaluatedObject = id 0x00000000
Isn't this a null/nil value? What is different about this value compared to nil?
You should also check for NSNull, which can be placed into an NSArray since it is a proper object.
BOOL ret = (evaluatedObject != nil && [evaluatedObject isKindOfClass:[NSNull class]] == NO);
It is impossible for an NSArray to contain a nil element.
Some enumeration methods do hand you nil after the enumeration, as a signal that you've reached the end, but the nil is not in the array — it's just a signal, and you are not expected to do anything serious with it. However, I do not know whether this is one of them.
I suggest that instead of trying to remove nil from the array, which is impossible since nil was never there in the first place, you examine the array directly (log it, look in the debugger, whatever) and assure yourself that what you're trying to do is unnecessary.

How to check if NSString returned by objectForKey is "" objective c

I'm not exactly sure how to check whether a NSString is blank or not, I've got this code...
NSString *imageName = [myItem objectForKey:#"iconName"];
if(imageName == #"")
{
}
And when I do a print on the myItem object, it comes up as..
iconName = "";
At the NSString *imageName line, I noticed in xcode in the console it says
"variable is not NSString"
Which I don't get as iconName is saved and stored on the parse.com database as a NSString.
When I run that code though it doesn't seem to realise that imageName = "";
You should use this code block when comparing strings:
if ([imageName isEqualToString:#""]){
}
You need to use isEqualToString to compare two strings. If you just use == then you are comparing two pointers.
You could also check to see if the object you are receiving is a NSString by:
if ([imageName isKindOfClass:[NSString class]])
Hope this helps.
Although you have a few answers already, here is my take.
First of all, your warning (not error) can be fixed like this:
NSString *imageName = (NSString *)[myItem objectForKey:#"iconName"];
Then, I would check to make sure that the string is not nil and that it is not blank. The easiest way to do this in objective-C is to check the length of the string, since if it nil it will return 0, and if it is empty, it will return 0:
if([imageName length] == 0)
{
// This is an empty string.
}
As #jlehr points out, if there is the possibility that imageName may not actually be stored as a string, then in order to prevent a crash you need to check first. (This may or may not be needed, depending on the logic of your application):
if ([imageName isKindOfClass:[NSString class]]
{
if([imageName length] == 0)
{
// This is an empty string.
}
}
The "variable is not NSString" is probably because objectForKey: return an id.
To should use [imageName isEqualToString:#""].

How to write a valid boolean conditional in objective-c using objectForKey

I'm seeking to understand why the following always hits the ELSE clause. What I can't figure out is that regardless of the actual value ( NSLog shows a 0 or 1) this always hits the else. Any reason why?
The item NSArray is pulled from a JSON object -fyi
BOOL* instock = [item objectForKey:#"itemInStock"];
obj.instock = instock;
NSLog(#"and it was %#", obj.instock);
if (obj.instock == YES) {
//do yes stuff
}else {
//do no stuff
}
Your code here is rather strange. What is the type of obj.instock? Your very first line
BOOL* instock = [item objectForKey:#"itemInStock"];
makes no sense at all. -objectForKey: doesn't return BOOL* values. It returns id. I'm guessing here that you're actually getting an NSNumber * back, and it just happens to work alright because an NSNumber * fits inside of a BOOL * (as they are both pointers). Similarly, obj.instock is likely to be an NSNumber* as well (if it wasn't an object of some sort, your NSLog() would crash).
So, assuming that obj.instock is an NSNumber*, the conditional you want is simply
if ([obj.instock boolValue]) {
// yes
} else {
// no
}
You should also fix this code to not try and claim you have a BOOL* when you don't.
In your NSLog, you are using %#.
%# refers to objects. Contrary to your BOOL* instock which is not an object.
There are ways to fix that.
What data type does your [item objectForKey:#"itemInStock"]; return?
If it returns an NSNumber for example, then you can do:
obj.instock = [[item objectForKey:#"itemInStock"] boolValue];
NSLog(#"and it was %d", obj.instock);
if (obj.instock == YES) {
//do yes stuff
}else {
//do no stuff
}
Again, there are other ways to do that.

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)