I'm using The Amazing Audio Engine to handle playback in syncing in an iOS app.
The framework requires you to use C functions as the call back (playbackTimingReceiver) which is called on the audio thread. You then need to message the main thread again using a C function (AEAudioControllerSendAsynchronousMessageToMainThread) to which you pass a handler (pageTurnHandler).
I'm not overly experienced working with C but as I understand it I'm passing a pointer in the message which needs to be dereferenced.
Which I can achieve successfully with the line:
PlaybackManager* receiver = *((PlaybackManager**)userInfo);
But only if I turn ARC off in the project for that file using the -fno-objc-arc flag in compiled sources on the projects target.
To my question, is it possible to achieve this with ARC turned on? If so what is the correct syntax?
Relevant code segment:
#pragma mark - Audio Timing Callback
-(AEAudioControllerTimingCallback)timingReceiverCallback
{
return playbackTimingReceiver;
}
static void playbackTimingReceiver(PlaybackManager* receiver,
AEAudioController *audioController,
const AudioTimeStamp *time,
UInt32 const frames,
AEAudioTimingContext context)
{
receiver->_hostTime = getUptimeInMilliseconds(time->mHostTime);
AEAudioControllerSendAsynchronousMessageToMainThread(audioController,
pageTurnHandler,
&audioController,
sizeof(id));
}
static void pageTurnHandler(AEAudioController *audioController, void *userInfo, int userInfoLength)
{
PlaybackManager* receiver = *((PlaybackManager**)userInfo);
NSLog(#"Receiver:%#", receiver);
}
PlaybackManager * receiver = (__bridge_transfer id)*(void **)userInfo;
should do the trick. This first casts the userInfo to a pointer-to-pointer, because it contains the address of the original object pointer. Dereference that to get the original pointer, and use __bridge_transfer with a type -- id or PlaybackManager will work -- to tell ARC that the dereferenced value is actually an object that it needs to take care of.
Without running the code there appear to be two errors:
1) You are passing the contents of audioController when it looks like you meant to pass the contents of receiver - so last two args to AEAudioControllerSendAsynchronousMessageToMainThread should be &receiver & sizeof(PlaykbackManager *)
2) You need a bridge cast to get the object reference back out
Something like:
static void playbackTimingReceiver(PlaybackManager* receiver,
AEAudioController *audioController,
const AudioTimeStamp *time,
UInt32 const frames,
AEAudioTimingContext context)
{
receiver->_hostTime = getUptimeInMilliseconds(time->mHostTime);
AEAudioControllerSendAsynchronousMessageToMainThread(audioController,
pageTurnHandler,
&receiver,
sizeof(PlaybackManager*));
}
static void pageTurnHandler(AEAudioController *audioController, void *userInfo, int userInfoLength)
{
PlaybackManager* receiver = (__bridge Playback *)*((PlaybackManager**)userInfo);
NSLog(#"Receiver:%#", receiver);
}
Note: when passing object references from the ARC controlled world to the C world you often transfer ownership on the way in - so ARC doesn't release the referenced object - and transfer ownership back on the way back out - so ARC resumes ownership management. However due to the nature of AEAudioControllerSendAsynchronousMessageToMainThread, where userInfo is passed by address and copied internally - hence the size argument, it is tricker to transfer ownership. Therefore the above code does not. This means you must make sure that whatever object receiver references stays alive by having another owner.
You can just tell ARC the type of storage you would like:
PlaybackManager *audioBufferPlayer = *(__weak PlaybackManager **)userInfo;
Just be sure to do necessary nil checks before accessing any properties or calling any methods.
Related
I'm trying to extend a piece of code that uses the SuperPowered audio library in an iOS application. In particular, I'm interested in the void playerEventCallback(void *clientData, SuperpoweredAdvancedAudioPlayerEvent event, void *value)callback, which gets passed a pointer to the managing SuperPowered object when the audio player is created:
SuperpoweredAdvancedAudioPlayer(void *clientData, SuperpoweredAdvancedAudioPlayerCallback callback, unsigned int sampleRate, unsigned char cachedPointCount, unsigned int internalBufferSizeSeconds = 2, unsigned int negativeSeconds = 0);
If I understand the code right, nothing prevents me from modifying this situation for my own purpose, passing, for example, a pointer to an object in an array, which would identify the player that is/was running, instead of the managing SuperPowered object.
Can I do that, or is there a consequence I should be aware of?
I ended up doing exactly that, passing an instance of a struct defined as follows:
struct AudioPlayerWithIndex {
int index;
SuperpoweredAdvancedAudioPlayer* player;
Superpowered* superpoweredInstance;
AudioPlayerWithIndex(int index, SuperpoweredAdvancedAudioPlayer* player, Superpowered* superpoweredInstance) : index(index), player(player), superpoweredInstance(superpoweredInstance) {}
};
I have several UIView or UITableViewCell. Inside I have C callback, for example:
CCallback(bridge(self),
{(observer, data) -> Void in
let mySelf = Unmanaged<DetailedView>.fromOpaque(observer!).takeRetainedValue()
mySelf.fillLoadedData(data: data)
});
Somewhere else
func bridge<T : AnyObject>(_ obj : T) -> UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}
In C:
void CCalback(void * classPtr, void(*callback)(void *, MyData)){
//fill data
callback(classPtr, data)
}
Should I use takeUnretainedValue or takeRetainedValue in closure? As far as I understand this, retained will increase objects reference count, so it wont be auto-destructed? Otherwise, if I use takeUnretainedValue, if self is auto-released, this will crash, so using takeRetainedValue will prevent it. Am I correct?
An object pointer can be converted to a Unsafe(Mutable)RawPointer (the Swift
equivalent of the C void *) with or without retaining the object:
let ptr = UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
// Does not retain `obj`
let ptr = UnsafeMutableRawPointer(Unmanaged.passRetained(obj).toOpaque())
// Retains `obj`
The conversion back to an object pointer (often done in a callback
function called from C) can be with or without consuming a retain:
let obj = Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
// Does not consume a retain
let obj = Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
// Consumes a retain
If the lifetime of obj is guaranteed while the callback is active
then the easiest way is to use the "unretained" conversions in both directions. You are responsible for retaining obj while the
callback is active, e.g. by unregistering the callback before obj
is deinitialized.
The alternative is to use passRetained() to convert obj to
a pointer. This retains the object and therefore keeps it "alive".
The callback can still use the "unretained" conversion to convert
the pointer to an object pointer, without decreasing the retain count.
But there must be exactly one takeRetainedValue() call to consume the
retain. After that, the object can be destroyed if there are no
other references to it.
More generally, each call to passRetained(obj) increases the retain
count and each call to takeRetainedValue() decreases it, so they must be properly balanced.
I have an app that is making use of UITextChecker class provided by Apple. This class has a following bug: If the iOS device is offline (expected to be often for my app) each time I call some of UITextCheckers methods it logs following to console:
2016-03-08 23:48:02.119 HereIsMyAppName [4524:469877] UITextChecker sent string:isExemptFromTextCheckerWithCompletionHandler: to com.apple.TextInput.rdt but received error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.TextInput.rdt was invalidated."
I do not want to have logs spammed by this messages. Is there a way to disable logging from code? I would disable logging before call to any of UITextCheckers methods and reenable it afterwards. Or is there perhaps any way how to disable logging selectively per class (event if it is foundation class not mine)? Or any other solution?
Warning: This answer uses the private-yet-sort-of-documented Cocoa C functions _NSSetLogCStringFunction() and _NSLogCStringFunction().
_NSSetLogCStringFunction() is an interface created by Apple to handle the implementation function for NSLog. It was initially exposed for WebObjects to hook into NSLog statements on Windows machines, but still exists in the iOS and OS X APIs today. It is documented in this support article.
The function takes in a function pointer with the arguments const char* message, the string to log, unsigned length, the length of the message, and a BOOL withSysLogBanner which toggles the standard logging banner. If we create our own hook function for logging that doesn't actually do anything (an empty implementation rather than calling fprintf like NSLog does behind-the-scenes), we can effectively disable all logging for your application.
Objective-C Example (or Swift with bridging header):
extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));
static void hookFunc(const char* message, unsigned length, BOOL withSysLogBanner) { /* Empty */ }
// Later in your application
_NSSetLogCStringFunction(hookFunc);
NSLog(#"Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
An example implementation of this can be found in YILogHook, which provides an interface to add an array of blocks to any NSLog statement (write to file, etc).
Pure Swift Example:
#asmname("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 #asmname has been renamed to #_silgen_name
func _NSSetLogCStringFunction(_: ((UnsafePointer<Int8>, UInt32, Bool) -> Void)) -> Void
func hookFunc(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: Bool) -> Void { /* Empty */ }
_NSSetLogCStringFunction(hookFunc)
NSLog("Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
In Swift, you can also chose to ignore all of the block parameters without using hookFunc like so:
_NSSetLogCStringFunction { _,_,_ in }
To turn logging back on using Objective-C, just pass in NULL as the function pointer:
_NSSetLogCStringFunction(NULL);
With Swift things are a little different, since the compiler will complain about a type mismatch if we try to pass in nil or a nil pointer (NULL is unavailable in Swift). To solve this, we need to access another system function, _NSLogCStringFunction, to get a pointer to the default logging implementation, retain that reference while logging is disabled, and set the reference back when we want to turn logging back on.
I've cleaned up the Swift implementation of this by adding a NSLogCStringFunc typedef:
/// Represents the C function signature used under-the-hood by NSLog
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void
/// Sets the C function used by NSLog
#_silgen_name("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 #asmname has been renamed to #_silgen_name
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void
/// Retrieves the current C function used by NSLog
#_silgen_name("_NSLogCStringFunction")
func _NSLogCStringFunction() -> NSLogCStringFunc
let logFunc = _NSLogCStringFunction() // get function pointer to the system log function before we override it
_NSSetLogCStringFunction { (_, _, _) in } // set our own log function to do nothing in an anonymous closure
NSLog("Observe this isn't logged.");
_NSSetLogCStringFunction(logFunc) // switch back to the system log function
NSLog("Observe this is logged.")
iOS, transitioning to ARC. I've observed a curious behavior regarding CF/NS bridging. In the following scenario:
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = (__bridge NSString*)cfs;
the retain count of the string object is 2 at the end. However, in the following:
NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = ToNS(cfs);
the retain count is 3 at the end. What's going on, please? Who holds the extra reference? Is the object being added to the autorelease pool by the mere act of passing it around?
Preemptive response to "don't worry, ARC just works": I'm mixing Core Foundation with Cocoa here, no way around it. This is leak prone. Without the ability to account for the retain counts explicitly, I'm flying blind.
EDIT: it's an artifact of the debug build. In the release build, the retain count under the latter scenario is still 2.
There's a tangible difference between a fragment that leaves large autoreleased objects around and one that doesn't; you don't want the former in a big loop without a pool in the loop body. Helps to know it's an artifact of zero optimization, but still, not cool.
CFStringRef cfs = ComesFromSomewhere();
// retainCount -> 1
NSString *ns = ToNS(cfs);
// ToNS(cfs)
//
// ToNS is not object creating method,
// thus the returned object was automatically autoreleased
// retainCount += 1
// NSString *ns
//
// It's __strong variable, ns variable has an ownership of the object
// retainCount += 1
// retainCount -> 3
The definition of object creating method is a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, of a Objective-C class. See Basic Memory Management Rules - You own any object you create.
In the release build, the retain count under the latter scenario is still 2.
Also compiler can omit to send autorelease message to object if it's eligible.
EDITED
You can use C++ reference to avoid autorelease.
void ToNS(CFStringRef cfs, NSString __strong *& ns)
{
ns = (__bridge NSString*)cfs;
}
NSString *nsstr;
ToNS(cfstr, nsstr);
// retainCount -> 2
EDITTED
NS_RETURNS_RETAINED NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
NS_RETURNS_RETAINED makes the framework treat the function as an object creating one (which it really is). Cocoa has a name convention that lets you designate a method as an object creator, but the convention only applies to Objective C class methods, not to C style functions and not to C++ class member functions.
I'm currently playing with AudioQueue services, and I have a small problem.
The AudioQueue has a bunch of callbacks, each of which can carry a "user data", a pointer basically. I wish I could pass one of my objects as this pointer.
So what happens is that, on certain occasions, the AudioQueue is calling my callback in a way close to this:
static void HandleOutputBuffer (
void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
) {
MyPlayerData *mpd = (MyPlayerData *)aqData;
...
}
This usually works good, but when my player reaches the end of the media, it is deallocated. But often times the callback HandleOutputBuffer is called after the MyPlayerData object is deallocated, resulting in a nice segfault.
I wish I could use a weak reference for this. Is there any way I could have an ARC void * pointer? Something that would be set to nil whenever the object is deallocated?
I'm currently looking at __weak, but I'm not sure this is the right tool to use...
ARC cannot manage a void * reference to an object and set it to NULL if the object is
deallocated.
You have two different options when creating the context pointer from the object pointer:
const void *context = (__bridge void *)mpd;
This is what you do now. In this case, you have to ensure that the object exists
as long as the audio queue uses it in the callback.
const void *context = (__bridge_retained void *)mpd;
This increases the retain count of the object, so that the object is "kept alive",
i.e. it will not be deallocated.
In this case, you have to release this reference eventually when it is no longer needed,
with CFRelease(context).
Reference: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#bridged-casts