I have an AudioRecorder class for recording audio, and I have the following code:
_recorder = [[AVAudioRecorder alloc] initWithURL:audioFileURL settings:recordSettings error:&error];
if (_recorder && [_recorder prepareToRecord]) {
[_recorder recordForDuration:10.0]; // Record for 10 seconds
[_recorder setDelegate:self];
[_recorder setMeteringEnabled:YES];
}
So, here self is the AudioRecorder class which is declare as:
#interface AudioRecorder : NSObject <AVAudioRecorderDelegate>
#property AVAudioRecorder *recorder;
#property AVAudioSession *session;
#end
Then I have this callback method that should (but is not) called after the 10 seconds registration:
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {}
Why is this not called after the 10 sec registration?
There are two common reason which make delegate methods not being called.
The first one is not setting the delegate property or implementing a wrong method (ie, typo on the method).
Which is not the case here.
The second one, is that the object is released (from memory) too soon.
In other words, the delegate method will be called later (here ~10 seconds after), and delegate properties are almost always weakly reference to avoid memory issue (cycle), when the object need to call its delegate method, the object doesn't exist anymore. That's your issue.
The small difference here, it's that's the parent of the delegate that is released too soon.
Because _recorder is a property of AudioRecorder, so as long as the AudioRecorder instance exists, it will still be here and let the delegate method be called.
But, the AudioRecorder is a local variable, so it will cease to exists after its scope (~after the next closing }).
So you need to make AudioRecorder a property of the object holding it.
Related
I am developing a project on iOS 7 using ARC, I want to release a private property when the viewController is released
Here is the TestViewController that is presented as a modal view controller, setting a value to the private property testAVPlayer in viewDidLoad:
//TestViewController.m
#import "TestAVPlayer.h"
#interface TestViewController () {
TestAVPlayer *testAVPlayer;
}
#end
- (void)viewDidLoad
{
[self setupPlayer];
}
- (void)setupPlayer {
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"music" withExtension:#"mp3"]];
testAVPlayer = [TestAVPlayer playerWithPlayerItem:item];
[testAVPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
[testAVPlayer play];
}
- (void)dealloc {
NSLog(#"dealloc TestViewController: %#", self);
}
TestAVPlayer is a subclass of AVPlayer, I put a NSLog into the dealloc
// TestAVPlayer.h
#import <AVFoundation/AVFoundation.h>
#interface TestAVPlayer : AVPlayer
#end
// TestAVPlayer.m
#import "TestAVPlayer.h"
#implementation TestAVPlayer
- (void)dealloc {
NSLog(#"dealloc testAVPlayer: %#", self);
}
#end
When TestViewController is dismissed, the testAVPlayer seems never be released, I see the "dealloc TestViewController", but there is no "dealloc testAVPlayer" in console log
I tried your code, the problem is that even if you call [TestAVPlayer playerWithPlayerItem:item] the TestAVPlayer class doesn't have such method, so it will call playerWithPlayerItem: function from the AVPlayer base class, which will return an instance of the AVPlayer class instead of the TestAVPlayer class. The compiler won't give you any warning because the playerWithPlayerItem: method returns a type of id. If you check this with the debugger you'll see that the private variable's type is not TestAVPlayer:
The dealloc of the TestAVPlayer will never be called as no such object was created.
The AVPlayer instance gets deallocated when the TestViewController is deallocated. You can check this by using Instruments or simply adding a Symbolic Breakpoint to [AVPlayer dealloc].
Select the Breakpoint Navigator and click on the + button and add a Symbolic Breakpoint.
Write [AVPLayer dealloc] to the Symbol field and press Enter. When you run the application and the TestViewController gets deallocated then you will see that the breakpoint will be hit, hence the AVPlayer really gets deallocated.
You are using a class factory method to initialize your object, which means that you do not own the testAVPlayer object and thus are not responsible for releasing it.
See Class Factory Methods from the Concepts in Objective-C Programming guide for more details.
If you indeed want to own and control the lifetime of this object, use the following initializer:
testAVPlayer = [[TestAVPlayer alloc] initWithPlayerItem:item];
and your dealloc method will be called.
testAVPlayer = [AVPlayer playerWithPlayerItem:playerItem];
You are using AVPlayer, not your TestAVPlayer.
Try implementing - viewDidUnload then nil the testAVPlayer:
- (void) viewDidUnload
{
[super viewDidUnload];
testAVPlayer = nil;
}
Although you are calling it as [TestAVPlayer playerWithPlayerItem:item], you are really getting back an instance of AVPlayer, not TestAVPlayer. In fact, the AVPlayer instance you created really IS getting deallocated. You won't be able to see your log in dealloc because an instance of that class is never created.
As suggested by another, replace [TestAVPlayer playerWithPlayerItem:item] with [[TestAVPlayer alloc] initWithPlayerItem:item]; and you should start seeing your logs.
I have a TableViewController which when run, makes an instance of another class and calls json with it eg.
TableViewController;
-(void)viewDidLoad{
JSONClass *jc = [[JSONClass alloc]init];
jc.JSONClassDelegate = (id)self;
[jc view];
}
JSONClass will proceed to retrieve data from the web and once done, will send a delegate method call "JSONClassDataReceived" to tableViewController. Such as,
JSONClass;
-(void)viewDidLoad{
//codes of URL connection goes here...
NSMutableURLRequest *request = [[NSMutableURLRequest new];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:responseData waitUntilDone:YES];
}
- (void)fetchedData:(NSData *)responseData {
NSMutableDictionary *data = [NSJSONSerialization
JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:&error];
if (JSONPromotionsLocationsDelegate && [JSONPromotionsLocationsDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONPromotionsLocationsDelegate JSONPromotionsLocationsDataReceived];
}
}
TableViewController;
- (void)JSONClassDataReceived{
[tableView reloadTable];
}
After which relevant data is populated.
How do I stop JSONClass when back button is pressed on TableViewController before the delegate method JSONClassDataReceived is called on my tableViewController?
I tried
jc.JSONClassDelegate = nil;
jc = nil;
when back button is pressed, but my app crashes because JSONClass has reached JSONClassDelegate and thus cannot find the method - (void)JSONClassDataReceived due to the fact that tableViewController view no longer exist. I have also tried implement dealloc in JSONClass. None seem to work.
- (void)dealloc{
self.view = nil;
}
I have having the error EXC_BAD_ACCESS on the lines,
if (JSONPromotionsLocationsDelegate && [JSONPromotionsLocationsDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONPromotionsLocationsDelegate JSONPromotionsLocationsDataReceived];
}
I seem to have fixed the crash by simply setting my delegate to weak instead of assign.
crash
#property (nonatomic, assign)id <JSONClassDelegate> JSONClassDelegate;
no crash
#property (nonatomic, weak)id <JSONClassDelegate> JSONClassDelegate;
When posting a question about a crash to Stack Overflow, it's helpful to include the crash log or stack trace to empower your peers to help you solve the problem.
The correct way to troubleshoot an EXC_BAD_ACCESS crash is to use the Zombies instrument of Instruments. This will tell you exactly what is causing this problem.
Without that information, we can only guess.
But in your case, I can take an educated guess.
Your delegate is being deallocated at some point, and after that you are attempting to use the deallocated memory. This is most likely because you have not declared the delegate as a weak reference. This is specifically mentioned at several points in the documentation.
First, declare your delegate as a weak property.
#property (nonatomic, weak) id JSONClassDelegate;
Don't forget to synthesize it. Not synthesizing properties can lead to bad things.
Now in the methods where you call your delegate you should make it a strong reference for the duration of the method. This prevents it from being deallocated while you are using it. For example:
__strong id aDelegate = [self JSONClassDelegate];
if ([JSONClassDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONClassDelegate JSONPromotionsLocationsDataReceived];
}
This creates a strong local stack variable that points to your weak reference. It will be retained until it goes out of scope.
First, make sure that before calling your delegate, you do this:
if (self.delegate && [self.delegate respondsToSelector:#selector(JSONClassDataReceived)]) {
[self.delegate JSONClassDataReceived];
}
Other than that, in your JSONClass' - (void)dealloc method, you can stop the web call. If you're using ARC, make sure you don't call [super dealloc]. For the dealloc method to be called, you'll need to keep nulling out your jc object.
Hope this helps.
No, you can't (usefully) "test if an address contains a valid object". Even if you were able to grub around inside the internals of the memory allocation system and determine that your address points to a valid object, that would not necessarily mean that it was the same object that you were previously referring to: the object could have been deallocated and another object created at the same memory address.
Found from here.
I have declared property
#property(nonatomic, strong) AVQueuePlayer *player;
and when I initialize it with + queuePlayerWithItems: it is not deallocated if I assign to player new object or nil. Even if I do it right after one row below. When I initialize player with – initWithItems: everything works as expected. I use ARC. Is it bug or are not static initializers autoreleasing or what is the difference? I remember the times before ARC when it would be like
+ (AVQueuePlayer *)queuePlayerWithItems:(NSArray *)items
{
return [[[AVQueuePlayer alloc] initWithItems:items] autorelease];
}
So what is the matter?
With ARC your code may like look as this:
+ (AVQueuePlayer *)queuePlayerWithItems:(NSArray *)items
{
return [[AVQueuePlayer alloc] initWithItems:items];
}
The compiler may still put the created object into an autorelease pool, though. Whether or not this happens also depends on the level of optimization set, and whether the method family is correctly recognized.
You can help the compiler declaring the method like this:
+ (AVQueuePlayer *)queuePlayerWithItems:(NSArray *)items NS_RETURNS_RETAINED;
This will cause the compiler to assume that the method returns an allocated object and needs to be sent a balanced release message when appropriate, instead to put the object into the autorelease pool.
NS_RETURNS_RETAINED will expand to __attribute__((ns_returns_retained))
The definition should omit the attribute NS_RETURNS_RETAINED.
Edit:
If this is not your method and if you cannot change the declaration, you may use an explicit autorelease pool to get rid of the autoreleased object:
{
AVQueuePlayer* player;
#autoreleasepool {
// the next line retains the returned object:
player = [AVQueuePlayer queuePlayerWithItems:items];
} // here, the autorelease pool releases the returned object (it's still alive)
// do something with variable player:
...
[player ...]; // last usage of variable player
// now, the last reference to player ceases to exist, it gets released and deallocated
}
I've wrote a class which gets an image from the camera. Its header is as follows:
typedef void(^ImageTakenCallback)(UIImage *image);
#interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
UIImagePickerController *picker;
ImageTakenCallback completionBlock
}
-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;
#end
As you can see, I'm trying to make something like that in client code:
[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
}];
Here is how I've implemented ImageGetter:
-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
completionBlock = [completion copy];
picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.delegate = self;
[view addSubview:picker.view];
}
- (void)imagePickerController:(UIImagePickerController *)picker_
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
[picker.view removeFromSuperview];
picker = nil;
completionBlock(image);
}
The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.
Which are common ways to resolve such a issue?
I can see some ways, however, none of them seems to be quite right:
retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?
So how can I build such a class?
You can handle that within the ImageGetter class by creating and releasing a "self-reference".
In a class extension in the implementation file, declare a property
#interface ImageGetter ()
#property (strong, nonatomic) id selfRef;
#end
In requestImageInView:, set self.selfRef = self to prevent deallocation.
In the completion method, set self.selfRef = nil.
Remark: Actually you can manage the retain count even with ARC:
CFRetain((__bridge CFTypeRef)(self)); // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self)); // Decreases the retain count.
But I am not sure if this is considered "good programming" with ARC or not.
Any feedback is welcome!
If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.
However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.
You can use the following strategy:
ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
[imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];
Inside your ImageGetter implementation class, create a method that you can call inside the block like this.
-(void) releaseCompletionBlock
{
completionBlock = nil;
}
I need to perform an action in the dealloc method of a category. I've tried swizzling but that doesn't work (nor is it a great idea).
In case anyone asks, the answer is no, I can't use a subclass, this is specifically for a category.
I want to perform an action on delay using [NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:] or [self performSelector:withObject:afterDelay:] and cancel it on dealloc.
The first issue is that NSTimer retains the target, which I don't want. [self performSelector:withObject:afterDelay:] doesn't retain, but I need to be able to call [NSObject cancelPreviousPerformRequestsWithTarget:selector:object:] in the dealloc method or we get a crash.
Any suggestions how to do this on a category?
I still think it would be better to subclass your class and not mess with the runtime, but if you are definitely sure you need to do it in a category, I have an option in mind for you. It still messes with the runtime, but is safer than swizzling I think.
Consider writing a helper class, say calling it DeallocHook which can be attached to any NSObject and perform an action when this NSObject gets deallocated. Then you can do something like this:
// Instead of directly messing with your class -dealloc method, attach
// the hook to your instance and do the cleanup in the callback
[DeallocHook attachTo: yourObject
callback: ^{ [NSObject cancelPrevious... /* your code here */ ]; }];
You can implement the DeallocHook using objc_setAssociatedObject:
#interface DeallocHook : NSObject
#property (copy, nonatomic) dispatch_block_t callback;
+ (id) attachTo: (id) target callback: (dispatch_block_t) block;
#end
Implementation would be something like this:
#import "DeallocHook.h"
#import <objc/runtime.h>
// Address of a static global var can be used as a key
static void *kDeallocHookAssociation = &kDeallocHookAssociation;
#implementation DeallocHook
+ (id) attachTo: (id) target callback: (dispatch_block_t) block
{
DeallocHook *hook = [[DeallocHook alloc] initWithCallback: block];
// The trick is that associations are released when your target
// object gets deallocated, so our DeallocHook object will get
// deallocated right after your object
objc_setAssociatedObject(target, kDeallocHookAssociation, hook, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return hook;
}
- (id) initWithCallback: (dispatch_block_t) block
{
self = [super init];
if (self != nil)
{
// Here we just copy the callback for later
self.callback = block;
}
return self;
}
- (void) dealloc
{
// And we place our callback within the -dealloc method
// of your helper class.
if (self.callback != nil)
dispatch_async(dispatch_get_main_queue(), self.callback);
}
#end
See Apple's documentation on Objective-C runtime for more info about the associative references (although I'd say the docs are not very detailed regarding this subject).
I've not tested this thoroughly, but it seemed to work. Just thought I'd give you another direction to look into.
I just stumbled on a solution to this that I haven't seen before, and seems to work...
I have a category that--as one often does--needs some state variables, so I use objc_setAssociatedObject, like this:
Memento *m = [[[Memento alloc] init] autorelease];
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And, I needed to know when the instances my category extending were being dealloced. In my case it's because I set observers on self, and have to remove those observers at some point, otherwise I get the NSKVODeallocateBreak leak warnings, which could lead to bad stuff.
Suddenly it dawned on me, since my associated objects were being retained (because of using OBJC_ASSOCIATION_RETAIN_NONATOMIC), they must be being released also, and therefore being dealloced...in fact I had implemented a dealloc method in the simple storage class I had created for storing my state values. And, I postulated: my associated objects must be dealloced before my category's instances are! So, I can have my associated objects notify their owners when they realize they are being dealloced! Since I already had my retained associated objects, I just had to add an owner property (which is not specified as retain!), set the owner, and then call some method on the owner in the associated object's dealloc method.
Here's a modified part of my category's .m file, with the relevant bits:
#import <objc/runtime.h> // So we can use objc_setAssociatedObject, etc.
#import "TargetClass+Category.h"
#interface TargetClass_CategoryMemento : NSObject
{
GLfloat *_coef;
}
#property (nonatomic) GLfloat *coef;
#property (nonatomic, assign) id owner;
#end
#implementation TargetClass_CategoryMemento
-(id)init {
if (self=[super init]) {
_coef = (GLfloat *)malloc(sizeof(GLfloat) * 15);
}
return self;
};
-(void)dealloc {
free(_coef);
if (_owner != nil
&& [_owner respondsToSelector:#selector(associatedObjectReportsDealloc)]) {
[_owner associatedObjectReportsDealloc];
}
[super dealloc];
}
#end
#implementation TargetClass (Category)
static NSString *kMementoTagKey = #"TargetClass+Category_MementoTagKey";
-(TargetClass_CategoryMemento *)TargetClass_CategoryGetMemento
{
TargetClass_CategoryMemento *m = objc_getAssociatedObject(self, kMementoTagKey);
if (m) {
return m;
}
// else
m = [[[TargetClass_CategoryMemento alloc] init] autorelease];
m.owner = self; // so we can let the owner know when we dealloc!
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return m;
}
-(void) doStuff
{
CCSprite_BlurableMemento *m = [self CCSprite_BlurableGetMemento];
// do stuff you needed a category for, and store state values in m
}
-(void) associatedObjectReportsDealloc
{
NSLog(#"My associated object is being dealloced!");
// do stuff you need to do when your category instances are dealloced!
}
#end
The pattern here I learned somewhere (probably on S.O.) uses a factory method to get or create a memento object. Now it sets the owner on the memento, and the memento's dealloc method calls back to let the owner know it's being dealloced
CAVEATS:
Obviously, you have to have your associated object set with OBJC_ASSOCIATION_RETAIN_NONATOMIC, or it won't be retained and released for you automatically.
This becomes trickier if your memento/state associated object gets dealloced under other circumstances than the owner being dealloced...but you can probably train one object or the other to ignore that event.
The owner property can't be declared as retain, or you'll truly create a strong reference loop and neither object will ever qualify to be dealloced!
I don't know that it's documented that OBJC_ASSOCIATION_RETAIN_NONATOMIC associated objects are necessarily released before the owner is completely dealloced, but it seems to happen that way and almost must be the case, intuitively at least.
I don't know if associatedObjectReportsDealloc will be called before or after the TargetClass's dealloc method--this could be important! If it runs afterwards, if you try to access member objects of the TargetClass you will crash! And my guess is that it's afterwards.
This is a little messy, because you're double-linking your objects, which requires you to be very careful to keep those references straight. But, it doesn't involve swizzling, or other interference with the runtime--this just relies on a certain behavior of the runtime. Seems like a handy solution if you already have an associated object. In some cases it might be worth creating one just to catch your own deallocs!
Your proposed solution unfortunately won't work: because NSTimer retains its target, the target will never run its dealloc until the timer has been invalidated. The target's retain count will always be hovering at 1 or above, waiting for the timer to release it. You have to get to the timer before dealloc. (Pre-ARC, you could override retain and release and destroy the timer, although that's really not a good solution.)
NSThread also has this problem, and the solution is simple: a bit of redesigning separates the controller of the thread from the "model". The object which creates and owns the thread, or timer in this case, should not also be the target of the timer. Then, instead of the retain cycle you currently have (timer owns object which owns timer), you have a nice straight line: controller owns timer which owns target. Outside objects only need to interact with the controller: when it is deallocated, it can shut down the timer without you having to play games with overriding dealloc or other memory management methods.
That's the best way to handle this. In the case that you can't do that for some reason -- you're talking about category overrides, so apparently you don't have the code for the class which is the target of the timer (but you can still probably make a controller even in that case) -- you can use weak references. Unfortunately I don't know any way to make an NSTimer take a weak reference to its target, but GCD will give you a fair approximation via dispatch_after(). Get a weak reference to the target and use that exclusively in the Block you pass. The Block will not retain the object through the weak reference (the way NSTimer would), and the weak reference will of course be nil if the object has been deallocated before the Block runs, so you can safely write whatever message sends you like.