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;
}
}
}
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;
}
}
}
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;
}
}
can anyone tell me please why returnSet is returning as nil when there are lowercase characters in 'program'
I have stepped through and the NSLog is definitely picking the variables out but when it addObject: it just doesn't?
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet *returnSet = [[NSMutableSet alloc]init];
if ([program isKindOfClass:[NSArray class]]) {
[program enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isKindOfClass:[NSString class]]) {
if ([obj rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]].location != NSNotFound) {
NSLog(#"Variable: %#", obj);
[returnSet addObject:obj];
}
}
}];
}
return returnSet;
}
The posted code has no bug. It cannot return a value of nil.
Your error is elsewhere.
I'm guessing that your problem is an ARC memory management problem. The code you posted returns a non-owning reference to the set it creates. Unless you save it to a strong instance variable, it will be deallocated.
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.
}