endless "for" loop in release configuration of iPhone app - iphone

i have the following code in my application:
NSArray *lstAttributes = [conditionAttribute componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValues = [conditionValue componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValueTypes = [conditionValueType componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator];
if([lstAttributes count] != [lstValues count] ||
[lstAttributes count] != [lstValueTypes count]) return NO;
BOOL bResult = YES;
NSLog(#"attributes amount - %u", [lstAttributes count]);
for(uint i = 0; i < [lstAttributes count]; i ++)
{
NSLog(#"counter: %u", i);
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:i];
condition.conditionValue = [lstValues objectAtIndex:i];
condition.conditionValueType = [lstValueTypes objectAtIndex:i];
bResult &= [self checkCondition:condition forOwner:owner];
FreeObject(&condition);
if(!bResult) break;
}
return bResult;
everything is fine in "debug" configuration. but once i switch it to "release" i face the endless loop. console shows me the following: attributes amount - 2, counter: 0, counter: 1, counter: 1, counter: 1, counter: 1, counter: 1, counter: 1, ...... etc.
i tried to use different "for" and "while" operators, but nothing of that worked correctly. the loop still can't be stopped.
has anyone faced the same problem before?

Since you mention in your comments that it works if you comment out the &= operation, I'd change that line to:
bResult = bResult && [self checkCondition:condition forOwner:owner];
This does a boolean AND, which is what you really intend to do on this line anyway. &= does a bitwise AND, which isn't always equivalent to a boolean AND, since any nonzero value evaluates to true. For example:
BOOL a = 1;
BOOL b = 2;
if (a && b) {
NSLog (#"a && b is true"); // This line will execute
}
if (a & b) {
NSLog (#"a & b is true"); // This line won't execute
}
Since it's common to use object addresses in our conditionals, using the bitwise AND in place of a boolean AND could create bugs. For example, if you return an object address from getCondition:fromOwner:, intending it to mean YES, and that memory address happens to be an even number, then a bitwise AND with YES will evaluate to NO, where a boolean AND would evaluate to YES.
My best guess as to the cause of your specific bug is that the bitwise-AND is somehow causing a buffer overrun, which stomps on your i variable. If that hypothesis is correct, then switching to a boolean AND should fix that too.

workaround for this bug as promised:
at first i tried to use code blocks to enumerate all objects in a list instead of "for" loop.
__block BOOL bResult = YES;
[lstAttributes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:idx];
condition.conditionValue = [lstValues objectAtIndex:idx];
condition.conditionValueType = [lstValueTypes objectAtIndex:idx];
bResult &= [self checkCondition:condition forOwner:owner];
FreeObject(&condition);
if(!bResult) *stop = YES;
}];
but i got a crash on second iteration. "lstValues" and "lstValueTypes" pointers suddenly changed their values and application received EXC_BAD_ACCESS. possibly usage of 3 arrays while enumerating only 1 of them is not a good idea. debugger shows that enumeration is performed on the same thread, but 2 of 3 arrays are corrupted by the moment of 2nd iteration.
so i decided to split up my initial loop into 2 parts:
prepare a list of conditions
check each condition.
first is a usual "for" loop, second - 'enumerateObjectsUsingBlock:" method of NSArray class. so the final code looks like:
NSArray *lstAttributes = [conditionAttribute componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValues = [conditionValue componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValueTypes = [conditionValueType componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator];
if([lstAttributes count] != [lstValues count] ||
[lstAttributes count] != [lstValueTypes count]) return NO;
NSMutableArray *lstConditions = [NSMutableArray new];
for(uint i = 0; i < [lstAttributes count]; i ++)
{
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:i];
condition.conditionValue = [lstValues objectAtIndex:i];
condition.conditionValueType = [lstValueTypes objectAtIndex:i];
[lstConditions addObject:condition];
FreeObject(&condition);
}
__block BOOL bResult = YES;
[lstConditions enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop)
{
if([self checkCondition:[lstConditions objectAtIndex:i] forOwner:owner] == NO)
{
bResult = NO;
*stop = YES;
}
}];
FreeObject(&lstConditions);
return bResult;
this code works.
i would appreciate if anyone can explain the behavior of my initial loop.

Related

Int decimal count in iPhone

I need to know whatever an int64_t has decimals, and how many. This should be placed in if-else-statement. I tried this code, but it causes the app to crash.
NSNumber *numValue = [NSNumber numberWithInt:testAnswer];
NSString *string = [numValue stringValue];
NSArray *stringComps = [string componentsSeparatedByString:#"."];
int64_t numberOfDecimalPlaces = [[stringComps objectAtIndex:1] length];
if (numberOfDecimalPlaces == 0) {
[self doSomething];
} else {
[self doSomethingElse];
}
Your question doesn't make a lot of sense; you are creating the NSNumber object from an int so it will never have decimal places, as an int cannot store them. The reason your code is crashing is that it assumes that the array of components is always at least 2 elements long (as you use objectAtIndex:1).
This is better, though still not that good:
NSString *answer = ...; // From somewhere
NSArray *stringComps = [answer componentsSeparatedByString:#"."];
if ([stringComps count] == 0) {
[self doSomething];
} else if [stringComps count] == 1) {
[self doSomethingElse];
} else {
// Error! More than one period entered
}
This still isn't a very good test as it only tests if a period (.) has been entered, not a valid number.

Returning the number of objectForKey

I have a part of my code that returns the numberOfRowsInSection.
Code
for (NSDictionary *consoleDictionary in [self arrayFromJSON]) {
if ([[consoleDictionary objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%#", consoleDictionary);
}
}
Output
2013-02-03 22:37:08.468 PageControl01[5782:c07] {
console = PlayStation;
game = "007 Legends";
id = 1;
model = "PlayStation 3";
publisher = "Electronic Arts";
}
2013-02-03 22:37:08.478 PageControl01[5782:c07] {
console = PlayStation;
game = "Ace Combat: Assault Horizon";
id = 2;
model = "PlayStation 3";
publisher = Namco;
}
This one is apparently right because it logs all the "PlayStation 3" model. However, this is not what I need. I want to log the number of "PlayStation 3"'s. So I tweak the code a little bit and then this:
for (NSDictionary *consoleDictionary in [self arrayFromJSON]) {
if ([[consoleDictionary objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", [consoleDictionary count]);
}
}
Output
2013-02-03 22:39:43.605 PageControl01[5816:c07] 5
2013-02-03 22:39:43.605 PageControl01[5816:c07] 5
This one is pretty near yet so close. Instead of logging the number 5, it should log the number 2 since there are only 2 "PlayStation 3".
Please help.
You don't need to explicitly loop through the array.
NSIndexSet *is = [array indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[obj objectForKey:#"model"] isEqualToString:#"PlayStation 3"];
}];
int numOfPS3s = is.count;
It is logging the number 5 because there is 5 keys in each of your dictionaries (console, game, id, model, publisher). If instead of logging the [consoleDictionary count] you simply add one to an int counter each time, you would get the expected result in your counter at the end.
You can obtain the number of objects is a much more easier way: [self arrayFromJSON] is an array
Typically:
NSInteger nbPS3 = [[self arrayFromJSON] indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [obj[#"model"] isEqualToString:#"PlayStation 3"];
}].count
Yes, count is always the total count, not the index, not the count of matches. Using indexesOfObjectsPassingTest is the most direct solution for this specific question, but if you're interested in other techniques for iterating through your result set, but also keeping track of not only the object, but also the index, consider these two approaches, too:
for (NSInteger i = 0; i < [self.arrayFromJSON count]; i++) {
if ([[[self.arrayFromJSON objectAtIndex:i] objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", i);
}
}
or
[self.arrayFromJSON enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", idx);
}
}];
Obviously, if you're looking for how many records match, you'd just increment your own counter, rather than logging the index for each match.

How to compare elements in int array to a integer?

I want to compare an array containing int values to a number
example [1,2,30,1,40,200,10,500] < 500 meaning if array contains elements less than 500.
How can i do dis?
Thank alot in advance
I did this bt it say invalid opernd:
if ([placeName count])
{
for (int i =0; i < [placeName count]; i++)
{
NSArray *sortedArray=[placeName sortedArrayUsingFunction:intSort context:NULL];
self.placeName = [NSMutableArray arrayWithArray:sortedArray];
NSMutableArray *tempArray = [sortedArray objectAtIndex:i];
NSDecimalNumber *Distance = [tempArray objectForKey:#"distance"];
NSMutableArray *intDistanse=[Distance intValue];
DLog(#"Distance%d", intDistanse);
NSMutableArray *intDistanse=[Distance intValue];
DLog(#"Distance%d", intDistanse);
for(int j = 0; j < [intDistanse count]; j++)
{
if ( intDistanse[j] < 500 ) //here its says error
{
DLog(#"success");
}
}
}
}
What kind of an array? NSArray or language array?
Easiest way is a loop. Without knowing the type of the array, this is pseudo code.
for(int i = 0; i < arrayCount; i++)
if ( array[i] < 500 )
.... got an element less than 500 ....
The code doesn't really make sense:
that code should be generating a ton of compiler warnings; every warning indicates a bug that should be fixed. Also, try "Build and Analyze".
you are sorting an array every time through the loop; waste of resources and doesn't make sense
everything being typed as NSMutableArray* doesn't make sense; do you really have an array of arrays of arrays?
... calling objectForKey: on anything but an NSDictionary doesn't work
intValue returns an (int), not an NSMutableArray*
if intDistance is an (int), then it should just be ( intDistance < 500 )
Overall, I would suggest you step back and review the Objective-C language guide and a bunch of working examples.
Typically, you loop over each element in the array, checking if each element is less than 500.
For example:
int i;
for (i=0; i < THE_SIZE_OF_THE_ARRAY; ++i)
{
if (array[i] < 500)
{
/* Do something */
}
}
If you want to see whether there is at least one item that matches your condition:
bool found = false;
for(int i = 0; i < sizeof(example)/sizeof(example[0]); ++i)
{
if(example[i] < 500)
{
found = true;
break;
}
}
And if you want to check whether all items match your condition:
bool found = true;
for(int i = 0; i < sizeof(example)/sizeof(example[0]); ++i)
{
if(example[i] >= 500)
{
found = false;
break;
}
}
Did find a solution to it type casting:
NSMutableArray *tempArray = [sortedArray objectAtIndex:i];
//DLog(#"sortedArray%#", sortedArray);8=
NSNumber *DistanceNum = [tempArray objectForKey:#"distance"];
NSLog(#"distance%#:::",DistanceNum);
NSInteger intDistance = (int)[DistanceNum floatValue];
if(intDistance<500)
{
NSLog(#"h:)");
NSString *notifications =#"Yes";
[[AppHelper mDataManager] setObject:notifications forKey:#"notifications"];
NSLog(#"notifications:%#",notifications);
RemindMeViewController *object = [[RemindMeViewController alloc] initWithNibName:#"RemindMeViewController" bundle:nil];
// RemindMeViewController *object=[[RemindMeViewController alloc]initWithNibName];
NSLog(#"notifications set");
[object scheduleNotification];
}
You can do this very easily by just implementing the observer to compare. Following is just an example to implement the custom observer to compare
#implementation NSArray (KVO)
- (void)addObserver:(NSObject *)observer toObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
NSUInteger idx=[indexes firstIndex];
while(idx!=NSNotFound)
{
[[self objectAtIndex:idx] addObserver:observer
forKeyPath:keyPath
options:options
context:context];
idx=[indexes indexGreaterThanIndex:idx];
}
}
- (void)removeObserver:(NSObject *)observer fromObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath
{
NSUInteger idx=[indexes firstIndex];
while(idx!=NSNotFound)
{
[[self objectAtIndex:idx] removeObserver:observer
forKeyPath:keyPath];
idx=[indexes indexGreaterThanIndex:idx];
}
}
-(void)addObserver:(id)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context;
{
if([isa instanceMethodForSelector:_cmd]==[NSArray instanceMethodForSelector:_cmd])
NSRaiseException(NSInvalidArgumentException,self,_cmd,#"not supported for key path %# (observer was %#)", keyPath, observer);
else
[super addObserver:observer
forKeyPath:keyPath
options:options
context:context];
}
-(void)removeObserver:(id)observer forKeyPath:(NSString*)keyPath;
{
if([isa instanceMethodForSelector:_cmd]==[NSArray instanceMethodForSelector:_cmd])
NSRaiseException(NSInvalidArgumentException,self,_cmd,#"not supported for key path %# (observer was %#)", keyPath, observer);
else
[super removeObserver:observer
forKeyPath:keyPath];
}
#end

Change UILabel in loop

I want to change the UILabel after 2 sec in a loop.
But current code change it to last value after loop finished.
- (IBAction) start:(id)sender{
for (int i=0; i<3; i++) {
NSString *tempStr = [[NSString alloc] initWithFormat:#"%s", #" "];
int randomNumber = 1+ arc4random() %(3);
if (randomNumber == 1) {
tempStr = #"Red";
}else if (randomNumber == 2) {
tempStr = #"Blue";
} else {
tempStr = #"Green";
}
NSLog(#"log: %# ", tempStr);
labelsText.text = tempStr;
[tempStr release];
sleep(2);
}
}
Your code updates label to last value only as your function blocks main thread so UI cannot get updated. To solve that problem move your updating code to separate function and call it using performSelector:withObject:afterDelay: method. (or schedule calls using NSTimer)
Possible solution (you will also need to handle the case when user taps your button several times in a row, but that should not be too difficult):
- (IBAction) start:(id)sender{
[self updateLabel];
}
- (void) updateLabel{
static const NSString* allStrings[] = {#"Red", #"Blue", #"Green"};
static int count = 0;
int randomNumber = arc4random()%3;
NSString *tempStr = allStrings[randomNumber];
NSLog(#"log: %# ", tempStr);
labelsText.text = tempStr;
++count;
if (count)
[self performSelector:#selector(updateLabel) withObject:nil afterDelay:2.0];
}
- (IBAction) start:(id)sender{
for (int i=0; i<3; i++) {
int randomNumber = 1+ arc4random() %(3);
NSString *tempStr = #"";
if (randomNumber == 1) {
tempStr = #"Red";
}else if (randomNumber == 2) {
tempStr = #"Blue";
} else {
tempStr = #"Green";
}
[labelsText performSelector:#selector(setText:) withObject:tempStr afterDelay:i * 2]
NSLog(#"log: %# ", tempStr);
}
}
Don't use sleep() to perform actions after a delay, it blocks the whole thread. Use performSelector:withObject:afterDelay: instead. As you are probably running this on the main thread, sleep() will block any UI updates until after the whole loop has run, so the only thing you see is the last update. As a general rule of thumb, assume that the UI doesn't ever get updated until after the app has finished executing your method.
You shouldn't use %s as the format specifier for an NSString, that's the format specifier for a C string. You should use %# instead. In fact, if all you are doing is initialising the string with an NSString literal, there's no need to use initWithFormat at all, you can just use the literal itself.
You've also got big memory problems. At the beginning of the loop, you allocate memory for an instance of NSString that is a single space. You then overwrite the pointer to this memory when you assign to tempStr again, meaning you leak the original allocation of memory. Build and Analyze will find problems like this for you. Then you release tempStr, but as the second assignment to this pointer variable was to an autoreleased NSString, the instance will be released one time too many when the autorelease pool gets drained, which will probably manifest itself as a crash that's impossible to debug a little later in the app.
I'd do something like this:
- (void)showRandomColourAfterDelay {
static NSUInteger count = 0;
switch (arc4random()%3) {
case 0:
labelsText.text = #"Red";
case 1:
labelsText.text = #"Blue";
case 2:
labelsText.text = #"Green";
}
count++;
if (count >= 3) return;
[self performSelector:#selector(showRandomColourAfterDelay) withObject:nil afterDelay:3];
}
In fact, I'd probable use an NSArray to hold the colour strings, but that would involve changes outside of a single method, so I've stuck with your hard-coding approach.

Why doesn't this for loop execute?

I have a picker view controller to select a chemical source and possibly a concentration. If the source doesn't have concentrations, it just presents a single picker. It gets populated by an NSDictionary with source type names as keys and a custom model object I made called Chemical that has four properties, two NSString, one float and one BOOL.
When I trigger this with dictionary that has 2 components, I want to extract the four values from the Chemical that is represented. Note that I populate the picker with values from the first two properties, but not the float or BOOL. I run through the array for the key that's selected in the first component and check the string from the second component against the chemConcentration property from each of the Chemicals in the key/value array. When the chemConcentration matches, I know I have the right Chemical and I can get its properties to send back.
Whew!
The problem is that even though I know I get to the for loop, it seems to get skipped. The NSLog right before it prints, but the one inside doesn't. sourceConstant and sourceIsLiquid stay 0.0 and NO
- (IBAction)selectedSourceButton {
NSLog(#"selectedSourceButton pressed");
NSInteger sourceRow = [picker selectedRowInComponent:kSourceComponent];
NSString *selectedSource = [self.sources objectAtIndex:sourceRow];
NSArray *selectedChemicalGroup = [dictionaryOfSources objectForKey:selectedSource];
NSInteger concentrationRow = [picker selectedRowInComponent:kConcentrationComponent];
NSString *selectedConcentration = [[NSString alloc] init];
float selectedConstant = 0.0;
BOOL selectedIsLiquid = NO;
if (numberOfComponents == 2) {
NSLog(#"numberOfComponents = 2 if/then chosen"); // <-- This prints.
selectedConcentration = [self.concentrations objectAtIndex:concentrationRow];
NSLog(#"begin selectedConcentration for loop. Number of loops = %d", [selectedChemicalGroup count]); // <-- And so does this.
for (int i; i<[selectedChemicalGroup count]; i++) { // <-- But this doesn't seem to fire!
NSLog(#"selectedConcentration = %#, from selectedChemicalGroup = %#", selectedConcentration, [[selectedChemicalGroup objectAtIndex:i] chemConcentration]); // <-- Because this doesn't print.
if ([selectedConcentration isEqualToString:[[selectedChemicalGroup objectAtIndex:i] chemConcentration]]) {
selectedConstant = [[selectedChemicalGroup objectAtIndex:i] chemConstant];
selectedIsLiquid = [[selectedChemicalGroup objectAtIndex:i] chemIsLiquid];
}
}
}
else {
selectedConcentration = #"";
selectedConstant = [[selectedChemicalGroup objectAtIndex:0] chemConstant];
selectedIsLiquid = [[selectedChemicalGroup objectAtIndex:0] chemIsLiquid];
}
NSLog(#"selectedSourceButton source to return = %#, concentration = %#, sourceConstant = %1.7f, isLiquid = %d", selectedSource, selectedConcentration, selectedConstant, selectedIsLiquid);
if ([self.delegate respondsToSelector:#selector (sourcePickerViewController:didSelectSource:andConcentration:andConstant:andIsLiquid:)]) {
[self.delegate sourcePickerViewController:self didSelectSource:selectedSource andConcentration:selectedConcentration andConstant:selectedConstant andIsLiquid:selectedIsLiquid];
}
}
You need to initialize your variable i: for (int i = 0; ...
But there's a better way to do this, using "fast enumeration":
for (MyChemicalGroupClass *group in selectedChemicalGroup) {
if ([selectedConcentration isEqualToString:[group chemConcentration]]) {
...
}
}
Initialize loop count i
for (int i = 0; i<[selectedChemicalGroup count]; i++)
Do the following and you will understand why:
int i;
NSLog(#"%d", i);