Why does this conditional cause an error? - objective-c

I have a method that contains a few conditionals. The first conditional works fine and does not cause any problems. However, the second one causes the app to crash.
- (void)didReceiveGaiaGattResponse:(CSRGaiaGattCommand *)command
{
GaiaCommandType cmdType = [command getCommandId];
NSData *requestPayload = [command getPayload];
uint8_t success = 0;
NSLog(#"cmdType: %li", (long)cmdType);
[requestPayload getBytes:&success range:NSMakeRange(0, sizeof(uint8_t))];
if (cmdType == GaiaCommand_GetCurrentBatteryLevel && requestPayload.length > 1)
{
uint16_t value = 0;
[requestPayload getBytes:&value range:NSMakeRange(1, sizeof(uint16_t))];
NSInteger battery = CFSwapInt16BigToHost(value);
[self sendEventWithName:someDEVICE_BATTERY_CHANGED body:#{#"batteryLevel":[NSNumber numberWithInteger:battery]}];
return;
}
else if (cmdType == GaiaCommand_GET_FBC && requestPayload.length > 1)
{
uint16_t value = 0;
[requestPayload getBytes:&value range:NSMakeRange(1, sizeof(uint16_t))];
NSInteger feedbackCancellationMode = CFSwapInt16BigToHost(value);
[self sendEventWithName:FEEDBACK_CANCELLATION_MODE body:#{#"feedbackCancellationMode": [NSNumber numberWithInt:feedbackCancellationMode]}];
return;
}
//do more stuff
}
The conditional
if (cmdType == GaiaCommand_GetCurrentBatteryLevel &&
requestPayload.length > 1)
works without problems.
However, the conditional
else if (cmdType == GaiaCommand_GET_FBC && requestPayload.length > 1)
causes the following warning in xcode
Implicit conversion loses integer precision: 'NSInteger' (aka 'long')
to 'int'
In addition, I also saw the error message in the debugger
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[_NSInlineData getBytes:range:]: range {1, 2} exceeds
data length 2'

Consider what this is telling you:
Terminating app due to uncaught exception 'NSRangeException', reason:
'-[_NSInlineData getBytes:range:]: range {1, 2} exceeds data length 2'
Your data object is 2 bytes in length. The first byte, at position 0, is (according to your code) the success value. That leaves one more byte at position 1 to handle. But your code is attempt to copy 2 bytes out of it — that's the range {1, 2} in the message; a range starting at position 1 and with a length of 2. You're reading past the end of the data.
You have to check that the data has enough data to satisfy the -getBytes:... call you're attempting to make. You may also need to correct your assumptions about how large the cancellation mode value in the buffer is supposed to be, because it's apparently smaller than you expect. Your code assumes it's a uint16_t (2 bytes) but there's only one byte left in the data.

[NSNumber numberWithInt:feedbackCancellationMode]}]
should be
[NSNumber numberWithInteger: feedbackCancellationMode]}]

Related

EXC_BAD_ACCESS when reading 9 characters, but works with less than 9

I'm developing a console app in Objective-C. I've got it working, but when manually testing edge cases, I found a strange behavior that I can't explain.
Basically, I've set up scanf() in a loop, and when the user types invalid info, it prints an "invalid option" message. Then, if the input is less than 9 characters long, it goes through the loop again as intended. But, if the input is 9 characters or longer, it gives a EXC_BAD_ACCESS error on a certain line.(This error doesn't happen if I comment out said line.)I can't figure out any reason why 8 vs 9 characters being read would cause this error. Any ideas?
Below are the two methods that I figure are relevant, with a comment on the line throwing the error. If you think other referenced code may be causing this, let me know and I'll add that code.
-(void)startMenu {
printf("\nGAME OPTIONS\n| WinningScore = %d (w) | Name = %s (n) | Back (b) |\n",
_options.winningScore, [_options.name UTF8String]);
}
-(void)start {
char selectedOption;
char w = 'w';
char n = 'n';
char b = 'b';
while(YES) {
[self startMenu]; // This line gets the EXC_BAD_ACCESS error
// if the user puts in 9 or more characters.
// If it is commented out, then no error is thrown.
scanf("%s", &selectedOption);
if(selectedOption == w) {
[self setWinningScore];
} else if(selectedOption == n) {
[self setName];
} else if(selectedOption == b) {
break;
} else {
printf("'%s' is not a valid option.\n", &selectedOption);
}
}
}
It this was C (and the post is tagged C) I'd suggest:
char selectedOption;
....
scanf(" %c", &selectedOption);
...
printf("'%c' is not a valid option.\n", selectedOption);
The failure showing up after a 9 charterer input is serendipity. scanf("%s", &selectedOption); is certainly wrong for reading a single character. Any input starts causing problems. Use the matching format specifier and variable.
[Edit]
A C-like solution.
If more than 1 char is desire for input, use the idea put forth by #Devolus. Example:
char selectedOption[10];
if (fgets(selectedOption, sizeof selectedOption, stdin) == NULL)
Handle_EOForIOerror();
// Get rid of potential trailing \n if desired.
size_t len = strlen(selectedOption);
if (len > 0 && selectedOption[len-1] == '\n') selectedOption[--len] = '\0';
You should use fgets instead of scanf here, as you can limit the number of characters in the buffer.
scanf is potentially unsafe because the buffer can be exceeded.

array index blows up obj c

I am writing an iPhone app in Xcode 4.6.3.
I don't really know how to ask this question, but during operation, an array index goes from 0, which is hardcoded in a method, to 58911 for some reason. The chain is:
- (void)syncInitialState
{
[self syncState:0]; //value starts hardcoded here
}
then:
- (void)syncState:(int)index
{
self.state = [macrostate getState:index];
[self syncState];
}
in Macrostate:
- (NPState *)getState:(int)index
{
int *singleArray = {&index};
NSPointerArray *pointerArray = [self subsetFromIntArray:singleArray];
return (__bridge NPState *)[pointerArray pointerAtIndex:0]; //only one object
}
finally:
- (NSPointerArray *)subsetFromIntArray:(int *)intArray
{
NSPointerArray *subset = [NSPointerArray strongObjectsPointerArray];
for (int i=0; i<sizeof(intArray); i++) {
[subset addPointer:[pointerArrayOfStates pointerAtIndex:intArray[i]]]; //fails
}
return subset;
}
Obviously it fails because 58911 is outside the bounds of the pointer array. I have never seen this before. Thanks for reading.
It might help to know that the error is: * Terminating app due to uncaught exception 'NSRangeException', reason: ' -[NSConcretePointerArray pointerAtIndex:]: attempt to access pointer at index 58911 beyond bounds 4' ** First throw call stack: (0x1caa012 0x10e7e7e 0x1ca9deb 0xb1c1ab 0xd8ae 0xd7a5 0x47c6 0x485e 0x567f 0x5d95 0x2d3f 0x10d1c7 0x10d232 0x5c3d5 0x5c76f 0x5c905 0x65917 0x29c5 0x29157 0x29747 0x2a94b 0x3bcb5 0x3cbeb 0x2e698 0x1c05df9 0x1c05ad0 0x1c1fbf5 0x1c1f962 0x1c50bb6 0x1c4ff44 0x1c4fe1b 0x2a17a 0x2bffc 0x26fd 0x2625) libc++abi.dylib: terminate called throwing an exception
You can't use sizeof to get the length of the int array. You need to add a "length" parameter to your subsetFromIntArray: method and use that instead of sizeof.

Conditional Appears False... Why is it True?

Here's a couple lines of code... currentPage is an NSInteger, imageViewHolder is an NSMutableArray.
NSLog(#"currentPage: %d, imageViewHolderCount: %d", currentPage, [imageViewHolder count]);
if(currentPage < [imageViewHolder count] - 1)
{
NSLog(#"%d < %d - 1", currentPage, [imageViewHolder count]);
tempView = [imageViewHolder objectAtIndex:currentPage + 1];
NSLog(#"doesn't get here, crashed."); //this doesn't get logged because of crash.
//do other stuff here
}
I'm crashing at the "tempView = ..." line due to index out of bounds. This is the output I'm getting:
currentPage: 0, imageViewHolderCount: 0
0 < 0 - 1
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array'
*** First throw call stack:
(0x339bd88f 0x324af259 0x339069db 0x48f1b 0x48c1b 0x35603a85 0x35603409 0x35602c57 0x35716b0f 0x3560424d 0x4abfb 0x324ab175 0x43f77 0x45b91 0x479a7 0x36bff 0x285ed 0x35604ecb 0x32935933 0x33991a33 0x33991699 0x3399026f 0x339134a5 0x3391336d 0x32e91439 0x35608e7d 0xfa01 0xf9c0)
I'm sure I'm missing something obvious... why is my if statement being evaluated as true? I'm fairly certain that 0 is in fact greater than -1 :)
The problem is that count is an unsigned type. When you subtract 1 from an unsigned zero, you end up with a really large positive number1 rather than -1.
Replace the condition with an equivalent expression that uses addition to avoid the problem:
if((currentPage+1) < [imageViewHolder count]) ...
1 The value of -1 in 2-s complement representation used in most modern computers to represent negatives is a binary number composed entirely of ones. When this number gets re-interpreted as an unsigned 32-bit value, it becomes 2^32-1.
Yes, but 0 is much less than MAX_INT. You're working with unsigned integers (I'd have to double-check the standard on whether this is defined as MAX_INT, or undefined behavior that happens to be MAX_INT, but in either case it's never what you mean). I suspect if you look at your compiler output, there's a warning to this effect (never allow any warnings in ObjC code).
See also unsigned long 0 < -1?
this is a long shot ... but try writing your condition like this:
(currentPage < ([imageViewHolder count] - 1))
maybe the problem is that the compiler is grouping it like this
((currentPage < [imageViewHolder count]) - 1)
(0 < 0) returns false (0), then substracts 1, and stays at -1, which is different from 0, so it returns true

printf(), fprintf(), wprintf() and NSlog() won't print on XCode

I'm doing a small app for evaluating and analyzing transfer functions. As boring as the subject might seem to some, I want it to at least look extra cool and pro and awesome etc... So:
Step 1: Gimme teh coefficients! [A bunch of numbers]
Step 2: I'll write the polynomial with its superscripts. [The bunch of numbers in a string]
So, I write a little C parser to just print the polynomial with a decent format, for that I require a wchar_t string that I concatenate on the fly. After the string is complete I quickly try printing it on the console to check everything is ok and keep going. Easy right? Welp, I ain't that lucky...
wchar_t *polynomial_description( double *polyArray, char size, char var ){
wchar_t *descriptionString, temp[100];
int len, counter = 0;
SUPERSCRIPT superscript;
descriptionString = (wchar_t *) malloc(sizeof(wchar_t) * 2);
descriptionString[0] = '\0';
while( counter < size ){
superscript = polynomial_utilities_superscript( size - counter );
len = swprintf(temp, 100, L"%2.2f%c%c +", polyArray[counter], var, superscript);
printf("temp size: %d\n", len);
descriptionString = (wchar_t *) realloc(descriptionString, sizeof(wchar_t) * (wcslen(descriptionString) + len + 1) );
wcscat(descriptionString, temp);
counter++;
}
//fflush(stdout); //Already tried this
len = wprintf(L"%ls\n", descriptionString);
len = printf("%ls**\n", descriptionString);
len = fprintf(stdout, "%ls*\n", descriptionString);
len = printf("FFS!! Print something!");
return descriptionString;
}
During the run we can see temp size: 8 printed the expected number of times ONLY WHILE DEBUGGING, if I run the program I get an arbitrary number of prints each run. But after that, as the title states, wprintf, printf and fprintf don't print anything, yet len does change its size after each call.
In the caller function, (application:(UIApplication *)application didFinishLaunchingWithOptions:, while testing) I put an NSLog to print the return string, and I dont get ANYTHING not even the Log part.
What's happening? I'm at a complete loss.
Im on XCode 4.2 by the way.
What's the return value from printf/wprintf in the case where you think it's not printing anything? It should be returning either -1 in the case of a failure or 1 or more, since if successful, it should always print at least the newline character after the description string.
If it's returning 1 or more, is the newline getting printed? Have you tried piping the output of your program to a hex dumper such as hexdump -C or xxd(1)?
If it's returning -1, what is the value of errno?
If it turns out that printf is failing with the error EILSEQ, then what's quite likely happening is that your string contains some non-ASCII characters in it, since those cause wcstombs(3) to fail in the default C locale. In that case, the solution is to use setlocale(3) to switch into a UTF-8 locale when your program starts up:
int main(int argc, char **argv)
{
// Run "locale -a" in the Terminal to get a list of all valid locales
setlocale(LC_ALL, "en_US.UTF-8");
...
}

How to define end in objective C

OSStatus SetupBuffers(BG_FileInfo *inFileInfo)
{
int numBuffersToQueue = kNumberBuffers;
UInt32 maxPacketSize;
UInt32 size = sizeof(maxPacketSize);
// we need to calculate how many packets we read at a time, and how big a buffer we need
// we base this on the size of the packets in the file and an approximate duration for each buffer
// first check to see what the max size of a packet is - if it is bigger
// than our allocation default size, that needs to become larger
OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
AssertNoError("Error getting packet upper bound size", end);
bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 || inFileInfo- >mFileFormat.mFramesPerPacket == 0);
CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 0.5/*seconds*/, &mBufferByteSize, &mNumPacketsToRead);
// if the file is smaller than the capacity of all the buffer queues, always load it at once
if ((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize)
inFileInfo->mLoadAtOnce = true;
if (inFileInfo->mLoadAtOnce)
{
UInt64 theFileNumPackets;
size = sizeof(UInt64);
result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);
AssertNoError("Error getting packet count for file", end);***>>>>this is where xcode says undefined<<<<***
mNumPacketsToRead = (UInt32)theFileNumPackets;
mBufferByteSize = inFileInfo->mFileDataSize;
numBuffersToQueue = 1;
}
//Here is the exact error
label 'end' used but not defined
I have that error twice
If you look at the SoundEngine.cpp source that the snippet comes from, you'll see it's defined on the very next line:
end:
return result;
It's a label that execution jumps to when there's an error.
Uhm, the only place I can find AssertNoError is here in Technical Note TN2113. And it has a completely different format. AssertNoError(theError, "couldn't unregister the ABL"); Where is AssertNoError defined?
User #Jeremy P mentions this document as well.