Complex code in iOS [closed] - ios

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
here i am stuck with the following chunk of code
self.isFiltered?[self.filteredCategories count]:[self.categories count]
here isFiltered is an bool, filteredCategories is a mutable array, categories is an array. This line return an integer. I don't understand what and how this line is working. Thanks

That's a ternary statement. Say you have an if like this:
if (condition)
var = one thing
else
var = other thing
As a ternary, that would be
var = condition ? one thing : other thing
So, in your case, it will set your variable to the filter count if filtered, or to the full category count if unfiltered.

It's the same to:
int someVariable = 0;
if(self.isFiltered) {
someVariable = [self.filteredCategories count];
} else {
someVariable = [self.categories count];
}
In your code it's just another form.

Well it's not that hard, it's an "advanced" version of an if/else. It says that if the variable isFiltered is set to YES, it will return the number of elements in the filteredCategories array, but if it is set to NO it will return the number of elements in categories.

You can use a normal if-else to simplify things. When in doubt, go with the standard if-else syntax
if(self.isFiltered){
[self.filteredCategories count]
}
else{
[self.categories count]
}

Related

Is there a short way to check array size before going into for loop? [duplicate]

This question already has answers here:
Can't form Range with end < start Check range before doing for loop?
(3 answers)
Closed 6 years ago.
Currently I am using it like this:
if json.count>0{
for i in 0...json.count-1{
}
}
Is there a better and shorter way to safely start a for loop ?
Your code fragment is identical to
for i in 0 ..< json.count { ... }
There's no penalty for executing a loop zero times.
Even better
for element in json { /* do stuff with element */ }
Or if you need the index:
for (i, element) in json.enumerate() { }
Drop the initial condition:
for i in 0..<json.count {
// Do something
}
If the range in the loop is impossible the code will not get executed.
Also, if you don't use i inside the brackets you can replace it with _.

Objective C - better way to write my if statement [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
It's not an actual problem but it is frustrating me..
I was looking for a better way to right an IF statement with multiple values that can be accepted.
For example:
if ([[myJson objectForKey:#"pages"] intValue] == 0 || [[myJson objectForKey:#"pages"] intValue] == 3)
Isn't there any way to write something like:
if ([[myJson objectForKey:#"pages"] intValue] == 0 | 3)
{
}
Thanks !!
No, not really. You could do this:
int pages = [[myJson objectForKey:#"pages"] intValue];
if (pages == 0 || pages == 3)
That's what I would recommend. The code you posted is both less efficient and harder to maintain than the code I show.
In your code you actually invoke the objectForKey and intValue methods twice on the same object.
Plus if at some point in the future you change the key value, or the variable name, you have to make the same edit in 2 places, which is more work and adds another chance to introduce a new copy/paste error.
In addition to the other valid answers, you could use a switch:
int numberOfPages = [[myJson objectForKey:#"pages"] intValue];
switch (numberOfPages) {
case 0:
case 3: {
NSLog(#"Is 0 or 3");
break;
}
default: {
NSLog(#"Is NOT 0 or 3");
break;
}
}
This is a much better method as it is clean and easier to read:
int x = [[myJson objectForKey:#"pages"] intValue];
if (x == 0 || x == 3) {
}
You are highly optimistic there.
If myJson is not an NSDictionary your app will crash.
If myJson[#"pages"] is not an NSNumber then your app will crash.
If myJson[#"pages"] does not exist then intValue will return 0.
If myJson[#"pages"] has a value of 0.9 or 3.7 then intValue will return 0 or 3.
I suggest you add a category to NSDictionary with a method like integerValueForKey: withDefault where you lookup an item, check that it is an NSNumber with an integer value, return that value or return the default value.
As David Rönnqvist pointed out, it is mostly a matter of preference. I would personally go with NSArray containing allowed values. This way you won't clutter your code with unnecessary intValue calls. Also adding another allowed value will only require adding a single value to an array instead of adding another condition inside if statement.
Note that you can use Objective-C literal syntax to make the code more concise.
NSArray *allowedValues = #[#0, #3];
if([allowedValues containsObject:myJson[#"pages"]]) {
}
If [myJson objectForKey:#"pages"] is an NSNumber. You could do this:
if([#[#0, #3] containsObject:[myJson objectForKey:#"pages"]])
And if myJson is an NSDictionary you could even shorten it to:
if([#[#0, #3] containsObject:myJson[#"pages"]])
It's not necessarily optimal, but it does provide you with a lot more flexibility when adding more values to check against, which it sounds like you're looking for as opposed to the fastest code. If you're just checking a few values I'm assuming speed of execution is not an issue.
Side note: this works because NSArray's containsObject: method calls isEqual: on every object. NSNumber's isEqualToNumber: gets called. This will also work with NSString instead of NSNumber if that's what you're working with. Just change the search array to hold NSString objects in that case.

NSNotFound comparison not working [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 years ago.
Improve this question
Simple one.
I'm using indexOfObject to check if a value is already in an array.
If the value isn't in the array indexOfObject returns the constant NSNotFound.
Why does comparison with > 100000 work:
NSInteger indexOfCell = [_selectedCellIndices indexOfObject:cellIndex];
if(indexOfCell > 1000000)
but equality with NSNotFound fail:
NSInteger indexOfCell = [_selectedCellIndices indexOfObject:cellIndex];
if(indexOfCell == NSNotFound)
2147483647 is NSNotFound (also known, under certain circumstanced, as -1). There is something wrong with how you are testing whether your test succeeds or fails, because Cocoa itself is behaving exactly as advertised.
NSInteger indexOfCell = [[NSArray new] indexOfObject:#""];
NSLog(#"%d", indexOfCell); // some number
NSLog(#"%d", NSNotFound); // the same number
NSLog(#"%d", indexOfCell == NSNotFound); // 1, i.e. YES
Try it yourself.
you could use the ContainsObject: method. Might be easier.
indexOfObject is suppose to be NSUInteger (unsigned) not NSInteger. That is probably screwing up your comparison. Try changing the type to NSUInteger and trying again.

Faster way to pull specific data from NSMutableArray?

I have an array full of NSObjects I created called "Questions".
One property of each Question is which level it belongs to.
If the user has chosen to play level 2, I want to get all the Questions that have a .level property of 2. Right now I am looping through all the questions to find the matches, but this is taking ~2 seconds on an iPad 3 / new iPad device. Is there a faster way of dealing with a situation like this?
int goThrough = 0;
do {
Question *currentQuestion = [allQs objectAtIndex:(goThrough)];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
goThrough++;
} while (goThrough < [allQs count]);
Your help is greatly appreciated!
If you have to organize the questions by level on a regular basis, then why not keep all of the questions organized by level. Create a dictionary of arrays. Each key if the level and each array is the list of questions for that level. You do this once and it becomes trivial to get the questions for a level.
I dont have access to a mac at the moment but you can give a try to this:
[allQs enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger index, BOOL *stop) {
Question *currentQuestion = [allQs objectAtIndex:index];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
}
This will use all the cores of your device so it can be twice as fast
You could always use fast enumeration (which, unless you intend on mutating the objects is the fastest way to enumerate a collection). Something like this:
for (Question *thisQuestion in allQs) {
if (thisQuestion.level == levelChosen)
[questions addObject:thisQuestion];
}
}
Since you are not mutating the collection you are iterating through (allQs), this would work fine and be faster than using enumerateObjectsUsingBlock. If you need the index of the array you are iterating through (allQs), then use enumerateObjectsUsingBlock.
I would suggest using the NSArray method enumerateObjectsUsingBlock or one of it's variants. There are even variants that will loop through the array elements concurrently. You'd probably need to use a lock to add elements to your questions array however, since I doubt if NSMutableArray's addObject method is thread-safe.
You should probably test a non-concurrent version against a concurrent version with locking to see which is faster. Which approach is faster would depend on how many of the objects in the allQs array belong to the current level. If only a few belong, the code that asserts a lock won't fire very often, and the benefit of concurrency will outweigh the time penalty of asserting a lock. If most of the objects in the allQs array match the chosen level, code will end up spending a lot of time asserting locks, and the concurrent threads will still waiting for other threads to release a lock.
Modified code might look something like this:
single-threaded version:
[allQs enumerateObjectsUsingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
[questions addObject:currentQuestion];
}
];
Concurrent version:
[allQs enumerateObjectsWithOptions:
NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
#synchronized
{
[questions addObject:currentQuestion];
}
}
];
Actually, now that I think about it, you would likely get still faster performance by first doing a concurrent pass on the array using indexesOfObjectsWithOptions:passingTest. In that pass you'd build an NSIndexSet of all the objects that match the current level. Then in one pass you'd extract those elements into another array:
NSIndexSet *questionIndexes = [allQs indexesOfObjectsWithOptions: NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
return (currentQuestion.level == levelChosen)
}
];
questions = [allQs objectsAtIndexes: questionIndexes];
Another poster pointed out that you are better off breaking up your arrays of questions up by level in advance. If that works with your program flow it's better, since not filtering your array at all will always be faster than the most highly optimized filtering code.
There is a simple answer that seems to be missing. If you want to filter the objects of an array to only have certain ones left, -filteredArrayUsingPredicate: is what you would want. It can be done exceptionally simply.
NSPredicate *p = [NSPredicate predicateWithBlock:^(Question *aQuestion, NSDictionary *bindings){
return (aQuestion.level==2);
}];
NSArray *filteredArray = [originalArray filteredArrayUsingPredicate:p];

iOS format specifies type int but the argument has type int * [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have a method like this:
-(void)processSomeNumeber:(int *)number
{
NSLog(#"show me the number %d", number);
}
But I'm getting this error:
"format specifies type int but the argument has type int *"
Does anyone know why or how I can fix this?
Use:
-(void)processSomeNumeber:(int )number
{
NSLog(#"show me the number %d", number);
}
Or
-(void)processSomeNumeber:(int *)number
{
NSLog(#"show me the number %d", *number);
}

Resources