I have a big NSDictionary full of entries that are all of type NSData. I have several entries that need to be of type int32_t however I am not 100% sure how to copy the data in the entries of the NSDictionary across..
is it as simple as doing the following -
.h
//..
int32_t myint;
}
#property (assign) int32_t myint;
//..
.m
//..
#synthesize cardID;
//..
- (void)assignSearchData:(NSData*)searchData
{
myint = [searchData objectForKey:#"IntKey"];
}
//..
or do I need some type of data conversion inside my method?
and a quick side question, have I even declared the int32_t correctly? I have looked for an example in the docs and on here but am struggling to find one.
Well, you can access the raw bytes in the data object directly.
void const *dataPtr = [data bytes];
Now that you have a pointer to raw memory, you can copy it any way you want (these rules apply to any data transfer, not just iOS). If you need to consider alignment boundaries, you need to use memcpy.
int32_t myInt;
memcpy(&myInt, dataPtr);
Otherwise, if on an architecture that allows integer manipulation across alignment boundaries...
int32_t myInt = *(int32_t const *)dataPtr;
Now, ARM supports access across alignment boundaries, but it's much slower. I have not done a performance comparison, but you are not continuing to use the mal-alignged pointer, so it may be better than the memcpy function call (though, to be honest, that is probably way too much performance consideration for you).
The biggest concern is byte-order of the data. If it's provided by you, then do whatever you want, but you should prefer one standard.
If it's coming from a third party, it's probably in network byte order (aka big-endian). You may need to convert to your host endian representation. Fortunately, that's straight forward with hton and ntoh and their friends.
FWIW, Intel is little-endian, and network-byte-order is big-endian, modern Macs and iOS devices are little-endian, older Macs are big-endian.
// Convert from network order to host order.
// Does the right thing wherever your code is running
myInt = ntohl(myInt);
In short, either...
int32_t myInt = ntohl(*(int32_t const *)[data bytes]);
or
int32_t myInt;
memcpy(&myInt, [data bytes);
myInt = ntohl(myInt);
So, the data has to get in there somehow. It's, the inverse...
int32_t myInt = 42;
myInt = htonl(myInt);
NSData *data = [NSData dataWithBytesNoCopy:&myInt length:sizeof(myInt) freeWhenDone:NO];
Of course, use the right Data initializer... that one will just use those raw bytes on the stack, so you better not use it after the stack unwinds.
You don't have to worry about alignment on the data you send, unless you are guaranteeing the receiver that the data will be aligned to some boundary.
Yes, int32_t is fine. So you have a stream of byes. What you need to know is what he layout of the bytes are. It you know what the data is it will be pretty easy to construct it.
Given a NSData object with a length of 4 (size of int32_t), then you would :
int32_t val;
if([data length] == sizeof(uint32_t)) {
void *bytes = [data bytes];
// if the layout is same as iOS then
memcpy(&val, bytes, sizeof(int32_t) );
}
if that is not the case, then you can try:
unsigned char val[4] = {0,0,0,0};
if([data length] == sizeof(uint32_t)) {
memcpy(val, bytes, sizeof(int32_t) );
then rearrange the bytes
}
Related
When moving file from one place to another, or when replacing file, I always use the methods moveItemAtURL:toURL: or replaceItemAtURL:WithItemAtURL: from NSFileManager.
When calling these methods, I want to determine how much time needed, so that I can use the NSProgressIndicator to tell users how long it's going to take. Just like when you are moving file using OSX, it tells u how much time remaining.
I have looked at the apple doc but couldn't find any information regarding this.
Wondering if this can be implemented, please advise.
You can't know in advance haw long it going to take. What you can do is compute the "percent complete" while you are copying the file. But to do that you need to use lower level APIs. You can use NSFileManagers attributesOfItemAtPath:error to get the file size and NSStreams for doing the copying (there are so many way to do this). Percent complete is bytesWritten / totalBytesInFile.
--- Edit: added sample code as a category on NSURL with a callback block passing the total number of bytes written, percen complete and estimated time left in seconds.
#import <mach/mach_time.h>
#interface NSURL(CopyWithProgress)<NSObject>
- (void) copyFileURLToURL:(NSURL*)destURL withProgressBlock:(void(^)(double, double, double))block;
#end
#implementation NSURL(CopyWithProgress)
- (void) copyFileURLToURL:(NSURL*)destURL
withProgressBlock:(void(^)(double, double, double))block
{
///
// NOTE: error handling has been left out in favor of simplicity
// real production code should obviously handle errors.
NSUInteger fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil].fileSize;
NSInputStream *fileInput = [NSInputStream inputStreamWithURL:self];
NSOutputStream *copyOutput = [NSOutputStream outputStreamWithURL:destURL append:NO];
static size_t bufferSize = 4096;
uint8_t *buffer = malloc(bufferSize);
size_t bytesToWrite;
size_t bytesWritten;
size_t copySize = 0;
size_t counter = 0;
[fileInput open];
[copyOutput open];
uint64_t time0 = mach_absolute_time();
while (fileInput.hasBytesAvailable) {
do {
bytesToWrite = [fileInput read:buffer maxLength:bufferSize];
bytesWritten = [copyOutput write:buffer maxLength:bytesToWrite];
bytesToWrite -= bytesWritten;
copySize += bytesWritten;
if (bytesToWrite > 0)
memmove(buffer, buffer + bytesWritten, bytesToWrite);
}
while (bytesToWrite > 0);
if (block != nil && ++counter % 10 == 0) {
double percent = (double)copySize / fileSize;
uint64_t time1 = mach_absolute_time();
double elapsed = (double)(time1 - time0)/NSEC_PER_SEC;
double estTimeLeft = ((1 - percent) / percent) * elapsed;
block(copySize, percent, estTimeLeft);
}
}
if (block != nil)
block(copySize, 1, 0);
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSURL *fileURL = [NSURL URLWithString:#"file:///Users/eric/bin/data/english-words.txt"];
NSURL *destURL = [NSURL URLWithString:#"file:///Users/eric/Desktop/english-words.txt"];
[fileURL copyFileURLToURL:destURL withProgressBlock:^(double bytes, double pct, double estSecs) {
NSLog(#"Bytes=%f, Pct=%f, time left:%f s",bytes,pct,estSecs);
}];
}
return 0;
}
Sample Output:
Bytes=40960.000000, Pct=0.183890, time left:0.000753 s
Bytes=81920.000000, Pct=0.367780, time left:0.004336 s
Bytes=122880.000000, Pct=0.551670, time left:0.002672 s
Bytes=163840.000000, Pct=0.735560, time left:0.001396 s
Bytes=204800.000000, Pct=0.919449, time left:0.000391 s
Bytes=222742.000000, Pct=1.000000, time left:0.000000 s
I mostly concur with CRD. I just want to note that under certain common circumstances, both -moveItemAtURL:toURL: and -replaceItemAtURL:WithItemAtURL:... are very fast. When the source and destination are on the same volume, no data has to be copied or moved, only metadata. When the volume is local (as opposed to network-mounted), this typically takes negligible time. That said, it is appropriate to plan for the possibility that they could take significant time.
Also, he mentioned the copyfile() routine for moving files. A copy followed by deleting the original is the necessary approach when moving a file between volumes, but the rename() system call will perform a move within a volume without needing to copy anything. So, a reasonable approach would be to try rename() first and, if it fails with EXDEV, fall back to copyfile().
Finally, the exchangedata() system call can be used as part of a reimplementation of -replaceItemAtURL:WithItemAtURL:....
I don't recommend the approach suggested by aLevelOfIndirection because there are a lot of fiddly details about copying files. It's much better to rely on system libraries than trying to roll your own. His example completely ignores file metadata (file dates, extended attributes, etc.), for example.
The methods moveItemAtURL:toURL: and replaceItemAtURL:WithItemAtURL: are high-level operations. While they provide the semantics you want for the move/replace, as you've found out, they don't provide the kind of feedback you wish during those operations.
Apple is in the process of changing lower-level file handling routines, many are now marked as deprecated in 10.8, so you'll want to pick carefully what you choose to use. However at the lowest levels, system calls (manual section 2) and library functions (manual section 3), there are functions that you can use that are not being deprecated.
One option, there are others, is the function copyfile (manual section 3) which will copy a file or folder hierarchy and provides for a progress callback. That should give you most of the semantics of moveItemAtURL:toURL: along with progress, but you'll need to do more work for replaceItemAtURL:WithItemAtURL: to preserve safety (no data loss in case of error).
If that doesn't meet all your needs you can also look additionally at the low-evel stat and friends to find out file sizes etc.
HTH
I am using the libical library to parse the iCalendar format and read the information I need out of it. It is working absolutely fine so far, but there is one odd thing concerning ical.
This is my code:
icalcomponent *root = icalparser_parse_string([iCalData cStringUsingEncoding:NSUTF8StringEncoding]);
if (root)
{
icalcomponent *currentEvent = icalcomponent_get_first_component(root, ICAL_VEVENT_COMPONENT);
while (currentEvent)
{
while(currentProperty)
{
icalvalue *value = icalproperty_get_value(currentProperty);
char *icalString = icalvalue_as_ical_string_r(value); //seems to leak
NSString *currentValueAsString = [NSString stringWithCString:icalString
encoding:NSUTF8StringEncoding];
icalvalue_free(value);
//...
//import data
//...
icalString = nil;
currentValueAsString = nil;
icalproperty_free(currentProperty);
currentProperty = icalcomponent_get_next_property(currentEvent, ICAL_ANY_PROPERTY);
} //end while
} //end while
icalcomponent_free(currentEvent);
}
icalcomponent_free(root);
//...
I did use instruments to check my memory usage and were able to find out, that this line seems to leak:
char *icalString = icalvalue_as_ical_string_r(value); //seems to leak
If I'd copy and paste this line 5 or six times my memory usage would grow about 400kb and never get released anymore.
There is no free method for the icalvalue_as_ical_string_r method because it's returning a char *..
Any suggestions how to solve this issue? I would appreciate any help!
EDIT
Taking a look at the apple doc says the following:
To get a C string from a string object, you are recommended to use UTF8String. This returns a const char * using UTF8 string encoding.
const char *cString = [#"Hello, world" UTF8String];
The C string you receive is owned by a temporary object, and will become invalid when automatic deallocation takes place. If you want to get a permanent C string, you must create a buffer and copy the contents of the const char * returned by the method.
But how to release a char * string properly now if using arc?
I tried to add #autorelease {...} in front of my while-loop but without any effort. Still increasing memory usage...
Careful with the statement "no free method...because it's returning a char*"; that is never something you can just assume.
In the absence of documentation you can look at the source code of the library to see what it does; for example:
http://libical.sourcearchive.com/documentation/0.44-2/icalvalue_8c-source.html
Unfortunately this function can do a lot of different things. There are certainly some cases where calling free() on the returned buffer would be right but maybe that hasn't been ensured in every case.
I think it would be best to request a proper deallocation method from the maintainers of the library. They need to clean up their own mess; the icalvalue_as_ical_string_r() function has at least a dozen cases in a switch that might have different deallocation requirements.
icalvalue_as_ical_string_r returns a char * because it has done a malloc() for your result string. If your pointer is non-NULL, you have to free() it after use.
My app "streams" content (fixed sized files, hence quotation marks) from an HTTP server into a local file. Then there is another component of the app that opens that same file and displays it (plays it).
This is done for caching purposes, so that when the same file is requested next time, it will no longer need to be downloaded from the server.
App's spec requires that all local content is encrypted (even with the most light weight encryption)
Question: has there been done any work, allowing one to simply redirect the stream to a library which will then save the stream encrypted into a file? And then, when I request the stream from the local file, the library returns an on the fly decrypted stream?
I've been searching for a solution with no results so far
Thanks
I ended up writing a custom solution that uses RC4 encryption from the built in Crypt library. It was surprisingly straight forward. Basically it involved creating a function that encrypts/decrypts chunks of NSData and then read/write those chunks to files... Here's the function that does the encryption in case someone else is interested:
- (NSData*)RC4EncryptDecryptWithKey:(NSString *)key operation:(CCOperation)operation
{
// convert to C string..
int keySize = [key length];
char keyPtr[keySize];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr
maxLength:sizeof(keyPtr)
encoding:NSUTF8StringEncoding];
// encode/decode
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength;
void *buffer = malloc(bufferSize);
size_t numBytesOut = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmRC4,
kCCOptionECBMode,
keyPtr,
8,
NULL,
[self bytes],
dataLength,
buffer,
bufferSize,
&numBytesOut);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer
length:numBytesOut
freeWhenDone:YES];
}
free(buffer);
return nil;
}
- (NSData*)RC4EncryptWithKey:(NSString*)key {
return [self RC4EncryptDecryptWithKey:key operation:kCCEncrypt];
}
- (NSData*)RC4DecryptWithKey:(NSString*)key {
return [self RC4EncryptDecryptWithKey:key operation:kCCDecrypt];
}
Obviously one could create something more secure (eg AES) or whatever (in fact I used examples of other encryption wrappers to write this one)
I wouldn't worry about encryption just because Apple says so.
Make this work how you want it (without encryption, it sounds like) and submit it for approval. If approved, you're good. If not, worry about it then. If your design requires you to make a decision now, your design might be flawed.
When converting a NSString to a char* using UTF8String, how to retain it?
According to the following link, when you use UTF8String the returned char* is pretty much autoreleased, so it won't last beyond the current function: http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSString_Class/Reference/NSString.html#jumpTo_128
It says i have to copy it or something to keep it around. How can i do this?
The reason i ask is that if i do [myCharPointer retain] it doesn't retain it because it's not an obj-c object, it's a c pointer.
Thanks
You can use strdup()
const char* utf8Str = [#"an example string" UTF8String];
if (utf8Str != NULL)
{
stringIWantToKeep = strdup(utf8Str);
}
When you are done with stringIWantToKeep, you free it as though it was originally malloc'd.
Try using -getCString:maxLength:encoding:, e.g.:
NSUInteger bufferCount = sizeof(char) * ([string length] + 1);
const char *utf8Buffer = malloc(bufferCount);
if ([string getCString:utf8Buffer
maxLength:bufferCount
encoding:NSUTF8StringEncoding]) {
NSLog("Success! %s", utf8Buffer);
free(utf8Buffer); // Remember to do this, or you will get a memory leak!
}
If you need an object to retain/release as every other object you can do the following:
NSData* storage= [yourNSString dataUsingEncoding:NSUTF8StringEncoding];
char* yourCString= (char*)storage.bytes;
// your code flow here
Now you can pass both storage and yourCString to any function retaining/releasing storage as you like, when the retain count of storage will go to 0 the memory pointed by yourCString will be freed too.
I used this to keep a single copy of a very long string while having references created by strtok_r in beans and have the memory released only when all beans had been released.
A little note about (missing) BOM:
As stated here http://boredzo.org/blog/archives/2012-06-03/characters-in-nsstring dataUsingEncoding: should add BOM but i've checked with the debugger and there is no BOM in the returned bytes.
I'm stuck on stoopid today as I can't convert a simple piece of ObjC code to its Cpp equivalent. I have this:
const UInt8 *myBuffer = [(NSString*)aRequest UTF8String];
And I'm trying to replace it with this:
const UInt8 *myBuffer = (const UInt8 *)CFStringGetCStringPtr(aRequest, kCFStringEncodingUTF8);
This is all in a tight unit test that writes an example HTTP request over a socket with CFNetwork APIs. I have working ObjC code that I'm trying to port to C++. I'm gradually replacing NS API calls with their toll free bridged equivalents. Everything has been one for one so far until this last line. This is like the last piece that needs completed.
This is one of those things where Cocoa does all the messy stuff behind the scenes, and you never really appreciate just how complicated things can be until you have to roll up your sleeves and do it yourself.
The simple answer for why it's not 'simple' is because NSString (and CFString) deal with all the complicated details of dealing with multiple character sets, Unicode, etc, etc, while presenting a simple, uniform API for manipulating strings. It's object oriented at its best- the details of 'how' (NS|CF)String deals with strings that have different string encodings (UTF8, MacRoman, UTF16, ISO 2022 Japanese, etc) is a private implementation detail. It all 'just works'.
It helps to understand how [#"..." UTF8String] works. This is a private implementation detail, so this isn't gospel, but based on observed behavior. When you send a string a UTF8String message, the string does something approximating (not actually tested, so consider it pseudo-code, and there's actually simpler ways to do the exact same thing, so this is overly verbose):
- (const char *)UTF8String
{
NSUInteger utf8Length = [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSMutableData *utf8Data = [NSMutableData dataWithLength:utf8Length + 1UL];
char *utf8Bytes = [utf8Data mutableBytes];
[self getBytes:utf8Bytes
maxLength:utf8Length
usedLength:NULL
encoding:NSUTF8StringEncoding
options:0UL
range:NSMakeRange(0UL, [self length])
remainingRange:NULL];
return(utf8Bytes);
}
You don't have to worry about the memory management issues of dealing with the buffer that -UTF8String returns because the NSMutableData is autoreleased.
A string object is free to keep the contents of the string in whatever form it wants, so there's no guarantee that its internal representation is the one that would be most convenient for your needs (in this case, UTF8). If you're using just plain C, you're going to have to deal with managing some memory to hold any string conversions that might be required. What was once a simple -UTF8String method call is now much, much more complicated.
Most of NSString is actually implemented in/with CoreFoundation / CFString, so there's obviously a path from a CFStringRef -> -UTF8String. It's just not as neat and simple as NSString's -UTF8String. Most of the complication is with memory management. Here's how I've tackled it in the past:
void someFunction(void) {
CFStringRef cfString; // Assumes 'cfString' points to a (NS|CF)String.
const char *useUTF8StringPtr = NULL;
UInt8 *freeUTF8StringPtr = NULL;
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
if((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) {
if((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
}
long utf8Length = (long)((freeUTF8StringPtr != NULL) ? usedBytes : stringLength);
if(useUTF8StringPtr != NULL) {
// useUTF8StringPtr points to a NULL terminated UTF8 encoded string.
// utf8Length contains the length of the UTF8 string.
// ... do something with useUTF8StringPtr ...
}
if(freeUTF8StringPtr != NULL) { free(freeUTF8StringPtr); freeUTF8StringPtr = NULL; }
}
NOTE: I haven't tested this code, but it is modified from working code. So, aside from obvious errors, I believe it should work.
The above tries to get the pointer to the buffer that CFString uses to store the contents of the string. If CFString happens to have the string contents encoded in UTF8 (or a suitably compatible encoding, such as ASCII), then it's likely CFStringGetCStringPtr() will return non-NULL. This is obviously the best, and fastest, case. If it can't get that pointer for some reason, say if CFString has its contents encoded in UTF16, then it allocates a buffer with malloc() that is large enough to contain the entire string when its is transcoded to UTF8. Then, at the end of the function, it checks to see if memory was allocated and free()'s it if necessary.
And now for a few tips and tricks... CFString 'tends to' (and this is a private implementation detail, so it can and does change between releases) keep 'simple' strings encoded as MacRoman, which is an 8-bit wide encoding. MacRoman, like UTF8, is a superset of ASCII, such that all characters < 128 are equivalent to their ASCII counterparts (or, in other words, any character < 128 is ASCII). In MacRoman, characters >= 128 are 'special' characters. They all have Unicode equivalents, and tend to be things like extra currency symbols and 'extended western' characters. See Wikipedia - MacRoman for more info. But just because a CFString says it's MacRoman (CFString encoding value of kCFStringEncodingMacRoman, NSString encoding value of NSMacOSRomanStringEncoding) doesn't mean that it has characters >= 128 in it. If a kCFStringEncodingMacRoman encoded string returned by CFStringGetCStringPtr() is composed entirely of characters < 128, then it is exactly equivalent to its ASCII (kCFStringEncodingASCII) encoded representation, which is also exactly equivalent to the strings UTF8 (kCFStringEncodingUTF8) encoded representation.
Depending on your requirements, you may be able to 'get by' using kCFStringEncodingMacRoman instead of kCFStringEncodingUTF8 when calling CFStringGetCStringPtr(). Things 'may' (probably) be faster if you require strict UTF8 encoding for your strings but use kCFStringEncodingMacRoman, then check to make sure the string returned by CFStringGetCStringPtr(string, kCFStringEncodingMacRoman) only contains characters that are < 128. If there are characters >= 128 in the string, then go the slow route by malloc()ing a buffer to hold the converted results. Example:
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
for(CFIndex idx = 0L; (useUTF8String != NULL) && (useUTF8String[idx] != 0); idx++) {
if(useUTF8String[idx] >= 128) { useUTF8String = NULL; }
}
if((useUTF8String == NULL) && ((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL)) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
Like I said, you don't really appreciate just how much work Cocoa does for you automatically until you have to do it all yourself. :)
In the sample code above, the following appears:
CFIndex stringLength = CFStringGetLength(cfString)
stringLength is then being used to malloc() a temporary buffer of that many bytes, plus 1.
But the header file for CFStringGetLength() expressly says it returns the number of 16-bit Unicode characters, not bytes. So if some of those Unicode characters are outside the ASCII range, the malloc() buffer won't be long enough to hold the UTF-8 conversion of the string.
Perhaps I'm missing something, but to be absolutely safe, the number of bytes needed to hold N arbitrary Unicode characters is at most 4*n, when they're all converted to UTF-8.
From the documentation:
Whether or not this function returns a valid pointer or NULL depends on many factors, all of which depend on how the string was created and its properties. In addition, the function result might change between different releases and on different platforms. So do not count on receiving a non-NULL result from this function under any circumstances.
You should use CFStringGetCString if CFStringGetCStringPtr returns NULL.
Here's some working code. I started with #johne's answer, replaced CFStringGetBytes with CFStringGetLength for simplicity, and made the correction suggested by #Doug.
const char *useUTF8StringPtr = NULL;
char *freeUTF8StringPtr = NULL;
if ((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL)
{
CFIndex stringLength = CFStringGetLength(cfString);
CFIndex maxBytes = 4 * stringLength + 1;
freeUTF8StringPtr = malloc(maxBytes);
CFStringGetCString(cfString, freeUTF8StringPtr, maxBytes, kCFStringEncodingUTF8);
useUTF8StringPtr = freeUTF8StringPtr;
}
// ... do something with useUTF8StringPtr...
if (freeUTF8StringPtr != NULL)
free(freeUTF8StringPtr);
If it's destined for a socket, perhaps CFStringGetBytes() would be your best choice?
Also note that the documentation for CFStringGetCStringPtr() says:
This function either returns the requested pointer immediately, with no memory allocations and no copying, in constant time, or returns NULL. If the latter is the result, call an alternative function such as the CFStringGetCString function to extract the characters.
Here's a way to printf a CFStringRef which implies we get a '\0'-terminated string from a CFStringRef:
// from: http://lists.apple.com/archives/carbon-development/2001/Aug/msg01367.html
// by Ali Ozer
// gcc -Wall -O3 -x objective-c -fobjc-exceptions -framework Foundation test.c
#import <stdio.h>
#import <Foundation/Foundation.h>
/*
This function will print the provided arguments (printf style varargs) out to the console.
Note that the CFString formatting function accepts "%#" as a way to display CF types.
For types other than CFString and CFNumber, the result of %# is mostly for debugging
and can differ between releases and different platforms. Cocoa apps (or any app which
links with the Foundation framework) can use NSLog() to get this functionality.
*/
void show(CFStringRef formatString, ...) {
CFStringRef resultString;
CFDataRef data;
va_list argList;
va_start(argList, formatString);
resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList);
va_end(argList);
data = CFStringCreateExternalRepresentation(NULL, resultString,
CFStringGetSystemEncoding(), '?');
if (data != NULL) {
printf ("%.*s\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data));
CFRelease(data);
}
CFRelease(resultString);
}
int main(void)
{
// To use:
int age = 25;
CFStringRef name = CFSTR("myname");
show(CFSTR("Name is %#, age is %d"), name, age);
return 0;
}