【问题标题】:Dereference pointer with ARC in C function in iOS在 iOS 中的 C 函数中使用 ARC 取消引用指针
【发布时间】:2013-07-18 02:56:02
【问题描述】:

我正在使用 The Amazing Audio Engine 来处理 iOS 应用中的同步播放。

该框架要求您使用 C 函数作为在音频线程上调用的回调 (playbackTimingReceiver)。然后,您需要使用向其传递处理程序 (pageTurnHandler) 的 C 函数 (AEAudioControllerSendAsynchronousMessageToMainThread) 再次向主线程发送消息。

我对使用 C 语言并没有过多的经验,但据我所知,我在消息中传递了一个需要取消引用的指针。

我可以通过这条线成功实现:

PlaybackManager* receiver = *((PlaybackManager**)userInfo);

但只有当我在项目目标的编译源中使用 -fno-objc-arc 标志为该文件关闭项目中的 ARC 时。

对于我的问题,是否可以在开启 ARC 的情况下实现这一点?如果是这样,正确的语法是什么?

相关代码段:

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

【问题讨论】:

    标签: ios objective-c c automatic-ref-counting dereference


    【解决方案1】:
    PlaybackManager * receiver = (__bridge_transfer id)*(void **)userInfo;
    

    应该可以解决问题。这首先将userInfo 转换为指向指针的指针,因为它包含原始对象指针的地址。取消引用以获取原始指针,并使用带有类型的__bridge_transfer——idPlaybackManager 将起作用——告诉 ARC 取消引用的值实际上是一个需要处理的对象。

    【讨论】:

    • 它不是取消引用 void 指针,而是取消引用指向指针的指针。 void **
    • 谢谢...停止了编译器的抱怨,但在运行时我得到了错误的访问:线程 1:EXE_BAD_ACCESS (code=1, address=0x30000008)
    • register read rdi -> 是否在调试器中产生地址po (id)<the address>,@Brendt?还有register read rsip <that address>?假设你在objc_msg_send() 中崩溃了。
    【解决方案2】:

    没有运行代码似乎有两个错误:

    1) 当您看起来像是要传递 receiver 的内容时,您正在传递 audioController 的内容 - 所以 AEAudioControllerSendAsynchronousMessageToMainThread 的最后两个参数应该是 &receiversizeof(PlaykbackManager *)

    2) 你需要一个桥接来恢复对象引用

    类似:

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

    注意:当将对象引用从 ARC 控制的世界传递到 C 世界时,您通常会在传入的过程中转移所有权 - 因此 ARC 不会释放被引用的对象 - 并在返回的过程中将所有权转移回 - 所以 ARC 会恢复所有权管理。然而,由于AEAudioControllerSendAsynchronousMessageToMainThread 的性质,userInfo 是通过地址传递并在内部复制的——因此使用 size 参数,转移所有权会比较棘手。因此上面的代码没有。这意味着您必须确保任何对象 receiver 引用通过拥有另一个所有者来保持活动状态。

    【讨论】:

      【解决方案3】:

      您可以告诉 ARC 您想要的存储类型:

      PlaybackManager *audioBufferPlayer = *(__weak PlaybackManager **)userInfo;

      请务必在访问任何属性或调用任何方法之前进行必要的 nil 检查。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-23
        • 2020-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-06
        相关资源
        最近更新 更多