How to save IBOutlet property for release (iOS using ARC)

I am embedding the AQRecorder class from the Apple SpeakHere example in my project using ARC. To compile it, I needed to create a class (AQRecorderController) that manages the AQRecorder instance (equivalent to SpeakHereController in the example). AQRecorderController is connected through the tip of my main view controller and implemented as a property. The problem arises whether the property is strong or weak.

My problem is that shortly after loading the view controller, AQRecorderController is released, but only when testing on the device. this does not happen in the simulator. This happens for iPad and iPhone, iOS 5 and iOS 6. I need to keep this link for the entire life of my view controller for recording purposes (you cannot delete the recorder during recording and expect to receive a finished file).

Has anyone come across this or something similar? If the AQRecorderController property is strong, an attempt to use it raises an invalid access error, if it is weak, I just get zero and cannot be used.

Any help would be greatly appreciated.

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

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 creates an instance of the recorder.

AQRecorderController.mm:

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

It uses a recorder. At this point, the AQRecorderController has been released and this code is never executed (this causes a crash because the AQRecorderController has been released).

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

}

This is where the instance of AQRecorderController is created.

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
source share
3 answers

Now I have worked. I have not completely corrected it, but I can record without its failures. I commented on each line related to the AQRecorderController until it stopped being released, and then slowly added them back until I knew where it was going. It seems that the audio session setup code somehow provokes it to free the controller. This is the code that causes it (but there are no errors):

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

Until now, this was not necessary for my application to work, but I am curious why this will lead to the release of an instance of AQRecorderController.

0
source

My problem is that shortly after loading the view controller, AQRecorderController is released ... I need to keep this link for the duration of my view controller

Mark your property strong instead of weak . weak means that the object pointed to by aqRecorderController will not be held by the installer; strong will save it.

If the AQRecorderController property is strong, I get poor access when trying to use it, if it is weak, I just get zero and its unsuitability.

It looks like the property is being set by an invalid value somewhere in your program. Since you cannot manually save the object under ARC, and you weak property, it can be released very early. I'm not sure why you will have a problem if you mark it strong ... this will help you see the code in which you set the variable or property.

+3
source

You never set the AQRecorderController in your formViewController from what I see. You need to do self.aqRecorderController = aqRecorderController , I believe that it just disappears as soon as you leave the area in which you are creating the controller.

+1
source

Source: https://habr.com/ru/post/1441326/


All Articles