0

The iOS SpeakHere example code has a pair of methods in Class SpeakHereController, stopRecord and record that respectively save and initialize a file for saving the recording. The two methods handle the filename string slightly differently as you can see in the following two lines of code in those methods.

recordFilePath = (CFStringRef)[NSTemporaryDirectory() stringByAppendingPathComponent: @"recordedFile.caf"];
recorder->StartRecord(CFSTR("recordedFile.caf"));

The string "recordedFile.caf" occurs once preceded by an @ sign and once without. I am planning on using the following NSString constuct to produce filename, but I don't know how to use the result correctly in the two places mentioned in this paragraph. So my question is how to use the constructed string filename in those lines?

@property int counter;
NSString *filename = [[NSString alloc] initWithFormat:@"recordedFile%d.caf",self.counter];
zerowords
  • 2,915
  • 4
  • 29
  • 44

2 Answers2

1

try

recorder->StartRecord(CFSTR([filename UTF8String]));
Ryan
  • 4,799
  • 1
  • 29
  • 56
  • 4
    this is a bad workaround. you can just bridge cast `NSString` to `CFString` – Bryan Chen Aug 13 '14 at 01:37
  • @BryanChen Why is it so bad? too pricy? Why `(__bridge CFStringRef)filename` is better? – Ryan Aug 13 '14 at 01:43
  • 1
    it perform unnecessary copy. cast is just nop in runtime. (except bridge retained/transfer have an extra retain/release call) – Bryan Chen Aug 13 '14 at 01:49
  • 1
    ... not just an unnecessary copy but given that the storage type within `NSString` is opaque (and probably `unichar`), quite possibly two string conversions. Which means parsing the one format and outputting the other. – Tommy Aug 13 '14 at 02:49
0

The difference is between a C API for string objects (CFStringRef in Core Foundation) vs. an Objective-C API for string objects (NSString in Cocoa).

The compiler knows that @"..." is an Objective-C string literal and constructs a static instance of (a private subclass of) NSString.

The compiler doesn't exactly have a similar native knowledge of CFString literals. Instead, Apple's headers define the CFSTR() preprocessor macro to wrap a C-style string in a Core Foundation string. The argument passed to the macro must be a C string literal (e.g. "foo"). The syntax in the other answer which passes a run-time expression returning a non-literal C string pointer not only isn't correct, I don't believe it can compile. Depending on the compiler, CFSTR() may produce a true compile-time CFString object rather than being a run-time expression.

So: @"recordedFile.caf" is an NSString literal, CFSTR("recordedFile.caf") is a C string literal turned into a CFStringRef. You should just think of it as a CFString literal.

Happily, Apple designed Core Foundation and Cocoa so that NSString and CFString are toll-free bridged. They are, at a certain level of abstraction, the same sort of object. You can just type-cast between the two types freely. With ARC, such a type cast requires a bridge that lets ARC know about the memory management of the change.

So, you can do:

CFStringRef cfstring = CFSTR("recordedFile.caf");
NSString* nsstring = (__bridge NSString*)cfstring;

Or:

NSString* nsstring = @"recordedFile.caf";
CFStringRef cfstring = (__bridge CFStringRef)nsstring;

For your particular case:

recorder->StartRecord((__bridge CFStringRef)filename);
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154