Objective-C: Instance Method is Never Called - iphone

I've got a class called RootViewController with this:
Test *myLevelObject = [[Test alloc] init];
[myLevelObject afficher];
It is supposed to call my method -[ Test afficher] but there is no warning or error and it does enter the method. Any ideas? thx
Declaration of test class :
test.m :
#import "Test.h"
#import "RootViewController.h"
test.h :
#import <UIKit/UIKit.h>
#class RootViewController;
#interface Test : UIViewController <UIActionSheetDelegate,UITextFieldDelegate>{
-(void) afficher{
NSLog(#"hello");
poids.text = [NSString stringWithFormat:#"lol"];
labelalco.text = [NSString stringWithFormat:#"cac"];
}
it show in console "hello" but it doesnt execute my 2 line of code.
ok now i tryed to do UIAlertView and it works but it fails only when i use the variables
that are initialized in Test.h with interface builder.
Initialization :
IBOutlet UILabel *labelalco;
Interface builder :
alt text http://img72.imageshack.us/img72/4507/screenshot20100225at090.png
this is another screen shot of where the test and the 'drink' section which is rootviewcontroller is shown .
THANKS

It's possible that your init method is passing back nil, instead of an object. If this is the case, than the -afficher method is sent to nil, which doesn't break, but doesn't DO anything. Check your return value after the alloc/init.

Since you say an NSLog message within the afficher method is written to the console, your method is empirically being called. That is doesn't do what you intend it to do is a bug in your code. From the minimal listing you've provided, I'd guess that poids and lableco are not initialized properly (meaning they're still nil when you method is called).

If you're using IBOutlet in -init method, it won't work there (I guess it's set to nil), but it should work in -awakeFromNib method.

I echo what was stated above. Your afficher is likely getting invoked if you see the "hello" output in your console. (To be absolutely certain, clear your console prior to running and check that it shows again to rule out stale output.) One other thing I would suggest is to use an NSAssert() to be sure your outlets are set. Here's an example:
-(void) afficher{
NSLog(#"hello");
NSAssert(nil!=poids, #"poids should be set.");
poids.text = [NSString stringWithFormat:#"lol"];
NSAssert(nil!= labelalco, #"labelalco should be set.");
labelalco.text = [NSString stringWithFormat:#"cac"];
}
I just looked very closely at your screen shots and it appears that your Test object is a UIViewController. From the things you describe I bet you're assuming the labelalco to be set via interface builder connections. That will not happen in your 1st code snippet because you instantiate it directly without using the nib file.
Test *myLevelObject = [[Test alloc] init];
[myLevelObject afficher];
should be:
Test *myLevelObject = [[Test alloc] initWithNibName:#"Test.xib" bundle:nil];
[myLevelObject afficher];
Assuming Test.xib is where the connections are made in IB. It looks like this is a sub controller in your main window's tab view controller so there may be additional complexity in what you're trying to do.

Your latest comments say you're using a UITabBarController that contains RootViewController and Test.
But in RootViewController, you're trying to call a method in Test by creating a new instance of it. This instance would be separate from the one the tabbarcontroller is using.
I think in RootViewController, you need to instead obtain a reference to the same instance of Test the tabbarcontroller is using.
See this post for a possible way to do this:
iPhone SDK: How to access a ViewController nested in a TabBar from MyAppDelegate?
However, why do you have code in RootViewController that updates Test? If Test is displayed, could it not update itself instead? How does the code in RootViewController get executed after Test has already been displayed?

Related

iOS: How to Preload Data before Display

In an IPhone app I am making, I have an initialViewController
(1) with a button. Once the button is clicked, it segues to another View Controller
(2), which at that point loads data from a CoreData File and displays it to the user.
My issue is that there is a small delay between the loading of (2) and the actual Display of the data. That is of course because the data takes a little moment to be loaded. I am doing it asynchronously, and my goal is to never show a spinning wheel or a loading screen (more user friendly).
What I want to do is to "preload" the data at (1), not at (2), that way data should have already been loaded by the time (2) loads and should be displayed immediately. I know how to load the data at (1), but I have no idea how to easily pass it along to (2). I can't do it with a segue because my app is actually a bit more complicated than the description above and it's a hassle to do it via segue.
I have heard it is possible to use the "AppDelegate" but as I am new to programming I have no idea how to use it effectively. The online classes I've been following do not give very clear insight on how to use it, so I'm a bit lost.
After seeing your comment on Paras's post, I have a better solution for you:
Create a subclass of NSObject called fileLoader
//in fileLoader.h
#interface fileLoader : NSObject {
}
+(fileLoader *)sharedInstance;
+(void)createSharedInstance;
//add functions and variables to load, store data for use in another class
#end
Then,
//in fileLoader.m
#implementation fileLoader
static id _instance;
+(void)createSharedInstance{
_instance = [[fileManager alloc] init];
}
+(fileManager *)sharedInstance{
return _instance;
}
//other functions for storing, retrieving, loading, standard init function, etc.
#end
Now you can call [fileManager createSharedInstance] to instantiate a file manager that you can use from anywhere by calling functions on [fileManager sharedInstance].
You could use some kind of DataManager object in which you store your data after loading it for easy retrieval anywhere in your app. If you only have to load the data once you could do it when the app starts.
The trick is to make this a singleton object so no matter where in your app you refer to it it will that same instance that already pre-loaded the data. I use these a lot in my project to manage any and all data that I need inside the app.
There are countless examples out there
The AppDelegate would be better suited for passing data from (2) to (1). For loading data into (2) from (1), you can use the same function that you would use to load in (2) because (1) sees (2) as an instance of a viewController. It is simply like this:
//In OneViewController.m
-(void)viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
TwoViewController *VCTwo = [[TwoViewController alloc] initWithNibName:#"TwoViewController" bundle:nil];
//note that I am only instantiating VCTwo, I am not displaying it.
}
-(void)loadVCTwoFromVCOne{
[TwoViewController preloadInformation];
//simply call the function to load the data on the instance of the viewController before displaying it
}
-(IBAction)presentVCTwo{
[self presentViewController:VCTwo animated:YES completion:nil];
//when you are ready, you can display at any time
}
Property synthesize the NSMutableArray with you data like this.. in yourNextViewController.h file
#property(nonatomic, retain) NSMutableArray *nextViewArray;
and in yourNextViewController.m file just Synthesize like bellow..
#synthesize nextViewArray;
and then just pass out like bellow..
yourNextViewController *objNextView = [[yourNextViewController alloc]initWithNibName:#"yourNextViewController" bundle:nil];
objNextView.nextViewArray = [[NSMutableArray alloc]init];
objNextView.nextViewArray = yourDataArray;
[objNextView.nextViewArray retain];
[self.navigationController pushViewController:objNextView animated:YES];
[objNextView release];
Also you can pass string or Dictionary instead of Array..

How can I use an IBOutlet from another Class file into a different .m file?

MyGems.h
#import <UIKit/UIKit.h>
#interface MyGems : UIViewController {
IBOutlet UIImageView *lock1;
Collect.m
#import "Collect.h"
#import "MyGems.h"
#implementation Collect
- (IBAction) unlock {
if (lock1.hidden = NO) {
ruby.hidden = NO;
}
I get 4 errors, can someone please help? It seems to simple, but is confusing me.
You should almost never do this. The view controller (MyGems, which should be named something like MyGemsViewController) is responsible for managing this view. Other classes should not reach in and modify its IBOutlets. Doing so will cause you significant problems when the view managed by MyGems unloads and lock1 surprisingly becomes nil.
It's unclear what Collect is in this case; I assume it is another view controller? I'm not clear why unlock isn't a method on MyGemsViewController.
Also note that this code is incorrect, and should be throwing warnings at you telling you it's incorrect:
if (lock1.hidden = NO) {
This assigns NO to lock1.hidden. You meant to use ==, but you should never test against NO this way. You should do it this way:
if (! lock1.hidden) {
You must be careful of testing booleans against YES and NO. There are many true values that do not equal YES.
You need to make sure you have a reference to lock1 in your Collect class, most likely you will want to achieve this by having a reference to the Collect class in MyGems and then moving this view logic back into MyGems rather than in your Collect model class

Why is my instance variable nil even though I'm setting it?

I've got a custom class BoardMatchData, containing information about a chess match.
I've also got a custom UIViewController named BoardViewController, which is alloc'ed when a user selects a match from a list.
Here's a snippet from where I create the BoardViewController, set its delegate, and then set the new BoardMatchData:
Games *selectedGame = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if (!self.bvc) {
NSLog(#"Alloc-ing a BVC");
self.bvc = [[BoardViewController alloc] init];
self.bvc.delegate = self;
}
[self.bvc setNewBoardMatchData:[MasterViewController boardMatchDataFromGame:selectedGame]];
When debugging, I can see this method setNewBoardMatchData being called, and it has valid data coming into it. However, later on within the BoardViewController, this boardMatchData always seems to be nil. Here's the setNewBoardMatchData method:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData {
NSLog(#"BMD is being set");
if (self.boardMatchData != newBoardMatchData) {
self.boardMatchData = newBoardMatchData;
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
And within BoardViewController.h, I just have an instance variable:
BoardMatchData *boardMatchData;
A method declaration:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData;
And then at the top of BoardMatchData.m, I have:
#interface BoardViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#property (nonatomic,retain) BoardMatchData *boardMatchData;
- (void)configureView;
#end
#synthesize boardMatchData = _boardMatchData;
...my intent here was to make sure that the setter was only ever being called by itself, and not by some other object.
Is there something wrong with how I'm trying to set self.boardMatchData?
I'm not doing any retains/releases because I'm using ARC.
Edit:
Caleb - I did that #synthesize statement as part of my flailing around trying to find this bug. Originally I had:
#synthesize boardMatchData;
... which I just switched back to. The behaviour is the same; self.boardMatchData always ends up nil, even after I've set it.
I think now I have only one ivar, boardMatchData, and I'm always accessing it through self.boardMatchData.
How do I prevent this from becoming nil?
#synthesize boardMatchData = _boardMatchData;
This says that the ivar the accessors should use is _boardMatchData, but you've also got an ivar named boardMatchData. If you're using the accessors, only _boardMatchData will be set. Since Objective-C automatically clears any ivars when your object is created, theboardMatchData ivar will always be nil.
Your comment (that you posted as an answer which you shouldn't do) suggests that you work on two different instances.
Here are a couple of possible reasons for this:
self.bvc is just assign property instead of retain.
You load one from within a nib and one is constructed in your code
(as shown) - this is probably the most like one. Maybe you just
forgot to wire up the outlet.
Your set self.bvc to nil somewhere so that you keep creating new
instances.
Aha; I found this question with an almost identical problem:
Objective-C – Retained property after being set is nil?
There was no conclusion to that one, but the last suggestion was to "try logging the address of self in -viewDidLoad and -viewWillAppear and -queueFinished. Something like NSLog(#"self is %p", self); and making sure they are the same."
I did that myself and now see that in initWithNibName/configureView/setNewBoardMatchData, I'm seeing one pointer for self, and then when viewDidLoad runs, I'm getting a different one!
I'm not sure how or why yet, but this clearly appears to be the problem (a new instance of my class is being instantiated AFTER I've set boardMatchData).
Edit:
While the above led me to the path of finding this bug (I was getting multiple versions of my BoardViewController), it's not the complete answer. I wanted to add here should anyone find this and be in the same position.
I was actually using storyboarding in Xcode4, and the Apple provided master-detail template with Core Data.
When I was instantiating my detail controller (BoardViewController), I was doing an alloc/init. I should have simply been referencing [segue destinationViewController], as the segue already instantiated a version for me.
I did not provide enough context to actually get to the root

What are alternatives to "delegates" for passing data between controllers?

Are there alternatives to "delegates" to pass back data from one controller to another?
Just seems like a lot of work implementing a delegate just to pass back the result from a child controller, back to the parent controller. Is there not another method? Are "blocks" one answer, and if so some example code would be great.
Delegates aren't a lot of work, aren't a lot of code, and are commonly the most appropriate solution. In my opinion they're neither difficult nor messy.
Five lines of code in the child's interface. Before #interface:
#protocol MyUsefulDelegate <NSObject>
- (void)infoReturned:(id)objectReturned;
#end
Inside #interface:
id <MyUsefulDelegate> muDelegate;
After #inteface's #end:
#property (assign) id <MyUsefulDelegate> muDelegate;
One line of code in the child's implementation:
[[self muDelegate] infoReturned:yourReturnObject];
One addition to an existing line of code in the parent's interface:
#interface YourParentViewController : UIViewController <MyUsefulDelegate>
Three lines of code in the parent's implementation. Somewhere before you call the child:
[childVC setMuDelegate:self];
Anywhere in the implementation:
- (void)infoReturned:(id)objectReturned {
// Do something with the returned value here
}
A total of nine lines of code, one of which is merely an addition to an existing line of code and one of which is a closing curly brace.
It's not as simple as a returning a value from a local method, say, but once you're used to the pattern it's super straightforward, and it has the power of allowing you do do all kinds of more complex stuff.
You could use many ways:
Calling a method of the super controller, needs casting maybe
Notifications
Simple Key-Value-Observing
Core Data
Example for for 1.
interface of your MainViewController: add a public method for the data to be passed
- (void)newDataArrivedWithString:(NSString *)aString;
MainViewController showing ChildController
- (void)showChildController
{
ChildController *childController = [[ChildController alloc] init];
childController.mainViewController = self;
[self presentModalViewController:childController animated:YES];
[childController release];
}
Child Controller header / interface: add a property for the mainViewController
#class MainViewController;
#interface ChildController : UIViewController {
MainViewController *mainViewController;
}
#property (nonatomic, retain) MainViewController *mainViewController;
Child Controller passing data to the MainViewController
- (void)passDataToMainViewController
{
NSString * someDataToPass = #"foo!";
[self.mainViewController newDataArrivedWithString:someDataToPass];
}
KVO or notifications are the way to go in many cases, but delegation gives a very good foundation to build upon. If you plan on extending the relationship between the view controllers in the future, consider using delegation.
Blocks are not really relevant to the above, but in short - it is a technique introduced with iOS 4, where you pass around blocks of code as variables/ parameters. It is very powerful and has many uses. For example, here is how you enumerate objects in an array using a block:
[someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(#"obj descriptions is - %#", [obj description]);
}];
The part from the ^ until the } is a block. Note that I've passed it in as parameter. Now, this block of code will be executed for every object in the array (i.e. output will be the description of each object).
Blocks are also very efficient performance-wise, and are used heavily in many new frameworks.
Apple's blocks beginners guide is quite good.
Check out NSNotificationCenter — NSNotificationCenter Class Reference
Folks pay a lot of attention the the V and the C in MVC, but often forget the M. If you've got a data model, you can pass it from one controller to the next. When one controller makes changes to the data stored in the model, all the other controllers that share the same model will automatically get the changes.
You might find using a singleton is practical. Just use it as a central storage for all your shared data.
Then throw in saving the state of your application too;)

Whats the best place to set a View Controller ivar?

I have an XML reader class which I initialize with a URL
- (id)initWithURL:(NSURL *)url
This class adds objects to an array in the calling class using an instance variable
// in the interface
ViewController *viewController;
// in the implementation
[viewController addObject:theObject];
Now, I initialize my XML reader class, then set the View Controller separately:
XMLController *xmlController = [[XMLController alloc]
initWithURL:url];
xmlController.viewController = self;
My question is whether I should create a new init function which sets the viewController at the same time.
Thanks.
Edit: I forgot to add that my XML reader starts downloading and parsing the class in the init function.
It's entirely up to you. You can see examples of both styles all over Apple's code.
As long as you don't make any assumption about the viewController property's value being constant over time, it should be fine to leave it as-is.
BTW, you might think about refactoring the addObject: logic into a protocol instead of requiring a specific subclass. Something like:
-xmlController:didDecodeObject:
Or whatever makes sense for your XMLController object's logic.
If your init routine is going to cause delegate/controller calls, or set off asyncronous activities (potentially including your downloading) that could message the delegate, then you should include it in the init function.
Otherwise your controller might miss potential delegate messages such as xmlController:didStartConnection that might be called before your initWithURL routine returns.
Also, if the controller/delegate is a required part of the XMLController activities, then you should include it in your init routine.
So yes, in this case I would suggest:
XMLController *xmlController = [[XMLController alloc] initWithURL:url andController:self];