Multi-character character sequence in 3rd party library - objective-c

I'm using a 3rd party library for an iOS project I work on, and I'm down to one warning left in the project, namely on this line of code
[NSNumber numberWithUnsignedLongLong:'oaut']
And the warning is
Multi-character character constant
I suck at C, so I don't know how to fix this, but I'm sure the fix is relatively easy. Help?
EDIT: More context.
#implementation MPOAuthCredentialConcreteStore (KeychainAdditions)
- (void)addToKeychainUsingName:(NSString *)inName andValue:(NSString *)inValue {
NSString *serverName = [self.baseURL host];
NSString *securityDomain = [self.authenticationURL host];
// NSString *itemID = [NSString stringWithFormat:#"%#.oauth.%#", [[NSBundle mainBundle] bundleIdentifier], inName];
NSDictionary *searchDictionary = nil;
NSDictionary *keychainItemAttributeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassInternetPassword, kSecClass,
securityDomain, kSecAttrSecurityDomain,
serverName, kSecAttrServer,
inName, kSecAttrAccount,
kSecAttrAuthenticationTypeDefault, kSecAttrAuthenticationType,
[NSNumber numberWithUnsignedLongLong:"oaut"], kSecAttrType,
[inValue dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
nil];
if ([self findValueFromKeychainUsingName:inName returningItem:&searchDictionary]) {
NSMutableDictionary *updateDictionary = [keychainItemAttributeDictionary mutableCopy];
[updateDictionary removeObjectForKey:(id)kSecClass];
SecItemUpdate((CFDictionaryRef)keychainItemAttributeDictionary, (CFDictionaryRef)updateDictionary);
[updateDictionary release];
} else {
OSStatus success = SecItemAdd( (CFDictionaryRef)keychainItemAttributeDictionary, NULL);
if (success == errSecNotAvailable) {
[NSException raise:#"Keychain Not Available" format:#"Keychain Access Not Currently Available"];
} else if (success == errSecDuplicateItem) {
[NSException raise:#"Keychain duplicate item exception" format:#"Item already exists for %#", keychainItemAttributeDictionary];
}
}
}
EDIT 2: They were attempting to meet the requirements of this by creating that NSNumber:
#constant kSecAttrType Specifies a dictionary key whose value is the item's
type attribute. You use this key to set or get a value of type
CFNumberRef that represents the item's type. This number is the
unsigned integer representation of a four-character code (e.g.,
'aTyp').

In C and Obj-C the single-quote ' is used only for single-character constants. You need to use the double-quote: "
Like so:
[NSNumber numberWithUnsignedLongLong:"oaut"]
That covers the warning, but there's also a semantic issue here. Although a single character constant, such as 'o', can be treated as an integer (and can be promoted to an unsigned long long), a "string" (char * or char []) cannot, which means you can't use "oaut" as an argument to numberWithUnsignedLongLong:
Update:
I guess the four-character code is supposed to be treated as an integer, i.e., the 8 bits of each char put in place as if they together were a 32-bit int:
char code[] = "oaut";
uint32_t code_as_int = code[0] | (code[1] << 8) | (code[2] << 16) | (code[3] << 24);
[NSNumber numberWithUnsignedLongLong:code_as_int]
although I'm not sure which endianness would be expected here, nor why this is calling for an unsigned long long, unless just to be certain there are enough bits.
Rudy's comment, now that I think of it, is correct -- multi-character constants are allowed by some compilers for exactly this purpose (it is "implementation-defined" behavior).

'oaut' (single quotes) is a character, so the compiler tries to interpret it as a multi-byte character but can't make any sense of it. That explains the error message.
I guess that if you gave a proper string, like #"oaut", you'd get another error message, since numberWithUnsignedLongLong: expects an unsigned long long, not a string or a character. Are you trying to pass a variable with the name "oaut"? If so, use
[NSNumber numberWithUnsignedLongLong: oaut];
If not, then please explain what "oaut" is.
Edit
'oaut' may actually be the original value. There are/were multi-character character constants in C. Using a (4 byte) char, used as int and promoted to unsigned long long would then be possible. This must be old code. It seems such code was accepted by CodeWarrior.
Assuming that really a multi-char char const was meant, 'oaut' looks like a "magic number" and this value was chosen because it is the beginning of "oauth". I guess it should either be value 0x6F617574 or 0x7475616F.

#Josh Caswell 's answer is partially right, the simplest and "official" solution is:
[NSNumber numberWithUnsignedInt:'oaut']
unsigned int's length is 32-bit in both 32-bit and 64-bit cpu, there's a practical example from Apple: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html

Related

How to replace a char in an an char array? Xcode

i got the following char array in Objective-C (Xcode):
char *incomeMessage;
NSString *str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
incomeMessage = [str UTF8String];
NSLog(#"%c", incomeMessage[0]);
NSLog(#"%c", incomeMessage[1]);
NSLog(#"%c", incomeMessage[2]);
NSLog(#"%c", incomeMessage[3]);
NSLog(#"%c", incomeMessage[4]);
NSLog(#"%c", incomeMessage[5]);
For example I get some results like this in console:
"3
2
6
1
8
4"
Now i want to replace the char in incomeMessage[2] by 4:
incomeMessage[2] = '4';
But then it gives me the error:
EXC_BAD_ACCESS
Do you have an idea, how to solve the problem?
According to the reference documentation, UTF8String returns a read-only (const char*) reference to the string data.
The reference material goes on to note:
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the C
string if it needs to be stored outside of the memory context in which
you use this property.
So I'd suggest following their advice and creating a copy of the array and then performing your modifications against that.
For example: http://ideone.com/mhjwZW
You might have better luck with something like:
NSString* str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
char* incomeMessage = malloc([str lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
strcpy(incomeMessage, [str UTF8String]);
//now you can change things
incomeMessage[2] = '4';
//do this when you're done
free(incomeMessage);
Although, is there any particular reason why you want to use a C-string/character array as opposed to an NSMutableString? I think you might find replaceCharactersInRange:withString: a better approach generally. See also: stringByReplacingCharactersInRange:withString:.
i got the following char array in Objective-C (Xcode)
You don't, you know. All you have is a pointer. You have not set aside any actual memory; there is no array there.
incomeMessage = [str UTF8String];
All you've done in that line is repoint the pointer incomeMessage at your string's UTF8String. A string's UTF8String is immutable. Note this passage in the docs:
you should copy the C string if it needs to be stored outside of the memory context in which you use this property.
So basically, if you want to write into an array of char, your first task should be to make an array of char.

NSString (or NSArray or something) to variable parameter list of C (char *) strings

Is there any easy way to convert an Objective-C holding class of NSStrings into parameters for a function accepting a variable list of char *? Specifically I have a function like:
-(void)someFunction:(NSSomething *) var
that I want to forward to a C function like
void someCFunction(char * var, ...)
Is there an easy way to go about this?
No, you can only do what you want if the number of arguments you're passing is known at compile time. If you just want to convert a single string, use the -UTF8String message:
// Example with two strings
NSString *str1 = ...;
NSString *str2 = ...;
someCFunction([str1 UTF8String], [str2 UTF8String]); // etc.
But if the number of strings will vary at runtime, you'll need to use a different API, if one is available. For example, if there's an API that took an array of strings, you could convert the Objective-C array into a C array:
// This function takes a variable number of strings. Note: in C/Objective-C
// (but not in C++/Objective-C++), it's not legal to convert 'char **' to
// 'char *const *', so you may sometimes need a cast to call this function
void someCFunction(const char *const *stringArray, int numStrings)
{
...
}
...
// Convert Objective-C array to C array
NSArray *objCArray = ...;
int numStrings = [objCArray count];
char **cStrArray = malloc(numStrings * sizeof(char*));
for (int i = 0; i < count; i++)
cStrArray[i] = [[objCArray objectAtIndex:i] UTF8String];
// Call the function; see comment above for note on cast
someCFunction((const char *const *)cStrArray, numStrings);
// Don't leak memory
free(cStrArray);
This would do the trick:
NSString *string = #"testing string"
const char * p1=[string UTF8String];
char * p2;
p2 = const_cast<char *>(p1);
Yes, this can be done, and is explained here:
How to create a NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?
And here:
http://www.cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
With modifications for ARC here:
How to create a NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?
Also, variable arguments are not statically or strongly typed, as the other poster seems to be suggesting. In fact, there is no clear indication in the callee of how many arguments you really have. Determining the number of arguments generally breaks down into having to either specify the number by an count parameter, using a null terminator, or inferring it from a format string a la (s)print* . This is frankly why the C (s)print* family of functions has been the source of many errors, now made much much safer by the XCode / Clang / GCC compiler that now warns.
As an aside, you can approach statically typed variable arguments in C++ by creating a template method that accepts an array of an unspecified size. This is generally considered bad form though as the compiler generates separate instances for each size of array seen by by the compiler (template bloat).

Map char to int in Objective-C

I have a need to map char values to int values in Objective-C. I know NSDictionary is out because it deals with reference types, and these are values. The map will be used while iterating through an NSString. Each character in the string will be converted to an integer value. All the integers will be summed together.
Using NSDictionary seems like a bad fit because of all the type coercion I'd have to do. (Converting values types, char and int, to reference types.)
I figure I'll have to drop down to C to do this, but my experience with C libraries is very limited.
Is there something most C developers use that will map char values to int values?
Edit for clarification
The C# equivalent would be a Dictionary<char,int>.
In pseudocode, I'd like to the following:
for (int i = 0; i < [string length]; i++) {
char current = [string characterAtIndex:i];
int score = map[current]; // <- I want map without boxing
// do something with score
}
Char to int?
char aChar = 'a';
int foo = (int) aChar;
Done. No need for a hash or anything else. Even if you wanted to map char -> NSNumber, an array of 256 char's (char being a signed 8 bit type) is very little overhead.
(Unless I entirely misparsed your question -- are you asking for (char*)? ... i.e. C strings? Show some code.).
If I understand correctly, you want to store chars and ints in a dictionary, as keys and values. However, NSDictionary only accepts objects. The solution? Wrap the chars and ints in the NSNumber object:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1],
[NSNumber numberWithChar:'a'],
[NSNumber numberWithInt:2],
[NSNumber numberWithChar:'b'],
nil];
Or if you don't want boxing, why not just make a function that takes chars and returns ints?
int charToScore(char character)
{
switch (character) {
case 'a':
return 1;
case 'b':
return 2;
default:
return 0;
}
}
#Pontus has the correct answer in Objective-C, but if you're willing to use C++, you can use std::map<char, int> (or the still-slightly-nonstandard unordered_map<char, int>.)
To use C++ from within Objective-C, you must rename the file from Whatever.m to Whatever.mm--this tells GCC that the file contains Objective-C++, which allows you to use the Objective-C syntax with C++ underpinnings.

need help converting an MPMediaItemPropertyPersistentID to a string and back again

I'm getting the ID of a media item from the MPMediaPickerController. According to the documentation, this value is an NSNumber object containing a uint64_t (unsigned long long). I would like to convert it to an NSString for saving in my data model. However, when I convert it to a string its value changes, and when I convert it back to a number it changes again! Obviously I'm not understanding something about these data types. Can you help?
MPMediaItem *mediaPicked;
// set mediaPicked to an MPMedia item using MPMediaPickerController...
NSLog(#"id as number: %qu", [mediaPicked valueForProperty:MPMediaItemPropertyPersistentID]); // outputs 566042331449280
NSLog(#"id as string: %#", [[mediaPicked valueForProperty:MPMediaItemPropertyPersistentID] stringValue]); // outputs 16204893883745507648
NSLog(#"id as number: %qu", [[[mediaPicked valueForProperty:MPMediaItemPropertyPersistentID] stringValue] longLongValue]); // outputs 9223372036854775807
If I try to play the media item before this conversion, it always works. But if I try to play the media item after this conversion, only about half of the media items I've tried work. So some ID values survive the conversion and some don't.
You are starting by saying this:
NSLog(#"id as number: %qu", [mediaPicked valueForProperty:MPMediaItemPropertyPersistentID]); // outputs 566042331449280
But that's wrong. %qu means "this thing is an unsigned long long". But this thing is not an unsigned long long. It's an object! It's an NSNumber wrapped around an unsigned long long. You are lying to NSLog, so you're getting garbage output in your very first statement.
Now, try this on your own machine:
uint64_t x = 16204893883745507648ULL;
NSLog(#"%qu", x);
NSNumber* n = [NSNumber numberWithUnsignedLongLong:x];
NSLog(#"%#", n);
NSLog(#"%#", [n stringValue]);
All of those NSLog statements give the same result - because they are all correct formulations, unlike the one you started with. So, those NSLog statements show you the kind of thing you ought to be saying.
Now, you might think: Oh, great, so I can get from an NSNumber to an NSString with stringValue after all. Yes, but you can't get back again. We cannot get from [n stringValue] to a correct NSNumber by using longlongValue, because a long long is not an unsigned long long. There is no unsignedLonglongValue. So you can't get there from here.
So what's the right thing to do? Don't convert at all! You've got an NSNumber, it's valid, just keep it and use it. An NSNumber is a value you can store in your model. (For example, it can go into a dictionary as a value or as a key, it can be a value in user defaults, and so on.)

Storing and retrieving unsigned long long value to/from NSString

I have an unsigned long long value which I want to store into an NSString and retrieve from the string.
Initially I have the value in an NSNumber and I am using this to get the string
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
where myNum is an NSNumber.
To get back the NSNumber from the NSString I have to first get the unsigned long long value. But there is no method in the NSString class to do that (we just have one for getting the long long value, not the unsigned long long value).
Can someone please tell me how I can get back the value into an NSNumber variable.
Thanks.
There are a lot of ways to accomplish this. The following is the most pragmatic:
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
// .. code and time in between when numStr was created
// .. and now needs to be converted back to a long long.
// .. Therefore, numStr used below does not imply the same numStr above.
unsigned long long ullvalue = strtoull([numStr UTF8String], NULL, 0);
This makes a few reasonable assumptions such as numStr will only contain numeric digits and it contains a 'valid' unsigned long long value. A drawback to this approach is that UTF8String creates what essentially amounts to [[numStr dataUsingEncoding:NSUTF8StringEncoding] bytes], or in other words something along the lines of 32 bytes of autoreleased memory per call. For the vast majority of uses, this is no problem what-so-ever.
For an example of how to add something like unsignedLongLongValue to NSString that is both very fast and uses no autoreleased memory as a side effect, take a look at the end of my (long) answer to this SO question. Specifically the example implementation of rklIntValue, which would require only trivial modifications to implement unsignedLongLongValue.
More information regarding strtoull can be found in its man page.