How would you stop fast enumeration once you have gotten what your looking for.
In a for loop I know you just set the counter number to like a thousand or something.
Example:
for (int i=0;i<10;i++){
if (random requirement){
random code
i=1000;
}
}
so without converting the fast enumeration into a forward loop type thing (by comparing i to the [array count] how can you stop a fast enumeration in the process?
from the docs
for (NSString *element in array) {
if ([element isEqualToString:#"three"]) {
break;
}
}
if you want to end enumeration when a certain index is reached, block-based enumeration might be better, as it give you the index while enumerating:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//…
if(idx == 1000)
*stop = YES;
}];
for (id object in collection) {
if (condition_met) {
break;
}
}
Couldn't you just use a break statement?
for (int x in /*your array*/){
if (random requirement){
random code
break;
}
}
Just adding that for nested loops, a break on an inner loop breaks just that loop. Outer loops will continue. If you want to break out completely you could do so like this:
BOOL flag = NO;
for (NSArray *array in arrayOfArrays) {
for (Thing *thing in array) {
if (someCondition) {
// maybe do something here
flag = YES;
break;
}
}
if (flag) {
break;
}
}
Related
I've been using enumerateObjectsUsingBlock: a lot lately for my fast-enumeration needs, and I'm having a hard time understanding the usage of BOOL *stop in the enumeration block.
The NSArray class reference states
stop: A reference to a Boolean value. The block can set the value to YES to
stop further processing of the array. The stop argument is an out-only
argument. You should only ever set this Boolean to YES within the
Block.
So then of course I can add the following in my block to stop the enumeration:
if (idx == [myArray indexOfObject:[myArray lastObject]]) {
*stop = YES;
}
From what I've been able to tell, not explicitly setting *stop to YES doesn't have any negative side effects. The enumeration seems to automatically stop itself at the end of the array. So is using *stop really necessary in a block?
The stop argument to the Block allows you to stop the enumeration prematurely. It's the equivalent of break from a normal for loop. You can ignore it if you want to go through every object in the array.
for( id obj in arr ){
if( [obj isContagious] ){
break; // Stop enumerating
}
if( ![obj isKindOfClass:[Perefrigia class]] ){
continue; // Skip this object
}
[obj immanetizeTheEschaton];
}
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if( [obj isContagious] ){
*stop = YES; // Stop enumerating
return;
}
if( ![obj isKindOfClass:[Perefrigia class]] ){
return; // Skip this object
}
[obj immanentizeTheEschaton];
}];
That is an out parameter because it is a reference to a variable from the calling scope. It needs to be set inside your Block, but read inside of enumerateObjectsUsingBlock:, the same way NSErrors are commonly passed back to your code from framework calls.
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
// N.B: This is probably not how this method is actually implemented!
// It is just to demonstrate how the out parameter operates!
NSUInteger idx = 0;
for( id obj in self ){
BOOL stop = NO;
block(obj, idx++, &stop);
if( stop ){
break;
}
}
}
i am trying to remove duplicate objects from array.
NSMutableArray* filterResults = [[NSMutableArray alloc] init];
BOOL copy;
// remove duplicate
if (![arrSelectedVehicle count] == 0)
{
for (Vehicles *a1 in arrSelectedVehicle) {
copy = YES;
for (Vehicles *a2 in filterResults) {
if ([a1.Vehicle_id isEqualToString:a2.Vehicle_id]) {
copy = NO;
[arrSelectedVehicle removeObjectIdenticalTo:a2];
break;
}
}
if (copy) {
[filterResults addObject:a1];
}
}
}
i am adding two object which is already their in the array
you cannot modify an array when you are enumerating it. you can do the following:
NSMutableArray* filterResults = [[NSMutableArray alloc] init];
BOOL copy;
// remove duplicate
if (![arrSelectedVehicle count] == 0)
{
NSArray* arraycopy = [arrSelectedVehicle copy];
for (Vehicles *a1 in arraycopy) {
copy = YES;
for (Vehicles *a2 in filterResults) {
if ([a1.Vehicle_id isEqualToString:a2.Vehicle_id]) {
copy = NO;
[arrSelectedVehicle removeObjectIdenticalTo:a2];
break;
}
}
if (copy) {
[filterResults addObject:a1];
}
}
[arraycopy release];
}
You can't modify an array while using it with fast enumeration. That's what the error is telling you. You need to change the loops
for (NSUInteger i = 0; i < arrSelectedVehicle.count; i++) {
Vehicles *a1 = arrSelectedVehicle[i];
copy = YES;
for (NSUInteger j = 0; j < filterResults.count; j++) {
Vehicles *a2 = filterResults[j];
if ([a1.Vehicle_id isEqualToString:a2.Vehicle_id]) {
copy = NO;
[arrSelectedVehicle removeObjectIdenticalTo:a2];
break;
}
}
if (copy) {
[filterResults addObject:a1];
}
}
There are several problems with your code. Anyway, the easiest way to remove duplicates, if you don't care about the order of the elements, is using an NSSet, because an NSSet doesn't allow duplicates:
NSArray *uniqueObjects = [[NSSet setWithArray:arrSelectedVehicle] allObjects];
The error message says it pretty much: you can't modify the contents of a mutable collection while you're using fast enumeration on it (because that's erroneous). You have to make a mutable copy of it, and remove the duplicates from that copy.
you can not use a for loop and then add or remove objects from the array that you are iterating through (arrSelectedVehicle). Instead try building up a new array with the objects that are OK. At the end of the loop you could assign that array back to arrSelectedVehicle.
I've been using enumerateObjectsUsingBlock: a lot lately for my fast-enumeration needs, and I'm having a hard time understanding the usage of BOOL *stop in the enumeration block.
The NSArray class reference states
stop: A reference to a Boolean value. The block can set the value to YES to
stop further processing of the array. The stop argument is an out-only
argument. You should only ever set this Boolean to YES within the
Block.
So then of course I can add the following in my block to stop the enumeration:
if (idx == [myArray indexOfObject:[myArray lastObject]]) {
*stop = YES;
}
From what I've been able to tell, not explicitly setting *stop to YES doesn't have any negative side effects. The enumeration seems to automatically stop itself at the end of the array. So is using *stop really necessary in a block?
The stop argument to the Block allows you to stop the enumeration prematurely. It's the equivalent of break from a normal for loop. You can ignore it if you want to go through every object in the array.
for( id obj in arr ){
if( [obj isContagious] ){
break; // Stop enumerating
}
if( ![obj isKindOfClass:[Perefrigia class]] ){
continue; // Skip this object
}
[obj immanetizeTheEschaton];
}
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if( [obj isContagious] ){
*stop = YES; // Stop enumerating
return;
}
if( ![obj isKindOfClass:[Perefrigia class]] ){
return; // Skip this object
}
[obj immanentizeTheEschaton];
}];
That is an out parameter because it is a reference to a variable from the calling scope. It needs to be set inside your Block, but read inside of enumerateObjectsUsingBlock:, the same way NSErrors are commonly passed back to your code from framework calls.
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
// N.B: This is probably not how this method is actually implemented!
// It is just to demonstrate how the out parameter operates!
NSUInteger idx = 0;
for( id obj in self ){
BOOL stop = NO;
block(obj, idx++, &stop);
if( stop ){
break;
}
}
}
I have to delete an object of an array every now and then, when I do it I get this error.
Collection < CALayerArray: 0xc4f3b20> was mutated while being enumerated
The error appears on this method, which is the accesor of the Array:
- (NSArray *)occupantsArray
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupantsArray;
}
else
{
__block NSArray *result;
dispatch_sync(moduleQueue, ^{ //ON THIS LINE
result = [occupantsArray copy];
});
return [result autorelease];
}
}
As you can see Im taking care of not returning the original array but a copy, but it still crashes.
Also the method where Im deleting elements of the array is this.
- (void)eraseJIDFromArray:(NSString*)jid{
dispatch_block_t block = ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i = 0;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
[pool drain];
};
if (dispatch_get_current_queue() == moduleQueue)
block();
else
dispatch_async(moduleQueue, block);
}
The array can have upto 200 elements, so it can take some time to go through all of them, but I'm setting queues, dont know what else I can do.
Any ideas?
Thanks.
This code:
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
is probably causing your problems. You shouldn't be removing elements while enumerating the array. The way you avoid this is by using an NSMutableIndexSet:
NSMutableIndexSet *indexes = [NSMutableIndexSet set]; // assuming NSInteger i;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[indexes addIndex:i];
}
i++;
}
[occupantsArray removeObjectsAtIndexes:indexes];
I have a bunch of UITextFields where a user must enter a number (the keyboard is the number pad) before pressing a submit button. When the submit button is pressed, a "check" method is called that wants to check if the data is valid... aka not empty. Now this would be easy if it was just one textField but I have 14. I want a simple if-statement that checks if one of the fields or all of the fields are empty....
I have the follow UITextFields declared: number1, number2, number3 etc etc
and I have the follow strings declared which can take the value of the UITextField.text... they are declared: temp1, temp2, temp3 etc...
how would I go about making a pseudocode if statement like the one below?
Thanks
if (!valid)
{
NSLog (#"not valid)
}
else
{
//proceed to next method
}
I am supposing UITextField variable as *tfield so here is the solution
if (tfield.text.length > 0 || tfield.text != nil || ![tfield.text isEqual:#""])
{
//do your work
}
else
{
//through error
}
or you could just call
[myTextField hasText];
which will return NO if the field is empty.
I think something like this is what you want. It's just using isEqualToString: to check if the string is empty.
NSString *temp1 = number1.text;
NSString *temp2 = number2.text;
... ///< temp3, etc
if ([temp1 isEqualToString:#""]) {
// temp1 not valid
} else if ([temp2 isEqualToString:#""]) {
// temp2 not valid
} ... {
// temp3, etc
} else {
// Valid
}
You may want to trim whitespace characters when grabbing temp1 so that #" " would also be blank. For that, take a look at NSString's stringByTrimmingCharactersInSet: method like so:
NSString *temp1 = [number1.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Update:
If you want to do it with an array you could do something like:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
[array addObject:number1.text];
[array addObject:number2.text];
... ///< number3, etc
BOOL ok = YES;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:#""]) {
ok = NO;
*stop = YES;
}
}];
if (ok) {
// Valid
} else {
// Not valid
}
Something like this would iterate through your UITextFields:
[self.view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UITextField class]]) {
// do your checks
}
}];
you can put like this
if(![temp1 isEqualToString:#""] && ![temp2 isEqualToString:#""] ... ![temp14 isEqualToString:#""])
{
NSLog (#" valid);
}
else
{NSLog (#"not valid);
}
In Swift,
You can use this block of code.
if textField.text?.isEmpty {
//Return true if the textfield is empty or false if it's not empty.
}