4

我正在使用 ARC 将 Apple 的 SpeakHere 示例中的 AQRecorder 类实现到我的项目中。为了让它编译,我必须创建一个类 (AQRecorderController) 来控制 AQRecorder 实例(相当于示例中的 SpeakHereController)。AQRecorderController 通过我的主视图控制器的 nib 连接并作为属性实现。无论属性是强还是弱,都会出现问题。

我的问题是加载视图控制器后不久,AQRecorderController 被释放,但仅在设备上测试时。在模拟器中,这不会发生。它发生在 iPad 和 iPhone、iOS 5 和 iOS 6 上。出于录制目的,我需要在视图控制器的整个生命周期中维护此引用(您不能在录制时删除录制器并期望有一个完成的文件)。

有没有人遇到过这个或类似的事情?如果 AQRecorderController 属性很强,我在尝试使用它时会遇到错误的访问错误,如果它很弱,我只会得到一个 nil,并且它无法使用。

任何帮助将不胜感激。

formViewController.h:

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@class AQRecorderController;

@interface formViewController : UIViewController <UIActionSheetDelegate,     UITableViewDelegate, UIGestureRecognizerDelegate> {

    IBOutlet AQRecorderController *aqRecorderController;
}

@property (nonatomic, weak)     IBOutlet AQRecorderController *aqRecorderController;

@end

AQRecorderController.h

#import <Foundation/Foundation.h>
#import "AQRecorder.h"

@interface AQRecorderController : NSObject
{
    AQRecorder *aqRecorder;
}

@property (readonly)            AQRecorder* aqRecorder;
@property (nonatomic, assign)   bool        isRecording;
@property (nonatomic, strong)   NSString*   fileName;

-(bool)startRecording;
-(bool)pauseRecording;
-(bool)stopRecording;
-(bool)initializeRecordSettingsWithCompression:(bool)compressionEnabled;
@end

formView.xib: 录音笔尖

这是 AQRecorderController 释放后的堆栈跟踪:

 2012-10-23 10:34:09.600 TestApp[510:907] (
       0   TestApp                             0x000f32ab
-[AQRecorderController dealloc] + 138
        1   CoreFoundation                      0x32247311 CFRelease + 100
        2   CoreFoundation                      0x3225195d <redacted> + 140
        3   libobjc.A.dylib                     0x31ad5489 <redacted> + 168
        4   CoreFoundation                      0x32249441 _CFAutoreleasePoolPop + 16
        5   Foundation                          0x37303a7f <redacted> + 466
        6   CoreFoundation                      0x322db5df <redacted> + 14
        7   CoreFoundation                      0x322db291 <redacted> + 272
        8   CoreFoundation                      0x322d9f01 <redacted> + 1232
        9   CoreFoundation                      0x3224cebd CFRunLoopRunSpecific + 356
        10  CoreFoundation                      0x3224cd49 CFRunLoopRunInMode + 104
        11  GraphicsServices                    0x32fb52eb GSEventRunModal + 74
        12  UIKit                               0x34e92301 UIApplicationMain + 1120
        13  TestApp                             0x00081a9d main + 48
        14  TestApp                             0x0005aa68 start + 40
)

这是记录器实例化的地方。

AQRecorderController.mm:

- (void)awakeFromNib
{
    aqRecorder = new AQRecorder();
}

这是使用录音机的地方。至此,AQRecorderController 已被释放,并且此代码永远不会执行(它会导致崩溃,因为 AQRecorderController 已被释放)。

-(bool)startRecording
{
    if (aqRecorder->IsRunning())
    {
            [self stopRecording];
    }
    else // If we're not recording, start.
    {
    @try
    {
        // Start the recorder
        CFStringRef filenameString = (CFStringRef)CFBridgingRetain(self.fileName);
        aqRecorder->StartRecord(filenameString);
    }
    @catch(NSException *ex)
    {
        NSLog(@"Error: %@", [ex description]);
        return NO;
    }
            [self setFileDescriptionForFormat:aqRecorder->DataFormat() withName:@"Recorded File"];
    }

[self checkIfRecording];

return YES;

}

这里是 AQRecorderController 实例化的地方。

formViewController.mm:

//this is called in viewDidAppear
-(void)initializeAQRecorder: (NSString*)soundFileName
{
    aqRecorderController = [[AQRecorderController alloc] init];


    NSLog(@"AQRecorderController is being initialized for file %@",soundFileName);
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDir = [documentPaths objectAtIndex:0];
    NSString *soundFilePath =[[NSString alloc] initWithFormat:@"%@",[documentsDir stringByAppendingPathComponent:soundFileName]];

    [aqRecorderController setFileName:soundFilePath];
    [aqRecorderController initializeRecordSettingsWithCompression:NO];

}
4

3 回答 3

3

我的问题是,加载视图控制器后不久,AQRecorderController 被释放......我需要在视图控制器的整个生命周期中维护这个引用

标记您的财产strong,而不是weak. weak表示 指向的对象aqRecorderController不会被 setter 保留;strong将导致它被保留。

如果 AQRecorderController 属性很强,我在尝试使用它时会遇到错误的访问错误,如果它很弱,我只会得到一个 nil,并且它无法使用。

这听起来像是在您的程序中某处将属性设置为某个无效值。由于您无法在 ARC 下手动保留该对象并且您已标记了该属性weak,因此它可能会在很早的时候发布。我不确定如果标记它为什么会出现问题strong……查看设置变量或属性的代码会有所帮助。

于 2012-10-22T21:42:35.140 回答
1

从我所看到的,您永远不会将其设置AQRecorderController为您的。formViewController您需要这样做self.aqRecorderController = aqRecorderController,我相信一旦您离开创建控制器的范围,它就会消失。

于 2012-10-23T20:55:34.350 回答
0

我现在让它工作。我还没有完全修复它,但我可以录制而不会崩溃。我注释掉了与 AQRecorderController 相关的每一行,直到它停止发布,然后慢慢将它们添加回来,直到我发现它发生在哪里。看起来音频会话设置代码以某种方式促使它释放控制器。这是导致它的代码(但这里没有抛出错误):

来自 AQRecorderController.mm:

-(void)initializeRecordSettingsWithCompression:(bool)compressionEnabled
{

    OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, (__bridge void*)self);
    if (error) printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error);
    else
    {
            UInt32 category = kAudioSessionCategory_PlayAndRecord;
            error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
            if (error) printf("couldn't set audio category!");

            error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*)self);
            if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);
            UInt32 inputAvailable = 0;
            UInt32 size = sizeof(inputAvailable);

            // we do not want to allow recording if input is not available
            error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
            if (error) printf("ERROR GETTING INPUT AVAILABILITY! %d\n", (int)error);

            // we also need to listen to see if input availability changes
            error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*)self);
            if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);

            error = AudioSessionSetActive(true);
            if (error) printf("AudioSessionSetActive (true) failed");
    }
}

到目前为止,这对于我的应用程序的运行并不是必需的,但我很好奇它为什么会导致 AQRecorderController 实例释放。

于 2012-10-23T21:44:11.553 回答