Objective C: looping issue - iphone

I'm just starting out with objective c (coming from java) and I'm working on a calculator program just to practice with the syntax and some basic stuff. The way I'm going about it is having the user input a string and looking through for operators (taking order of operations into account) and then finding the term surrounding that operator, calculating it, replacing the term with the answer, and repeating for all the terms; however, I'm having an issue with the method I'm using to calculate the term. I pass in the index of the operator and have it loop backwards until it hits another operator to find the number immediately before it, and do the same forwards for the number after. My issue is that the loop does not stop when it hits the operators, and instead just continues until the end of the string in both directions. It's probably something really simple that I've overlooked but I've been trying to figure this out for a while and can' seem to get it. I've included an SSCCE of just the first half of the method, with a predetermined string and operator index. (also, a secondary question: is there any better way to post code blocks on this site rather than manually putting in 4 spaces before every line?)
#import <Foundation/Foundation.h>
int firstNumInTerm(int index);
NSString *calculation;
int main(int argc, const char * argv[])
{
#autoreleasepool {
calculation = #"51-43+378*32";
int firstNumber = firstNumInTerm(9);
NSLog(#"The number before the term is: %i", firstNumber);
}
return 0;
}
int firstNumInTerm(int index){
int firstNumIndex = index - 1;
int firstNumLength = 1;
NSRange prevChar = NSMakeRange(firstNumIndex - 1, 1);
while ([calculation substringWithRange:prevChar] != #"*" &&
[calculation substringWithRange:prevChar] != #"/" &&
[calculation substringWithRange:prevChar] != #"+" &&
[calculation substringWithRange:prevChar] != #"-" &&
firstNumIndex > 0) {
NSLog(#"prevChar: %#", [calculation substringWithRange:prevChar]);//TEST
firstNumIndex--; firstNumLength++;
prevChar = NSMakeRange(firstNumIndex - 1, 1);
}
NSRange firstRange = NSMakeRange(firstNumIndex, firstNumLength);
int firstNum = [[calculation substringWithRange:firstRange] intValue];
NSLog(#"firstNum String: %#", [calculation substringWithRange:firstRange]);//TEST
NSLog(#"firstNum int: %i", firstNum);//TEST
return firstNum;
}

The problem with this line:
[calculation substringWithRange:prevChar] != #"*" is that you are comparing the value of two pointers. [calculation substringWithRange:prevChar] returns a pointer to an NSString object, as does the NSString literal statement #"*". The simplest way to compare two strings is by using the isEqualToString: method of NSString. For example:
NSString *myName = #"Stephen";
NSString *yourName = #"Matt";
if([myName isEqualToString:yourName]){
printf("We have the same name!");
}
else{
printf("We do not have the same name");
}
If you are going to be doing a lot of string comparisons, it might be wise to write a macro, such as:
#define STREQ(x,y) [x isEqualToString:y]
Regarding copy/pasting code into StackOverflow:
Since I use XCode 99% of the time, I find it handy to select the text I am going to copy and then hit Cmd-]. This shifts the text to the right one tab-width. I then Cmd-c to copy and then Cmd-[ to undo the right-shift.

You can't do that in Objective-C: [calculation substringWithRange:prevChar] != #"*"
Instead, you need to do :
[[calculation substringWithRange:prevChar] compare:#"*"] != NSOrderedSame
(I know, it's longer, but arithmetic operators aren't overloaded for string like they are in Java).

I see others have answered this to correct the issue with your string comparison operations, but a better way to split this string up would be using NSString's native parsing methods. For example:
NSArray *numbers = [ calculation componentsSeparatedByCharactersInSet:
[ NSCharacterSet characterSetWithCharactersInString: #"*/+-" ] ];
Will give you an array containing each of the numbers (in order) in your string. You could come up with custom parsing routines, but using NSString's is going to likely be more straightforward and a lot less buggy. It will also be easier for someone else to read and understand.

while((![[calculation substringWithRange:prevChar] isEqualToString:#"*"]) && …){
}
or
NSArray *operators = #[#"+", #"-", #"*", #"/"];
while(![operators contains:[calculation substringWithRange:prevChar]])

Related

iPhone objective-c: detecting a 'real' word

I need a (quick and dirty) solution to basically detect if a certain NSString is a 'real' word, that is, if it's in the dictionary. So basically, a very simplistic spell checker. Does anyone know of any way to do this? Basically I either need a file containing all words in the English dictionary (which I've searched for, but to no avail), or a way to interface with the iPhones spell checking service. Of course I would like to interface with the iPhones spell check service in a similar way to NSSpellChecker on OSX so my app will work with other languages, but at this point I'll take what I can get.
Lastly, here's some pseudo-code to better illustrate my needs:
-(BOOL)isDictionaryWord:(NSString*)word; //returns TRUE when word=#"greetings". returns FALSE when word=#"slkfjsdkl";
Use UITextChecker instead. The code below might not be perfect but should give you a good idea.
-(BOOL)isDictionaryWord:(NSString*)word {
UITextChecker *checker = [[UITextChecker alloc] init];
NSLocale *currentLocale = [NSLocale currentLocale];
NSString *currentLanguage = [currentLocale objectForKey:NSLocaleLanguageCode];
NSRange searchRange = NSMakeRange(0, [word length]);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:word range:searchRange startingAt:0 wrap:NO language:currentLanguage];
return misspelledRange.location == NSNotFound;
}
You can make UITextChecker work accurately without needing to add a new dictionary.
I use a two-step process because I need the first step to be fast (but not accurate). You may only need step two which is the accurate check. Note this makes use of the UITextChecker's completionsForPartialWordRange function which is why it's more accurate than the MisspelledWord function.
//Step one: I quickly check to see if a combination of letters passes the spell check. This is not that accurate but it's very fast so I can quickly exclude lots of letter combinations (brute force approach).
UITextChecker *checker;
NSString *wordToCheck = #"whatever"; // The combination of letters you wish to check
// Set the range to the length of the word
NSRange range = NSMakeRange(0, wordToCheck.length - 1);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:wordToCheck range: range startingAt:0 wrap:NO language: #"en_US"];
BOOL isRealWord = misspelledRange.location == NSNotFound;
// Call step two, to confirm that this is a real word
if (isRealWord) {
isRealWord = [self isRealWordOK:wordToCheck];
}
return isRealWord; // if true then we found a real word, if not move to next combination of letters
// Step Two: Extra check to make sure the word is really a real word. returns true if we have a real word.
-(BOOL)isRealWordOK:(NSString *)wordToCheck {
// we dont want to use any words that the lexicon has learned.
if ([UITextChecker hasLearnedWord:wordToCheck]) {
return NO;
}
// now we are going to use the word completion function to see if this word really exists, by removing the final letter and then asking auto complete to complete the word, then look through all the results and if its not found then its not a real word. Note the auto complete is very acurate unlike the spell checker.
NSRange range = NSMakeRange(0, wordToCheck.length - 1);
NSArray *guesses = [checker completionsForPartialWordRange:range inString:wordToCheck language:#"en_US"];
// confirm that the word is found in the auto-complete list
for (NSString *guess in guesses) {
if ([guess isEqualToString:wordToCheck]) {
// we found the word in the auto complete list so it's real :-)
return YES;
}
}
// if we get to here then it's not a real word :-(
NSLog(#"Word not found in second dictionary check:%#",wordToCheck);
return NO;
}

Pass a block of code?

Is it possible to pass a block of code, for example:
int i = 0;
while (i < [array count]) {
//Code to pass in here
i++;
}
Reason being that i need to perform various actions on every item in an array at different times, it'd be nice to have 1 method and pass it a block of code to run.
Have you considered using blocks. It's just an idea and not working or compilable code
typedef int (^block_t)();
-(void) methodName:(block_t) code_block
{
int i = 0;
while (i < [array count]) {
code_block() //Code to pass in here
i++;
}
block_t youCode = ^{ NSLog("Just an example"); }
[self methodName:youCode];
You can definitely iterate an array and process a block of code on it. This feature has been a standard part of Objective C since 2.0, iOS since 4.0 and in addition was included in Snow Leopard. If you look up the NSArray class reference you will find the following functions:
enumerateObjectsAtIndexes:options:usingBlock:
Executes a given block using the
objects in the array at the specified
indexes.
enumerateObjectsUsingBlock: Executes a
given block using each object in the
array, starting with the first object
and continuing through the array to
the last object.
enumerateObjectsWithOptions:usingBlock:
Executes a given block using each
object in the array.
You can define the code block to be executed globally in your implementation file, or in place where its needed. You can find some good examples on block programming here: http://thirdcog.eu/pwcblocks/.
It sounds like you want "blocks", which are a feature of Objective-C similar to "lambdas", "anonymous functions" and "closures" in other languages.
See Apple's documentation: Blocks Programming Topics
You should put the code into a method and call the method like so:
-(void)foo{
int i = 0;
while (i < [array count]) {
[self myMethod];
i++;
}
}
-(void)myMethod{
//Code to pass in here
}
You could also store the method as a variable allowing you to change which method is called
SEL methodToCall = #selector(myMethod);
-(void)foo{
int i = 0;
while (i < [array count]){
[self performSelector:methodToCall];
i++;
}
}
-(void)myMethod{
//Code to pass in here
}
first of all you could give a more detailed example.
second you could take a look at Action or Func in case you need a return message (well something equivalent for obj-C if exists)
but again, not understanding what you need to do, it is hard to give you an answer
cause from you Q i could understand as #Trevor did that you need to run a method:
int i = 0;
while (i < [array count]) {
if (i % 2)
EvenMethod(array[i])
else
OddMethod(array[i])
i++;
}
If you can live with 4.0+ compatibility, I highly recommend the use of blocks.
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
gives you all you need and you can define your block right where you need it. There are some other variants of this, too. Please refer to the 'NSArray' documentation.
Why not just define functions that do what you want, and call them at various times with the items in your array as arguments?
If your concern is that you don't want redundant code with multiple while loops you have here, you could write a function that takes an array and a function as an argument, then applies that function to every element in the array.
Adapted from here:
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
// <pt2Func> is a pointer to a function which returns void and takes an NSObject*
void DoForAllItems( NSArray* array, void (*pt2Func)(NSObject*) )
{
int i = 0;
while (i < [array count]) {
(*pt2Func)([array objectAtIndex:i]);
i++;
}
}
// 'DoIt' takes an NSObject*
void DoIt (NSObject* object){ NSLog(#"DoIt"); }
// execute example code
void Pass_A_Function_Pointer()
{
NSArray* array= [NSArray arrayWithObjects:#"1", #"2", nil];
DoForAllItems(array, &DoIt);
}
http://thirdcog.eu/pwcblocks/#objcblocks
Blocks were added recently to Objective-C I hope they have found their way to the Iphone also.
and it was anwered here also before:
Using Objective-C Blocks
Regards
If you are using Objective C++, I would strongly recommend function objects (even over blocks). They have a nice syntax, are easy to use, and are supported everywhere on modern C++ compilers (MSVC++11, GNUC++11, and LLVMC++11):
void go( function<void (int arg)> func )
{
int i = 0;
while (i < [array count]) {
//Code to pass in here
func( i ) ; // runs func on i
i++;
}
}
calling it:
go( []( int arg ){ // the square brackets are the "capture"
printf( "arg was %d\n", arg ) ; // arg takes on whatever value was passed to it,
// just like a normal C function
} ) ;

Create NSString with 'N' Unicode Characters

I'm trying to create a string containing (unicode) 'stars' based on an integer rating. I currently have:
NSMutableString *stars = [NSMutabelString stars];
for (int i = 0; i < rating; i++)
{
[stars appendString:#"\u2605"];
}
However, I find this a bit ugly. Does a way exist to construct such a string without using this looping method? Something using the string formats?
Sure - to do this on a single line you can use the stringByPaddingToLength method:
[#"" stringByPaddingToLength: rating withString: #"\u2605" startingAtIndex:0];
...should hopefully do the trick for you - and no need to create any subclasses or categories, etc!
You can make a category for NSString with an extra method - say, +(NSString)stringForRating:(NSInteger)rating, and move the loop in there. Then whenever you need a star string, just call that.

NSString stringWithFormat swizzled to allow missing format numbered args

Based on this SO question asked a few hours ago, I have decided to implement a swizzled method that will allow me to take a formatted NSString as the format arg into stringWithFormat, and have it not break when omitting one of the numbered arg references (%1$#, %2$#)
I have it working, but this is the first copy, and seeing as this method is going to be potentially called hundreds of thousands of times per app run, I need to bounce this off of some experts to see if this method has any red flags, major performance hits, or optimizations
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {
if (format != nil) {
va_list args;
va_start(args, format);
// $# is an ordered variable (%1$#, %2$#...)
if ([format rangeOfString:#"$#"].location == NSNotFound) {
//call apples method
NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
va_end(args);
return s;
}
NSMutableArray *newArgs = [NSMutableArray arrayWithCapacity:NUMARGS(args)];
id arg = nil;
int i = 1;
while (arg = va_arg(args, id)) {
NSString *f = [NSString stringWithFormat:#"%%%d\$\#", i];
i++;
if ([format rangeOfString:f].location == NSNotFound) continue;
else [newArgs addObject:arg];
}
va_end(args);
char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
[newArgs getObjects:(id *)newArgList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
free(newArgList);
return result;
}
return nil;
}
The basic algorithm is:
search the format string for the %1$#, %2$# variables by searching for %#
if not found, call the normal stringWithFormat and return
else, loop over the args
if the format has a position variable (%i$#) for position i, add the arg to the new arg array
else, don't add the arg
take the new arg array, convert it back into a va_list, and call initWithFormat:arguments: to get the correct string.
The idea is that I would run all [NSString stringWithFormat:] calls through this method instead.
This might seem unnecessary to many, but click on to the referenced SO question (first line) to see examples of why I need to do this.
Ideas? Thoughts? Better implementations? Better Solutions?
Whoa there!
Instead of screwing with a core method that you very probably will introduce subtle bugs into, instead just turn on "Static Analyzer" in your project options, and it will run every build - if you get the arguments wrong it will issue a compiler warning for you.
I appreciate your desire to make the application more robust but I think it very likely that re-writing this method will more likely break your application than save it.
How about defining your own interim method instead of using format specifiers and stringWithFormat:? For example, you could define your own method replaceIndexPoints: to look for ($1) instead of %1$#. You would then format your string and insert translated replacements independently. This method could also take an array of strings, with NSNull or empty strings at the indexes that don't exist in the “untranslated” string.
Your method could look like this (if it were a category method for NSMutableString):
- (void) replaceIndexPointsWithStrings:(NSArray *) replacements
{
// 1. look for largest index in "self".
// 2. loop from the beginning to the largest index, replacing each
// index with corresponding string from replacements array.
}
Here's a few issues that I see with your current implementation (at a glance):
The __VA_ARGS__ thingy explained in the comments.
When you use while (arg = va_arg(args, id)), you are assuming that the arguments are nil terminated (such as for arrayWithObjects:), but with stringWithFormat: this is not a requirement.
I don't think you're required to escape the $ and # in your string format in your arg-loop.
I'm not sure this would work well if uaStringWithFormat: was passed something larger than a pointer (i.e. long long if pointers are 32-bit). This may only be an issue if your translations also require inserting unlocalised numbers of long long magnitude.

What's the CFString Equiv of NSString's UTF8String?

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;
}