简体   繁体   中英

How to keep IBOutlet property from being released (iOS using ARC)

I am implementing the AQRecorder class from Apple's SpeakHere example into my project using ARC. To get it to compile, I had to create a class (AQRecorderController) that controls the AQRecorder instance (equivalent to the SpeakHereController in the example). AQRecorderController is connected through the nib of my main view controller and implemented as a property. The problem occurs whether or not the property is strong or weak.

My problem is that shortly after loading the view controller, the AQRecorderController is released, but only when tested on device. In the simulator, this does not occur. It occurs for iPad and iPhone, iOS 5 and iOS 6. I need to maintain this reference throughout the lifetime of my view controller for recording purposes (you can't delete the recorder while recording and expect to have a finished file).

Has anyone run into this or anything similar? If the AQRecorderController property is strong, I get a bad access error when trying to use it, if its weak, I just get a nil, and its unusable.

Any help would be greatly appreciated.

#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

#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

录音机笔尖

Here is the stack trace after the AQRecorderController has been released:

 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
)

This is where the recorder is instantiated.

AQRecorderController.mm:

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

This is where the recorder is used. By this point, the AQRecorderController has been released and this code never executes (it causes a crash, because the AQRecorderController has been deallocated).

-(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;

}

Here is where the AQRecorderController is instantiated.

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

}

My problem is that shortly after loading the view controller, the AQRecorderController is released...I need to maintain this reference throughout the lifetime of my view controller

Mark your property strong instead of weak . weak means that the object pointed to by aqRecorderController won't be retained by the setter; strong will cause it to be retained.

If the AQRecorderController property is strong, I get a bad access error when trying to use it, if its weak, I just get a nil, and its unusable.

That sounds like the property is being set to some invalid value somewhere in your program. Since you can't manually retain the object under ARC and you've marked the property weak , it may be released very early on. I'm not sure why you'd have a problem if you mark it strong ... it'd help to see the code where you set the variable or property.

You're never setting the AQRecorderController to your formViewController from what I see. You need to do self.aqRecorderController = aqRecorderController , I believe it's just disappearing as soon as you leave the scope where you create the controller.

I got it working for now. I haven't completely fixed it, but I can record without it crashing. I commented out every line having to do with AQRecorderController until it stopped being released, then slowly added them back until I found out where it happens. It looks like the audio session setup code somehow provokes it to release the controller. This is the code that causes it (but no errors are thrown here):

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

So far, this isn't necessary for the functioning of my app, but I am curious as to why it would cause the AQRecorderController instance to release.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM