4

ARC を使用して、Apple の SpeakHere サンプルから AQRecorder クラスをプロジェクトに実装しています。コンパイルするには、AQRecorder インスタンス (例の SpeakHereController に相当) を制御するクラス (AQRecorderController) を作成する必要がありました。AQRecorderController はメイン ビュー コントローラーのペン先を介して接続され、プロパティとして実装されます。この問題は、プロパティが強いか弱いかに関係なく発生します。

私の問題は、View Controller をロードした直後に 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

私の問題は、View Controllerをロードした直後に、AQRecorderControllerが解放されることです...ViewControllerの存続期間を通じてこの参照を維持する必要があります

strongの代わりにプロパティをマークしますweakweakが指すオブジェクトaqRecorderControllerがセッターによって保持されないことを意味します。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 に答える