views:

586

answers:

4

I am coming to Objective-C from C# without any intermediate knowledge of C. (Yes, yes, I will need to learn C at some point and I fully intend to.) In Apple's Certificate, Key, and Trust Services Programming Guide, there is the following code:

static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";

I have an NSString that I would like to use as an identifier here and for the life of me I can't figure out how to get that into this data structure. Searching through Google has been fruitless also. I looked at the NSString Class Reference and looked at the UTF8String and getCharacters methods but I couldn't get the product into the structure.

What's the simple, easy trick I'm missing?

+2  A: 

try this

NSString *newString = @"This is a test string.";
char *theString;

theString = [newString cStringWithEncoding:[NSString defaultCStringEncoding]];

ennuikiller
I assume you meant cStringUsingEncoding: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/cStringUsingEncoding:
bbrown
I think [newString UTF8String] is easier, no?
Jason Coco
Yeah, no need to use the longer method, -UTF8String is usually preferable.
Quinn Taylor
+3  A: 

Obtaining a char* (C string) from an NSString isn't the tricky part. (BTW, I'd also suggest UTF8String, it's much simpler.) The Apple-supplied code works because it's assigning a C string literal to the static const array variables. Assigning the result of a function or method call to a const will probably not work.

I recently answered an SO question about defining a constant in Objective-C, which should help your situation. You may have to compromise by getting rid of the const modifier. If it's declared static, you at least know that nobody outside the compilation unit where it's declared can reference it, so just make sure you don't let a reference to it "escape" such that other code could modify it via a pointer, etc.

However, as @Jason points out, you may not even need to convert it to a char* at all. The sample code creates an NSData object for each of these strings. You could just do something like this within the code (replacing steps 1 and 3):

NSData* publicTag  = [@"com.apple.sample.publickey"  dataUsingEncoding:NSUnicodeStringEncoding];
NSData* privateTag = [@"com.apple.sample.privatekey" dataUsingEncoding:NSUnicodeStringEncoding];

That sure seems easier to me than dealing with the C arrays if you already have an NSString.

Quinn Taylor
+5  A: 

Those are C strings: Arrays (not NSArrays, but C arrays) of characters. The last character is a NUL, with the numeric value 0.

“UInt8” is the CoreServices name for an unsigned octet, which (on Mac OS X) is the same as an unsigned char.

static means that the array is specific to this file (if it's in file scope) or persists across function calls (if it's inside a method or function body).

const means just what you'd guess: You cannot change the characters in these arrays.

\0 is a NUL, but including it explicitly in a "" literal as shown in those examples is redundant. A "" literal (without the @) is NUL-terminated anyway.

C doesn't specify an encoding. On Mac OS X, it's generally something ASCII-compatible, usually UTF-8.

To convert an NSString to a C-string, use UTF8String or cStringUsingEncoding:. To have the NSString extract the C string into a buffer, use getCString:maxLength:encoding:.

Peter Hosey
Dang, I can only mark one as the answer. Jason Coco covered the "easy trick" part but you really helped on the "unfamiliar syntax" issue. Thank you very much!
bbrown
+3  A: 

I think some people are missing the point here. Everyone has explained the two constant arrays that are being set up for the tags, but if you want to use an NSString, you can simply add it to the attribute dictionary as-is. You don't have to convert it to anything. For example:

NSString *publicTag = @"com.apple.sample.publickey";
NSString *privateTag = @"com.apple.sample.privatekey";

The rest of the example stays exactly the same. In this case, there is no need for the C string literals at all.

Jason Coco
That's definitely true if he can use it as an NSString directly. If he needs to interface with other code that requires a char*, converting it could still be the right thing to do. Since the Apple sample creates NSData objects from the strings, he could also use something like [theString dataUsingEncoding: NSUnicodeStringEncoding].
Quinn Taylor
That's what I ended up doing once the completely obvious issue of const was apparent.
bbrown
@quinn - I think that Apple's example is a bit confusing and is probably the case of somebody just updating an example from older documents. The only reason they pack the strings into NSData objects is so that they can then insert them into the CFDictionary that the new API expects. The older API (the one used on the Mac, but not on iPhone) still expects C Strings as parameters.
Jason Coco