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
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.
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.
Is there a mechanism which would allow an object to know that a zeroing weak reference turned nil?
For example I have a property
#property (nonatomic, weak) MyClass *theObject;
when theObject deallocates and the property turns nil I want to get notified. But how? Does the zeroing weak reference system use the setter to set the property to nil when the object goes away?
The runtime just sets the weak ivar _theObect to nil, a custom setter is not called.
What you could do (if you really need the notification):
define a local "watcher" class and implement dealloc in that class,
create a watcher object and set it as "associated object" of _theObject.
When _theObject is deallocated, the associated object is released and deallocated (if there are no other strong refereces to it). Therefore its dealloc method is called. This is your "notification".
(I'm writing this on the phone and can fill in the details later if necessary.)
If you care when an object goes away, you shouldn't be using a weak reference. What are you trying to do?
There is no notification about object deallocation.
The system will not use setter method (this means no KVO notifications will be raised). The ivar is the real weak reference which gets zeroed. The weak keyword on a property is merely an instruction for synthesizing the ivar, and a public declaration that the object is not retained.
Though you can always invent your own notifications and send them from dealloc method of your classes, note that normally you should not ever be interested in such notifications and there is at least one good reason that they don't exist.
Whenever there is any kind of automatic memory management is in use, you can not (by definition) expect objects to die exactly when you need them to, that applies to Objective-C reference counting. Because any component may unexpectedly prolong lifetime of any object for unknown period of time, relying program behavior on assumption that dealloc will be called exactly when you need it to is bad design and a recipe for trouble. dealloc should be used for cleaning up only.
Try this rule of thumb: will the program still work correctly if dealloc does not get called at all? If not, you should rethink program's logic rather than sending out dealloc notifications.
I implemented this using a so-called weak reference registry, see the class BMWeakReferenceRegistry, part of my open source BMCommons framework for iOS.
This class associates context objects with the object of interest. When this object is released, so is the context object and the cleanup block is called.
See the API:
/**
* Registry for monitoring the deallocation of objects of interest to perform cleanup logic once they are released.
*/
#interface BMWeakReferenceRegistry : BMCoreObject
BM_DECLARE_DEFAULT_SINGLETON
/**
* Cleanup block definition
*/
typedef void(^BMWeakReferenceCleanupBlock)(void);
/**
* Registers a reference for monitoring with the supplied cleanup block.
* The cleanup block gets called once the reference object gets deallocated.
*
* It is possible to register the same reference multiple times with different cleanup blocks (even if owner is the same).
* If this is not intended behavior, check hasRegisteredReference:forOwner: before calling this method.
*
* #param reference The object to monitor
* #param owner An optional owner (may be specified to selectively deregister references)
* #param cleanup The cleanup block
*/
- (void)registerReference:(id)reference forOwner:(id)owner withCleanupBlock:(BMWeakReferenceCleanupBlock)cleanup;
/**
* Deregisters the specified reference for monitoring. If owner is not nil, only the monitor(s) for the specified owner is/are removed.
*
* #param reference The monitored reference
* #param owner The optional owner of the reference
*/
- (void)deregisterReference:(id)reference forOwner:(id)owner;
/**
* Checks whether a monitor already exists for the specified reference/owner. If the owner parameter is nil all owners are checked.
*
* #param reference The monitored reference
* #param owner The optional owner
* #return True if registered, false otherwise.
*/
- (BOOL)hasRegisteredReference:(id)reference forOwner:(id)owner;
#end
Based on Martin R's answer, I came up with the following snippet. Just make sure you don't create any retain cycles with your onDeinit closure!
private var key: UInt8 = 0
class WeakWatcher {
private var onDeinit: () -> ()
init(onDeinit: #escaping () -> ()) {
self.onDeinit = onDeinit
}
static func watch(_ obj: Any, onDeinit: #escaping () -> ()) {
watch(obj, key: &key, onDeinit: onDeinit)
}
static func watch(_ obj: Any, key: UnsafeRawPointer, onDeinit: #escaping () -> ()) {
objc_setAssociatedObject(obj, key, WeakWatcher(onDeinit: onDeinit), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
deinit {
self.onDeinit()
}
}
Call it like this when initializing your weak var:
self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })
there is no notification system for weak vars.
The following is an example that I used to implement multicast of delegates. It might be useful to illustrate how to monitor the 'dealloc' of weak referenced objects (the delegates).
There will be a master DelegateRef object. Its array keeps record of all delegateRefs which wrap the real delegates. The main purpose here is to remove the strong reference to delegateRefs kept by the array when the real delegates dealloc. Therefore, a local watch object is created and associated to delegate when adding the delegate. When the local watch dealloc, the delegateRef gets removed from the master DelegateRef's array.
#import <objc/runtime.h>
#interface WeakWatcher : NSObject
#property (nonatomic, weak) NSMutableArray *masterarray;
#property (nonatomic, weak) DelegateRef *delegateRef;
#end
#implementation WeakWatcher
-(void)dealloc
{ // when the object dealloc, this will be called
if(_delegateRef != nil)
{
if([self.masterarray containsObject:_delegateRef])
{
[_masterarray removeObject:_delegateRef];
}
}
}
#end
#interface DelegateRef()
#end
#implementation DelegateRef
static char assoKey[] = "assoKey";
- (NSMutableArray *)array {
if (_array == nil) {
_array = [NSMutableArray array];
}
return _array;
}
-(void)addWeakRef:(id)ref
{
if (ref == nil) {
return;
}
DelegateRef *delRef = [DelegateRef new];
WeakWatcher* watcher = [WeakWatcher new]; // create local variable
watcher.delegateRef = delRef;
watcher.masterarray = self.array;
[delRef setDelegateWeakReference:ref];
objc_setAssociatedObject(ref, assoKey, watcher, OBJC_ASSOCIATION_RETAIN);
[self.array addObject:delRef];
}
#end
Apple achieve this on UIPageViewController's weak dataSource property by use of their private _UIWeakHelper class but you can easily implement the same. In the setDataSource setter they create an instance of [_UIWeakHelper.alloc initWithDeallocationBlock:block] and the block calls self.dataSource = nil after the weak/strong dance to avoid a retain cycle. They then call objc_setAssociatedObject on the dataSource object setting the weak helper object. Finally in the _UIWeakHelper dealloc they call the deallocation block. It works because when the dataSource is dealloced the associated _UIWeakHelper will be too.
In case you were wondering why they need this, it is because when the dataSource deallocs they want to disable scrolling of the pages because there are no pages to scroll to.
Just don't make the same mistake Apple have made (as of iOS 13.4.1 Xcode 11.4.1) they associate the helper object to the same key for both the dataSource and the delegate so only one deallocation block is fired, doh!
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.