如何防止IBOutlet属性被释放(iOS使用ARC)

时间:2012-10-22 16:12:26

标签: ios automatic-ref-counting iboutlet

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

我的问题是,在加载视图控制器后不久,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: recorder nib http://i46.tinypic.com/v4qfrq.png

这是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];

}

3 个答案:

答案 0 :(得分:3)

  

我的问题是加载视图控制器后不久,   AQRecorderController已发布......我需要维护此参考   在我的视图控制器的整个生命周期中

标记您的媒体资源strong而不是weakweak表示aqRecorderController指向的对象不会被setter保留; strong会导致它被保留。

  

如果AQRecorderController属性很强,我的访问权限很差   尝试使用它时出错,如果它很弱,我只是得到一个零,而且它无法使用。

听起来这个属性在你的程序中的某个地方被设置为某个无效值。由于您无法在ARC 下手动保留对象,因此您已标记了属性weak,因此可能很早就会将其释放。我不确定为什么如果你标记它strong就会出现问题...它有助于查看设置变量或属性的代码。

答案 1 :(得分:1)

您永远不会从我看到的AQRecorderController设置formViewController。您需要执行self.aqRecorderController = aqRecorderController,我相信只要您离开创建控制器的范围,它就会消失。

答案 2 :(得分: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实例发布。