When my UIViewController accesses an NSArray in my AppDelegate from an IBAction it crashes the program - iphone-sdk-3.0

I have a couple UIViewControllers that I am trying to access an array inside my AppDelegate. When I use an IBAction UIButton and in that method I access my AppDelegate my program dies silently. Nothing in output or the debugger, it just stops. If I run it several times I can see that it is failing to access the array properly.
To investigate this problem I created a very basic app.
In my AppDelegate.h I declared and set properties for the array
#import <UIKit/UIKit.h>
#class MyViewController;
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyViewController *viewController;
NSArray *images;
}
#property (nonatomic, retain) NSArray *images;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MyViewController *viewController;`
In the AppDelegate.m I synthesised and initialized the NSArray (Also made sure the images were added to the Resources folder).
#synthesize images;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
images = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource:#"bamboo_nw" ofType:#"jpg"],
.....
nil];
NSLog(#"init images size:%i",[images count]);
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
In my UIViewController.h I added class, imported header file, declared, and set properties for my AppDelegate pointer.
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"
#class MyAppDelegate;
#interface MyViewController : UIViewController {
MyAppDelegate *mainDelegate;
IBOutlet UIButton mybutton;
}
#property (nonatomic, retain) MyAppDelegate mainDelegate;
#property (nonatomic, retain) UIButton *mybutton;
-(IBAction) doSomething;`
In my UIViewController.m I synthesize and assign my AppDelegate. I set up an IBAction that will log the same count of the NSArray from the AppDelegate.
#import "MyViewController.h"
#import "MyAppDelegate.h"
#implementation MyViewController
#synthesize mybutton;
- (void)viewDidLoad {
mainDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"vdl images size:%i",[mainDelegate.images count]);
[super viewDidLoad];
}
-(IBAction) doSomething {
NSLog(#"ds images size:%i",[mainDelegate.images count]);
}
I print the size of the NSArray in the AppDelegate when I create it, in the ViewController when I first assign my AppDelegate pointer, and then as a result of my IBAction.
I find that everytime I hit the button the program dies. On the third time I hit the button, I saw that it ran my IBAction but printed my array size as 1 instead of 8. Am I missing something? Also, why don't I get stack traces or anything, the debugger just dies silently?
Thanks in advance for any help!
Debugger Console output for 3 runs:
[Session started at 2010-05-10 06:21:32 -0700.]
2010-05-10 06:21:44.865 My[59695:207] init images size:8
2010-05-10 06:21:47.246 My[59695:207] vdl images size:8
[Session started at 2010-05-10 06:22:15 -0700.]
2010-05-10 06:22:18.920 My[59704:207] init images size:8
2010-05-10 06:22:19.043 My[59704:207] vdl images size:8
[Session started at 2010-05-10 06:22:23 -0700.]
2010-05-10 06:22:25.966 My[59707:207] init images size:8
2010-05-10 06:22:26.017 My[59707:207] vdl images size:8
2010-05-10 06:22:27.814 My[59707:207] ds images size:1

You need to use self.images as your lvalue in application:DidFinishLaunchingWithOptions:, to wit:
#synthesize images;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions(NSDictionary *)launchOptions {
self.images = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource:#"bamboo_nw" ofType:#"jpg"],
.....
nil];
NSLog(#"init images size:%i",[images count]);
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
When you use the ivar images without the self, you are giving up the goodness of the automatic retention that you set up when you created the #property.

Related

KVO Method observeValueForKeyPath notifys me about the change, but I can't use it in viewDidLoad

I am working on this couple of days and I can't figure out what I am doing wrong.
First, I don't know if the KVO is the best way to pass the value of the clicked row info from one view controller to another view controller.
I have Main view, with 2 buttons only. Clicking on one of the buttons, I open MasterTableViewController, where I have the NSTableView with some records (one column only) from core data.
On click on the NSTableView, I am opening the DetailViewController, where I have only one label.
What I am trying is to change the label value, with the value of the clicked row in the NSTableView.
While I can print the value of the clicked row in the DetailViewController, I can't change the label value. I assume that notification comes before the viewDidLoad get called.
I have the following code:
In my MasterTableViewController.h file:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface MasterTableViewController : NSViewController
#property (nonatomic,strong) NSManagedObjectContext *mObjContext;
#property (weak) IBOutlet NSTableView *websitesTableView;
- (IBAction)tableViewDoubleClick:(id)sender;
#property (nonatomic,strong) NSString *cellValue;
#end
In my MasterTableViewController.m file:
#import "MasterTableViewController.h"
#import "DetailViewController.h"
#interface MasterTableViewController ()
#end
#implementation MasterTableViewController
-(void)awakeFromNib {
[_websitesTableView setTarget:self];
[_websitesTableView setDoubleAction:#selector(tableViewDoubleClick:)];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Get the object managed context
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
self.mObjContext = appDelegate.managedObjectContext;
}
- (IBAction)tableViewDoubleClick:(id)sender {
NSInteger rowNumber = [_websitesTableView clickedRow];
NSTableColumn *column = [_websitesTableView tableColumnWithIdentifier:#"websiteUrl"];
NSCell *cell = [column dataCellForRow:rowNumber];
_cellValue = [cell stringValue];
MasterTableViewController *mtvc = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
DetailViewController *dvc = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[mtvc addObserver:dvc
forKeyPath:#"cellValue"
options:NSKeyValueObservingOptionNew
context:NULL];
[mtvc setCellValue:_cellValue];
[mtvc removeObserver:dvc forKeyPath:#"cellValue"];
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
[appDelegate changeViewController:2];
}
#end
Code for the DetailViewController.h is:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface DetailViewController : NSViewController
#property (nonatomic, weak) AppDelegate *appDelegate;
#property (weak) IBOutlet NSTextField *detailLabel;
#property (nonatomic,strong) NSString *transferedLabelValue;
#property (nonatomic,strong) NSString *cellValue;
#end
Code for the DetailViewController.m is:
#import "DetailViewController.h"
#import "MasterTableViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
// if I uncomment this line, program crashes, because the value of the _transferedLabelValue is null here.
//[_detailLabel setStringValue:_transferedLabelValue];
}
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:#"cellValue"]) {
_transferedLabelValue = [change objectForKey:NSKeyValueChangeNewKey];
// Here I get the value of the clicked cell, it is printed in the console. However if I try to change the label value here, doesn't work, since seems that label does not exist yet at this point....
NSLog(#"Value in the observerValueForKeyPath is:%#",_transferedLabelValue);
}
}
#end
Code from the AppDelegate.h :
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (weak) IBOutlet NSView *mainAppView;
#property (nonatomic,strong) NSViewController *mainAppViewController;
- (IBAction)showMasterViewButtonHasBeenClicked:(id)sender;
- (IBAction)showDetailViewButtonHasBeenClicked:(id)sender;
- (void)changeViewController:(NSInteger)tag;
#end
Here is only the relevant code from the AppDelegatge.m, the boiler plate code is omited.
#import "AppDelegate.h"
#import "MasterTableViewController.h"
#import "DetailViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
- (IBAction)saveAction:(id)sender;
#end
#implementation AppDelegate
- (IBAction)showMasterViewButtonHasBeenClicked:(id)sender {
NSInteger tag = [sender tag];
[self changeViewController:tag];
}
- (IBAction)showDetailViewButtonHasBeenClicked:(id)sender {
NSInteger tag = [sender tag];
[self changeViewController:tag];
}
- (void)changeViewController:(NSInteger)tag {
[[_mainAppViewController view]removeFromSuperview];
switch (tag) {
case 1:
self.mainAppViewController = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
[_mainAppView addSubview:[_mainAppViewController view]];
[[_mainAppViewController view] setFrame:[_mainAppView bounds]];
[[_mainAppViewController view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
break;
case 2:
self.mainAppViewController = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[_mainAppView addSubview:[_mainAppViewController view]];
[[_mainAppViewController view] setFrame:[_mainAppView bounds]];
[[_mainAppViewController view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
break;
}
}
Again, if the KVO and notification is not the best way to go, please let me know how I should "inform" the other view which row has been clicked and which is his value.
Regards, John
I manage to solve my problem with the KVO...My problem was that I was creating new instances of the view controllers in the MasterTableViewController...What I did was I created properties for the ViewControllers in the appDelegatge.h like this:
#property MasterTableViewController *mtvc;
#property DetailViewController *dvc;
and then in the appDelegate.m :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_mtvc = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
_dvc = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
}
Rest of the changes in the code are just replacing the MasterTableViewController and DetailViewController with _mtvc and _dvc...
However...I had been warned that this solution is error prone, and that warning shows true. It happens from time to time that I get wrong value, not the clicked one. Once in 4-5 clicks, I get wrong value. What makes me confuse is that I make the function to work on double click, but it works on single click as well...But those things are for some other question I guess.

EXEC_BAD_ACCESS when clicking on a button in objective C

I am just learning and trying to put forth some of the knowledge with objective c and cocoa. I have a little program that has a login screen which then displays a main menu. On the main menu view, there is a button that should load a view to be able to add some data to the app. When I click on the button, it doesn't fire the IBAction but throws an error
EXC_BAD_ACCESS [StartMenuViewController performSelector:withObject:]: message sent to deallocated instance 0x6080195e9f90
I know that it is because there is an object that is not instantiated when the message is being sent, but I cannot find out why. Sorry for the code and the novice level here is the main menu view controller StartMenuViewController.m file:
#import "StartMenuViewController.h"
#import "AppDelegate.h"
#import "MasterViewController.h"
#interface StartMenuViewController ()
#end
#implementation StartMenuViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
-(IBAction)showStrainView:(id)sender{
AppDelegate *delegate = nil;
delegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
MasterViewController *addStrainView = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
[delegate swapView:self.view toView:addStrainView.view];
}
#end
here is the StartMenuViewController.h
#import <Cocoa/Cocoa.h>
#interface StartMenuViewController : NSViewController
#property (weak) IBOutlet NSButton *showStrainViewButton;
#end
Here is the AppDelegate.h
#import <Cocoa/Cocoa.h>
#class loginView, MasterViewController,StartMenuViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate>{
loginView *loginView;
MasterViewController *masterViewController;
}
#property (assign) IBOutlet NSWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (strong) loginView *loginView;
#property (strong) MasterViewController *masterViewController;
#property (strong) StartMenuViewController *starMenuViewController;
-(void)swapView:(NSView *)view1 toView:(NSView *)view2;
#end
Here is the AppDelegate.m
#import "AppDelegate.h"
#include "MasterViewController.h"
#import "ScaryBugDoc.h"
#import "Strains.h"
#import "loginView.h"
#interface AppDelegate()
#end
#implementation AppDelegate
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
#synthesize loginView;
#synthesize masterViewController;
#synthesize starMenuViewController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// 1. Create the master View Controller
self.loginView = [[loginView alloc] initWithNibName:#"loginView" bundle:nil];
// 2. Add the view controller to the Window's content view
[self.window.contentView addSubview:self.loginView.view];
self.loginView.view.frame = ((NSView*)self.window.contentView).bounds;
}
-(void)swapView:(NSView *)view1 toView:(NSView *)view2{
//[view1 removeFromSuperview];
[self.window.contentView addSubview:view2];
view2.frame = ((NSView*)self.window.contentView).bounds;
}
#end
If you need the loginView.h and loginView.m files I can add them as well. I would appreciate any and all help. Thanks in advance
The problem is here:
MasterViewController *addStrainView = [[MasterViewController alloc]initWithNibName:#"MasterViewController" bundle:nil];
[delegate swapView:self.view toView:addStrainView.view];
You don't keep a reference to the MasterViewController so it gets deallocated even though its view is kept around. Now anytime the view tries to access methods from the view controller, things fail.
You should make addStrainView a child view controller of self in the above code so the view controller sticks around as long as its view.

NSTimer in AppDelegate does not update UILabel in UIViewController

My goal is to start a timer in the app delegate and use it to call methods in other view controllers. When these methods are called they will update the text of the UILabel. I'm performing the method on the main thread but I can't figure out why the UILabel is not being updated. I know the method in the view controller is being called but the UILabel doesn't get updated. I also verified the IBOutlet connection in Interface Builder. I am using storyboard to lay out my views and attach the view controllers to those views. What am I doing wrong?
In my AppDelegate.m:
#import "AppDelegate.h"
#import "GreyViewController.h"
#interface AppDelegate ()
#property (nonatomic) int counter;
#property (strong, nonatomic) GreyViewController *greyClass;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(performOnMain)
userInfo:nil
repeats:YES];
return YES;
}
- (GreyViewController *)greyClass {
if (!_greyClass) {
_greyClass = [[GreyViewController alloc] init];
}
return _greyClass;
}
- (void)performOnMain {
[self performSelectorOnMainThread:#selector(updateLabel) withObject:nil waitUntilDone:YES];
}
- (void)updateLabel {
self.counter++;
NSLog(#"appdelegate counter %i", self.counter);
[self.greyClass updateGreyLabel:[NSString stringWithFormat:#"%i", self.counter]];
}
#end
The GreyViewController.h is:
#import <UIKit/UIKit.h>
#interface GreyViewController : UIViewController
#property (strong, nonatomic) NSString *greyString;
#property (weak, nonatomic) IBOutlet UILabel *greyLabel;
- (void)updateGreyLabel:(NSString *)string;
#end
The method in my GreyViewController.m:
- (void)updateGreyLabel:(NSString *)string {
self.greyLabel.text = string;
NSLog(#"greyviewcontroller string %#", string);
}
Every time you call:
GreyViewController *greyClass = [[GreyViewController alloc] init];
in your "updateLabel" method (i.e. every two seconds) you're instantiating a new GreyViewController. It's very likely you do not want to do that.
Instead, instantiate/create it once (or create it in your storyboard or XIB file) and the update the label which is connected to an outlet.

How do we disable this button with no compiler error?

I am trying to improve myself in Objective-C and what I am on about is now modifying and changing and existed project. I did everything I wanted expect one thing. Theres an info button in my program and it doesnt related with .xib file. I tried to remove it but always my main.m gave error like SIGABRT (int retval thing.)
Here is the thing what I want to modify
http://i.stack.imgur.com/ZG51s.png // The picture of my program and info button
I am trying to remove the (i) button on the right-down corner.
Here's my code
This is my RootViewController.h
#import <UIKit/UIKit.h>
#class MainViewController;
#interface RootViewController : UIViewController {
IBOutlet UIButton *infoButton; //Silinecek
MainViewController *mainViewController;
}
#property (nonatomic, retain) MainViewController *mainViewController;
#end
This is my RootViewController.m
#import "RootViewController.h"
#import "MainViewController.h"
#implementation RootViewController
#synthesize mainViewController;
- (void)viewDidLoad {
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[viewController release];
[self.view insertSubview:mainViewController.view belowSubview:infoButton];
}
and also I tried to modify my AppDelegate h and m. Here they are ;
#import <UIKit/UIKit.h>
#class RootViewController;
#interface AppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UIWindow *window;
IBOutlet RootViewController *rootViewController;
}
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#import "AppDelegate.h"
#import "RootViewController.h"
#implementation AppDelegate
#synthesize window;
#synthesize rootViewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:[rootViewController view]];
//[window makeKeyAndVisible];
}
/*
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
*/
What should I do for removing that info button in my View. Thanks for the any tips
And if you are interested in this is the whole algorithm : http://pastebin.com/hHQkQYS6
Do you still have the control linked somewhere in your xib file? It is declared as an IBOutlet control. Usually when I have these controls I will use the GUI link this control in some way (I can't remember what exactly as I have not done Obj-C in a long time). But I would definitely have a look around you xib file (as well as removing [self.view insertSubview:mainViewController.view belowSubview:infoButton];
The exception mentions:
[<RootViewController 0x4b9ab20> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key infoButton.
The call stack shows that it is thrown by UINib. So this is happening during nib load. So check for all objects present, carefully, in the nib and remove the info button

Objective-C: Calling class 2 instance from class1 [alloc init] way is not working

I've got the following method on a GameScreen.m file, with its own declaration - (void) drawNumbers on a GameScreen.h file:
//GameScreen.h
#import <UIKit/UIKit.h>
#interface GameScreen : UIView
{
IBOutlet UIButton *cell00;
}
- (void) drawNumbers;
- (IBAction) onCellClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *cell00;
#end
//GameScreen.m
#import "GameScreen.h"
- (void) drawNumbers
{
//testing if this works, so far it doesn't
[cell00 setTitle:#"Whatever" forState:UIControlStateNormal];
[cell00 setTitle:#"Whatever" forState:UIControlStateHighlighted];
}
I'm trying to call this method from my GameScreenViewController.m file, this way:
//GameScreenViewController.m
#import "GameScreenViewController.h"
#import "GameScreen.h"
...
- (void) viewDidLoad
{
GameScreen *aGameScreen = [[GameScreen alloc] init];
[aGameScreen drawNumbers];
[aGameScreen release];
[super viewDidLoad];
}
This is supposed to change the title of a button in a GameScreen.xib file where GameScreenViewController.m is the viewController and GameScreen class is the event handler where I get all the button clicks, timers running, etc. I am trying to call [drawNumbers] from [viewDidLoad] since I want the title to be changed when the screen is brought up front (screen management is done through the AppDelegate files).
The thing is, if I call drawNumbers instance from inside the same class through
//GameScreen.m
#import GameScreen.h
-(void) onButtonClick:(id)sender
{
//some other code
[self drawNumbers];
}
it works (as to say, nothing wrong with the code implementation or the graphic interface).
I've browsed through Apple Guide and tons of pages on the Internet, but I can't seem to find any light to this. Any further help (including answers as to where exactly find the answer in the ADG) would be really appreciated.
(Edited: here goes the AppDelegate code to flip to the specific view, just in case):
//myAppAppDelegate.h
#import <UIKit/UIKit.h>
#class myAppViewController, GameScreenViewController;
#interface myAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
myAppViewController *viewController;
GameScreenViewController *gameScreenViewController;
}
- (void) flipToGameScreen;
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) GameScreenViewController *gameScreenViewController;
#end
//myAppAppDelegate.m
-(void) flipToGameScreen
{
GameScreenViewController *aGameScreenView = [[GameScreenViewController alloc] initWithNibName: #"GameScreen" bundle:nil];
[self setGameScreenViewController:aGameScreenView];
[aGameScreenView release];
[gameScreenViewController.view.frame = [[UIScreen mainScreen] applicationFrame];
[viewController.view removeFromSuperview];
[self.window addSubview:[gameScreenViewController view]];
}
Since your cell00 is to be set by a NIB it will be nil if you simply do [[GameScreen alloc] init]. It will only be set if the corresponding NIB is loaded (and a connection is actually set up).
If the cell can be accessed in your viewDidLoad, create a property on GameScreen and pass it through the property (or a dedicated initWithCell: or something).
If you have something like an IBOutlet GameScreen *aGameScreen; on your GameScreenViewController (and also established a connection to cell00 in the same NIB) you should access that instead.