Loading presentModalViewController in ViewDidAppear causes EXC_BAD_ACCESS - objective-c

In the Follwoing ViewControllerClass I get EXC_BAD_ACCESS when trying to call presentModalViewController in ViewDidAppear method.
#import "SounderViewController.h"
#import "ASIFormDataRequest.h"
#import "ASIHTTPRequest.h"
#import "JSON.h"
#import "InfoViewController.h"
#implementation SounderViewController
#synthesize ipod;
#synthesize ivc;
#synthesize title_lb, artist_lb, check;
-(IBAction)showCurrentSongInfo{
MPMediaItem * song = [ipod nowPlayingItem];
NSString * title = [song valueForProperty:MPMediaItemPropertyTitle];
NSString * artist = [song valueForProperty:MPMediaItemPropertyArtist];
title_lb.text = title;
artist_lb.text = artist;
}
-(void)playbackStateChanged: (NSNotification*) notification{
[self showCurrentSongInfo];
NSLog(#"Playback state: %#",[notification name]);
if (ipod.playbackState != MPMusicPlaybackStatePlaying) {
NSLog(#"Is not playing");
[self presentModalViewController:self.ivc animated:YES];
}else if (ipod.playbackState == MPMusicPlaybackStatePlaying) {
NSLog(#"Is playing");
[self dismissModalViewControllerAnimated:YES];
}
}
-(void)nowPlayingItemChanged: (NSNotification*) notification{
[self showCurrentSongInfo];
NSLog(#"Playing item changed: %#",[notification name]);
}
- (void)viewDidLoad {
[super viewDidLoad];
self.ivc = [[InfoViewController alloc] initWithNibName:#"InfoViewController" bundle:nil];
self.ipod = [MPMusicPlayerController iPodMusicPlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (playbackStateChanged:)
name:#"MPMusicPlayerControllerPlaybackStateDidChangeNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (nowPlayingItemChanged:)
name:#"MPMusicPlayerControllerNowPlayingItemDidChangeNotification"
object:nil];
[[MPMusicPlayerController iPodMusicPlayer] beginGeneratingPlaybackNotifications];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (ipod.playbackState != MPMusicPlaybackStatePlaying) {
[self presentModalViewController:self.ivc animated:YES];
}else{
[self showCurrentSongInfo];
}
}
-(IBAction)showInfoView{
[self presentModalViewController:self.ivc animated:YES];
}
#pragma mark View Methods
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Method call
[self presentModalViewController:self.ivc animated:YES];
in ViewDidAppear causes EXC_BAD_ACCESS.
I have tried debuging it with NSZombieEnabled but got only a stack call to main.
The thing that gets me crazy is that if the same code is run from method playbackStateChanged it works fine.
If any of you can help I wan't get bold as quick. Thanks.

I finally made it to work! But I think it's just quick fix.
So I found out that to make my ivc to show up I need to delay the call to presentModalViewController
[self performSelector:#selector(showWaitingMessageView:) withObject:self.ivc afterDelay:1];
That's it. It works.
I don't know why this helped, so if one of you gurus knows more about it please enlighten me.

Related

iAds still refuse to work

Edit: Here's my AppDelegate as well (part of it)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
GameViewController *gameViewController = [[GameViewController alloc]init];
NSLog(#"NSLOG %#", [[gameViewController view]class]);
_bannerViewController = [[BannerViewController alloc]initWithContentViewController:gameViewController];
self.window.rootViewController = _bannerViewController;
[self.window makeKeyAndVisible];
return YES;
}
I am about to give up. I have tried 5 different ways just these past few days to get literally one single iAd to show correctly and as simple as Apple makes it seem, literally 100% of the time I either see no ad or get an error. I have followed the Apple documentation EXACTLY.
The only clue I have is in these two lines
GameViewController *gameViewController = [[GameViewController alloc]init];
NSLog(#"NSLOG %#", [[gameViewController view]class]);
Which are in my app delegate. The NSLog gives me "UIView". No. Why? Why would that ever be a UIView, it should be an SKView, because GameViewController was pre-written for me by apple for sprite kit. How could that possibly give me the wrong object?
I am getting 'NSInvalidArgumentException', reason: '-[UIView scene]: unrecognized selector sent to instance 0x174191780' which others have recommended to fix by putting the originalContent statement but I already have that and it isn't working.
Banner view controller:
#import "BannerViewController.h"
NSString * const BannerViewActionWillBegin = #"BannerViewActionWillBegin";
NSString * const BannerViewActionDidFinish = #"BannerViewActionDidFinish";
#interface BannerViewController () <ADBannerViewDelegate>
#end
#implementation BannerViewController {
ADBannerView *_bannerView;
UIViewController *_contentController;
}
-(instancetype)initWithContentViewController:(UIViewController *)contentController{
NSAssert(contentController != nil, #"Attempting to initialize a BannerViewController with a nil contentController.");
self = [super init];
if (self != nil) {
_bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
_contentController = contentController;
_bannerView.delegate = self;
}
return self;
}
-(void)loadView{
UIView *contentView = [[UIView alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
//Have also tried SKView *contentView = [[SKView alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
[contentView addSubview:_bannerView];
[self addChildViewController:_contentController];
[contentView addSubview:_contentController.view];
[_contentController didMoveToParentViewController:self];
self.view = contentView;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return [_contentController preferredInterfaceOrientationForPresentation];
}
-(NSUInteger)supportedInterfaceOrientations{
return [_contentController supportedInterfaceOrientations];
}
-(void)viewDidLayoutSubviews{
CGRect contentFrame = self.view.bounds, bannerFrame = CGRectZero;
bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];
if(_bannerView.bannerLoaded){
contentFrame.size.height -= bannerFrame.size.height;
bannerFrame.origin.y = contentFrame.size.height;
}else{
bannerFrame.origin.y = contentFrame.size.height;
}
_contentController.view.frame = contentFrame;
_bannerView.frame = bannerFrame;
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner{
[UIView animateWithDuration:0.25 animations:^{
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}];
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error{
[UIView animateWithDuration:0.25 animations:^{
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}];
}
-(BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave{
[[NSNotificationCenter defaultCenter]postNotificationName:BannerViewActionWillBegin object:self];
return YES;
}
-(void)bannerViewActionDidFinish:(ADBannerView *)banner{
[[NSNotificationCenter defaultCenter]postNotificationName:BannerViewActionDidFinish object:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Game View controller:
#interface GameViewController ()
#property (nonatomic, strong) IBOutlet UIView *contentView;
#end
#implementation GameViewController {
}
-(instancetype)init{
self = [super init];
if (self) {
}
return self;
}
-(void)viewDidLoad{
//self.canDisplayBannerAds = YES;
[super viewDidLoad];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
}
-(void)viewDidLayoutSubviews{
}
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
SKView *skView = (SKView*)self.originalContentView;
if (!skView.scene) {
SKScene *scene = [GameScene sceneWithSize:skView.bounds.size];
[skView presentScene:scene];
//skView.showsPhysics = YES;
}
}
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskPortrait;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
I figured it out. The answer, as I thought it would be, was incredibly simple and quite mind-boggling that Apple wouldn't put some sort of warning in their documentation, but then again maybe I am just too much of a noob and we are expected to know these kinds of things.
The answer, is that init was never being called in GameViewController, instead, initWithCoder: was being called. Once I NSLogged the init method and saw it wasn't being called, I figured this out.

UIImagePIckerController appears but the camera does not start

Yes, It might be a duplicate question of this. But since it didn't get an answer, I will be more specific on the case and code:
I have 3 involved UIViewControllers:
WelcomeView - the first one
TakePhotoViewController - the second one who is delegate of the OverlayviewController
OverlayViewController - custom view for the camera.
Scenario:
User enter WelcomeView and clicks on a button to be transfered with segue to TakeView.
UIImageViewController is being opened to take a photo.
The user clicks on cancel button - didCancelCamera method in TakePhotoViewController is being invoked and he returns to WelcomeView
The user leaves the app.
The user re-opens the app and perform step 1 again.
THE IMAGE PICKER IS NOT BEING OPENED. I COULD TAKE A PHOTO AND IT'S OK - BUT THE USER CAN'T SEE WHAT HE IS TAKING.
OverlayViewController.h
#interface OverlayViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
#property (nonatomic,weak) id<OverlayViewControllerDelegate> delegate;
#property (nonatomic,retain) UIImagePickerController *imagePickerController;
#end
OverlayViewController.m:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
}
- (IBAction)takePicture:(id)sender {
[self.imagePickerController takePicture];
}
- (IBAction)cancelImagePicker:(id)sender {
[self.delegate didCancelCamera];
}
- (void) setupImagePicker:(UIImagePickerControllerSourceType) sourceType
{
self.imagePickerController.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
self.imagePickerController.showsCameraControls = NO;
if ([[self.imagePickerController.cameraOverlayView subviews] count] ==0)
{
CGRect overlayViewFrame = self.imagePickerController.cameraOverlayView.frame;
CGRect newFrame = CGRectMake(0.0, CGRectGetHeight(overlayViewFrame)-self.view.frame.size.height-10.0, CGRectGetWidth(overlayViewFrame), self.view.frame.size.height + 10.0);
self.view.frame = newFrame;
[self.imagePickerController.cameraOverlayView addSubview:self.view];
}
}
}
- (void)finishAndUpdate
{
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
[self.delegate didFinishWithCamera]; // tell our delegate we are done with the camera
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self finishAndUpdate];
}
TakePhotoViewController.h
#interface TakePhotoViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate,OverlayViewControllerDelegate>
#property (nonatomic, retain) OverlayViewController *overlayViewController;
#end
TakePhotoViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// Insert the overlay
self.overlayViewController = (OverlayViewController *)[sb instantiateViewControllerWithIdentifier:#"Overlay"];
self.overlayViewController.delegate = self;
}
- (void)viewDidUnload
{
self.overlayViewController = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)openImagePicker {
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
[self showImagePicker:UIImagePickerControllerSourceTypeCamera];
}
else{
[self showImagePicker:UIImagePickerControllerSourceTypePhotoLibrary];
}
}
- (void)viewDidAppear:(BOOL)animated{
if (appDelegate.shouldOpenPicker){
[self openImagePicker];
}
}
- (void)showImagePicker:(UIImagePickerControllerSourceType)sourceType
{
if ([UIImagePickerController isSourceTypeAvailable:sourceType])
{
[self.overlayViewController setupImagePicker:sourceType];
[self presentViewController:self.overlayViewController.imagePickerController animated:YES completion:nil];
}
}
-(void)didCancelCamera{
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
}
I found the bug.
The method
-(void)didCancelCamera from TakePhotoViewController is being called when the user clicks on - (IBAction)cancelImagePicker:(id)sender in OverlayViewController.
However, somehow the code in didCancelCamera causes viewDidAppear method of TakePhotoViewController to be invoked again and reopen the image picker.
I have no idea why
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
causes the viewDidAppear method of that view (TakePhoto) being recalled again.
Hope that it will help someone

Correctly Stoping AVAudioPlayer. Currently runs in Background

I've been strugling with this problem for quite a while.
I'm trying to make a player that contains a playlist and plays only the music files stored inside the application.
Now, i'm using AVAudioPlayer to play the mp3 files. My problem is that when i load the playlist i can't stop the previous mp3 from playing and therefor i get a mixture of sounds between the first audio and all the other audio files that i load from the playlist.
so, here is my code:
#import "MainViewController.h"
#import <Foundation/Foundation.h>
#pragma mark Audio session callbacks_______________________
#implementation MainViewController
#synthesize artworkItem;
#synthesize userMediaItemCollection;
#synthesize playBarButton;
#synthesize pauseBarButton;
#synthesize musicPlayer;
#synthesize navigationBar;
#synthesize noArtworkImage;
#synthesize backgroundColorTimer;
#synthesize nowPlayingLabel, AudFile;
#synthesize appSoundButton;
#synthesize addOrShowMusicButton;
#synthesize appSoundPlayer;
#synthesize soundFileURL;
#synthesize interruptedOnPlayback;
#synthesize playedMusicOnce;
#synthesize playing;
#pragma mark Music control________________________________
- (IBAction) playOrPauseMusic: (id)sender {
}
- (IBAction) AddMusicOrShowMusic: (id) sender {
[audioPlayer stop];
// Load the Playlist direcly
MusicTableViewController *controller = [[MusicTableViewController alloc] initWithNibName: #"MusicTableView" bundle: nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController: controller animated: YES];
[controller release];
}
#pragma mark Application playback control_________________
- (IBAction) playAppSound: (id) sender {
// [appSoundPlayer play];
// playing = YES;
// [appSoundButton setEnabled: NO];
}
// delegate method for the audio route change alert view; follows the protocol specified
// in the UIAlertViewDelegate protocol.
- (void) alertView: routeChangeAlertView clickedButtonAtIndex: buttonIndex {
if ((NSInteger) buttonIndex == 1) {
[appSoundPlayer play];
} else {
[appSoundPlayer setCurrentTime: 0];
[appSoundButton setEnabled: YES];
}
[routeChangeAlertView release];
}
#pragma mark AV Foundation delegate methods____________
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) appSoundPlayer successfully: (BOOL) flag {
}
- (void) audioPlayerBeginInterruption: player {
}
- (void) audioPlayerEndInterruption: player {
}
#pragma mark Table view delegate methods________________
// Invoked when the user taps the Done button in the table view.
- (void) musicTableViewControllerDidFinish: (MusicTableViewController *) controller {
[self dismissModalViewControllerAnimated: YES];
}
#pragma mark Application setup____________________________
#if TARGET_IPHONE_SIMULATOR
#warning *** Simulator mode: iPod library access works only when running on a device.
#endif
//play audio 2
- (void)playAudio2:(NSString*) name type:(NSString*)type
{
[self stopAudio];
[audioPlayer stop];
[audioPlayer release];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:name
ofType:type]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
}
[audioPlayer play];
}
-(void)playAudio
{
[appSoundPlayer play];
}
- (void)stopAudio
{
[appSoundPlayer stop];
}
// Configure the application.
- (void) viewDidLoad {
[super viewDidLoad];
[self setPlayedMusicOnce: NO];
[self setNoArtworkImage: [UIImage imageNamed: #"no_artwork.png"]];
[self setPlayBarButton: [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemPlay
target: self
action: #selector (playOrPauseMusic:)]];
[self setPauseBarButton: [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemPause
target: self
action: #selector (playOrPauseMusic:)]];
[addOrShowMusicButton setTitle: NSLocalizedString (#"Add Music", #"Title for 'Add Music' button, before user has chosen some music")
forState: UIControlStateNormal];
[appSoundButton setTitle: NSLocalizedString (#"Play App Sound", #"Title for 'Play App Sound' button")
forState: UIControlStateNormal];
[nowPlayingLabel setText: NSLocalizedString (#"Instructions", #"Brief instructions to user, shown at launch")];
// Configure a timer to change the background color. The changing color represents an
// application that is doing something else while iPod music is playing.
[self setBackgroundColorTimer: [NSTimer scheduledTimerWithTimeInterval: 3.5
target: self
selector: #selector (updateBackgroundColor)
userInfo: nil
repeats: YES]];
[self setupAudioSession];
}
// Invoked by the backgroundColorTimer.
- (void) updateBackgroundColor {
[UIView beginAnimations: nil context: nil];
[UIView setAnimationDuration: 3.0];
CGFloat redLevel = rand() / (float) RAND_MAX;
CGFloat greenLevel = rand() / (float) RAND_MAX;
CGFloat blueLevel = rand() / (float) RAND_MAX;
self.view.backgroundColor = [UIColor colorWithRed: redLevel
green: greenLevel
blue: blueLevel
alpha: 1.0];
[UIView commitAnimations];
}
#pragma mark Application state management_____________
- (void) didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void) viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void) setupAudioSession
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *setCategoryError = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (setCategoryError){/* Handle Error COndition*/}
NSError *activationError = nil;
[audioSession setActive:YES error:&activationError];
if (activationError){
/*handle error*/
}
}
// just for test , delete after
-(void) myInstanceMethod: (id)sender{
[self setupAudioSession];
[audioPlayer stop];
[audioPlayer release];
AudFile = sender;
NSLog(#"My Instance metthod called ok with item : %#", sender);
[self playAudio2:[NSString stringWithFormat:#"%#",AudFile] type:#"mp3"];
//[self setupApplicationAudio: sender];
NSLog(#"New Audfile is: %#", AudFile);
// [self setupApplicationAudio];
}
+(void) myClassMethod{
NSLog(#"Class Called ok");
}
- (void)dealloc {
/*
// This sample doesn't use libray change notifications; this code is here to show how
// it's done if you need it.
[[NSNotificationCenter defaultCenter] removeObserver: self
name: MPMediaLibraryDidChangeNotification
object: musicPlayer];
[[MPMediaLibrary defaultMediaLibrary] endGeneratingLibraryChangeNotifications];
*/
[[NSNotificationCenter defaultCenter] removeObserver: self
name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification
object: musicPlayer];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: MPMusicPlayerControllerPlaybackStateDidChangeNotification
object: musicPlayer];
[musicPlayer endGeneratingPlaybackNotifications];
[musicPlayer release];
[artworkItem release];
[backgroundColorTimer invalidate];
[backgroundColorTimer release];
[navigationBar release];
[noArtworkImage release];
[nowPlayingLabel release];
[pauseBarButton release];
[playBarButton release];
[soundFileURL release];
[userMediaItemCollection release];
[super dealloc];
}
I am sending the file name from my playlist to my player through
-(void) myInstanceMethod: (id)sender{
[self setupAudioSession];
[audioPlayer stop];
[audioPlayer release];
AudFile = sender;
NSLog(#"My Instance metthod called ok with item : %#", sender);
[self playAudio2:[NSString stringWithFormat:#"%#",AudFile] type:#"mp3"];
//[self setupApplicationAudio: sender];
NSLog(#"New Audfile is: %#", AudFile);
// [self setupApplicationAudio];
}
and i load my playlist from my mainviewcontroller using the UIModalTransition in the method
- (IBAction) AddMusicOrShowMusic: (id) sender {
[audioPlayer stop];
// Load the Playlist direcly
MusicTableViewController *controller = [[MusicTableViewController alloc] initWithNibName: #"MusicTableView" bundle: nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController: controller animated: YES];
[controller release];
}
As you can see when i load my playlist i tell the player to stop playing through
[audioPlayer stop];
but for some reason it failes to stop playing, therefore creating an overlap of sounds.
This is my problem which i'm trying to figure out.
Thank you
and best regards!

Why is my NSOperation subclass never finishing?

I have an NSOperation subclass that I want to run concurrently.
My understanding is that for concurrent operations to work:
I need to define isConcurrent to return YES.
I need to define the start method
I need to send KVOs notification for isExecuting and isFinished when it's done.
Using #synthesize will automatically send the appropriate KVO notifications when the values for isExecuting and isFinished are changed.
Despite this, I have verified that my queue never moves on to the next item.
Here's the meat of my code:
#interface MyOperation()
#property (readwrite) BOOL isExecuting;
#property (readwrite) BOOL isFinished;
#end
#implementation MyOperation
- (void)start
{
#autoreleasepool {
self.isExecuting = YES;
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
self.isExecuting = NO;
self.isFinished = YES;
} copy];
[_HTTPOperation start];
}
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)completed
{
}
#end
What am I missing?
(This is on an iPhone, but I can't imagine that matters.)
It looks like whatever KVO notifications #synthesize sends aren't enough for NSOperationQueue to move on.
Sending the notifications manually fixes the problem:
- (void)start
{
#autoreleasepool {
[self willChangeValueForKey:#"isExecuting"];
self.isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURLRequest *URLRequest = [self buildRequest];
if (!URLRequest) {
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
return;
}
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
} copy];
[_HTTPOperation start];
}
}
See also:
Why does NSOperation disable automatic key-value observing?
What's your "queue" look like? Are you using an NSOperationQueue?
Anyway, I'll try to answer your question with what I understood :P
I would create a delegate for my NSOperation and have KVO take care of calling this.
Say for example your NSOperation class looks like this
#interface MyOperation : NSOperation
#property (assign) id<MyOperationDelegate> delegate;
Your implementation
#synthesize delegate;
#synthesize error;
-(id)init{
self = [super init];
if(self){
[self addObserver:self forKeyPath:#"isFinished"
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
-(void)dealloc{
[self removeObserver:self forKeyPath:#"isFinished"];
[super dealloc];
}
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:#"isFinished"] == YES){
if([self isCancelled] == NO){
if(delegate != nil && [delegate respondsToSelector:#selector(operationComplete:)]){
[delegate taskComplete:self];
}
}else{
if(delegate != nil && [delegate respondsToSelector:#selector(operationCancelled)]){
[delegate taskCancelled];
}
}
}
}
-(void)main{
[NSException exceptionWithName:kTaskException
reason:#"Only to be used with subclass"
userInfo:nil];
}
And finally your protocol
#class MyOperation;
#protocol MyOperationDelegate <NSObject>
#optional
-(void)operationComplete:(MyOperation*)operation;
-(void)operationCancelled;

iPhone delegate & controller dealloc?

I have been playing with a simple iphone app and decided to put an NSLog statement in the deallocs of both the controller and the delegate but neither of these print out to the Xcode console?
// APPLICATION DELEGATE
#import "iPhone_buttonFunAppDelegate.h"
#import "iPhone_buttonFunViewController.h"
#implementation iPhone_buttonFunAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
NSLog(#"applicationDidFinishLaunching ...");
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(#"AWT:");
}
- (void)dealloc {
NSLog(#"-DEL-"); // << Does not print?
[viewController release];
[window release];
[super dealloc];
}
#end
// VIEW CONTROLLER
#import "iPhone_buttonFunViewController.h"
#implementation iPhone_buttonFunViewController
#synthesize statusText;
-(IBAction)buttonPressed:(id) sender {
NSString *title;
NSString *newText;
title = [sender titleForState:UIControlStateNormal];
newText = [[NSString alloc] initWithFormat:#"%# button pressed.", title];
[statusText setText:newText];
[newText release];
NSLog(#"Button Press ... %#", title);
}
-(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
NSLog(#"-1-");
}
-(void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
NSLog(#"-2-");
self.statusText = nil;
}
-(void)dealloc {
NSLog(#"-CON-"); // << Does not print?
[statusText release];
[super dealloc];
}
#end
gary
This is an optimization in the Cocoa touch runtime. Certain deallocations aren't made at the end of the program, since the entire program is going to exit and they will be wiped out anyway.
This problem with NSLog(...) may be answered by this other stackoverflow question about applicationWillTerminate:
Good luck.