Please see below edit for current (minor) issue
I'm trying to call methods (methods right, not functions?) "between" the MainViewController.m and the FlipsideViewController.m -- from one file/class to another.
I guess this is what's often referred to as "Call methods from another class". There are plenty of such questions around, I know, but I just can't get it to work properly.
In my case, I have several user defined methods/functions in both above mentioned files. Sometimes, I need to call a method from within the FlipsideViewController.m that lies within the MainViewController.m File:
// in MainViewController.m
- (void) calculateDays {
//executes caluculations
// inserts data into labels, etc
}
If I want to call this function simply from within the same file, I just do:
[self calculateDays];
That's easy, however, I want to call this function from within the FlipsideViewController.m file too, as well as vice versa. So how do I do this? This, this and this questions sort of answer it but it doesn't quite work to me. I'll explain why in just a second.
This is what I've tried and think should work:
MainViewController *mvs = [[MainViewController alloc] init]; //alloc init MVC
[mvs calculateDays]; //call "external" function
It gives me the error: "Unknown type name MainViewController". So I assume I have to include/import it somehow for it to work (just like in javascript or PHP). So I include it in the FlipSideViewController.m class:
#import "MainViewController.h"
Great no errors so far. Then I try to compile/build it and runs into another error:
"clang: error: linker command failed with exit code 1 (use -v to see invocation)"
"ld: 3 duplicate symbols for architecture armv7s"
This leads me to think that importing the MainViewController like that isn't the way to go as I then import lots of other stuff that may interfere with some code in the FlipSideViewController class.
I've tried similar solutions but nothing seems to work. Can anyone please explain to me what I'm doing wrong, and perhaps how to do this properly: Call methods between MainViewController.m and FlipsideViewController.m and vice versa.
The proposed solution by H2CO3 did solve most of the issues (XCode bugged for a while and give me random errors which forced me to rebuild the entire project) but there's still this one thing that doesn't quite work: change the content of a UILabel (UIOutlet). Please see if anyone of you can help me with this:
When the method is called from within self (i.e. [self calculateDay]), the value is successfully inserted into the UILabel. When called from FlipsideViewController, the value to be inserted exists and is processed successfully, but can't be inserted into the UILabel. Please se below.
Some loggings:
//method called from within self on viewDidLoad: [self calculateDay];
Processed value to update label with: 26
New value in outlet after having been inserted: 26
//method called from another (FlipsideViewController) class file: [mvs calculateDay];
Processed value to update label with: 26
New value in outlet after having been inserted: (null)
/*
This doesn't work either from that external file:
[[mvs LabelName] setText:#"Hello, update label!"]; no errors but no display either
*/
If you import the header instead, that should give you all the necessary declarations, but you won't have "duplicate symbol" linker errors. This is a "standard"/common practice for writing (Objective-)C code.
#import "MainViewController.h"
^
".h" instead of ".m" -+
(layman's terms) In Objective-C you can only use objects that each file knows about. In this example you are trying to use a MainViewController in the FlipsideController.m file. The FlipsideController.m has no idea what a MainViewController is, so it throws errors because it doesn't know what it is or how to use it. You have two options for telling the Flipsidecontroller what a MainViewController is, you can import the header (#import "MainViewController.h") which will give you full access to everything defined in the FlipSideController.h. (You should probably never import a .m unless you really know what your doing) You can also create a forward declaration - #class FilpsideControllerin the .h and import the file in the .m. This is useful to avoid circular imports etc.
Related
This one has me stumped. I'm writing an iPhone app that tracks bus schedules. Users can bookmark their favorite bus stops so they can jump directly to them from the home screen. I manage the list of favorites in my AppDelegate class (unrelated code has been redacted):
#interface AppDelegate : NSObject <UIApplicationDelegate>
+ (BOOL) isInFavorites: (FavoriteStopData*) inStop;
#end
I have a view controller that presents the list of stops for a given bus route and lets users select one to see predicted bus arrival times for that stop in a new view (and maybe add the stop to their list of favorites):
#implementation RouteStopsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
FavoriteStopData *stopData = [[FavoriteStopData alloc] init];
// ... set various properties in stopData from data in the selected cell
FavoriteStopViewController *fvc = [[FavoriteStopViewController alloc] initWithNibName:#"FavoriteStopViewController" bundle:nil];
fvc.stop = stopData;
fvc.isBookmarked = [AppDelegate isInFavorites:stopData];
[stopData release];
[self.navigationController pushViewController:fvc animated:YES];
[fvc release];
}
#end
The line
fvc.isBookmarked = [AppDelegate isInFavorites:stopData];
gets two warnings:
"'AppDelegate' may not respond to +isInFavorites:"
"Passing argument 1 of 'setIsBookmarked' makes integer from pointer without a cast"
I can't see any reason for Xcode to think '+isInFavorites:' is undefined, yet it does. I've verified that these possible causes for the warning are not in fact the case:
'+isInFavorites:' is declared in "AppDelegate.h" (as shown above)
"RouteStopsViewController.m" does #import "AppDelegate.h" (and "FavoriteStopData.h" and "FavoriteStopViewController.h")
'isBookmarked' is a public BOOL property on FavoriteStopViewController
The code is not being munged by some #define macro; when I preprocess "RouteStopsViewController.m", this code is unchanged.
The code behaves correctly, but I REALLY don't want to live with a warning that I must ignore every time the code compiles, and disabling this warning with some #pragma is a road I'd rather not take unless I have to.
I've tried renaming the method name, the variable names, using the method to set a local BOOL variable and then setting the property with that, using a conditional operator (x ? y : z) to make sure I'm passing a BOOL to the property ... nothing works. That first warning never goes away.
Can anyone suggest why Xcode is giving me this warning?
This is with Xcode 4.2 (Build 4C199) and iOS 5 SDK running in the 5.0 iPhone Simulator on a MacBook Pro running Mac OS X 10.6.8 (Snow Leopard).
If the +isInFavorites: method was completely unknown to the compiler then you'd see a warning like +isInFavorites: not found (return type defaults to 'id').
If you're not seeing that warning then we can assume that the compiler has seen a declaration of that method somewhere. However, the compiler expects this method to return a pointer rather than a BOOL, which is why you're seeing the makes integer from pointer without a cast warning.
Check for any other declarations of an isInFavorites: method in your project. Check for any global variables named AppDelegate that may conflict with your class name. Check for any circular imports between AppDelegate.h and RouteStopsViewController.h. Try renaming your AppDelegate class.
declare your isInFavorites method in your appdelegate.h file.
'AppDelegate' may not respond to +isInFavorites:
Passing argument 1 of 'setIsBookmarked' makes integer from pointer without a cast
The first error causes the second. The compiler's confusion on the existence of +isInFavorites: causes the compiler to assume the return type is id
This assumption causes the warning of making integer from pointer without a cast
You really have to focus on the first warning.
Are these the only warnings?
Try changing your AppDelegate.h to
#class FavoriteStopData
#interface AppDelegate : NSObject <UIApplicationDelegate>
+ (BOOL) isInFavorites: (FavoriteStopData*) inStop;
#end
If you still have issues, you might want to consider making this an instance method instead, considering the appDelegate is valid the whole runtime of your app
Found it. Even makes perfect sense ... now that I know exactly where to look.
My project has two targets: Dev for the version I use to test new code, and App for the user-facing version. Each has its own AppDelegate class (and some other duplicates). Code specific to one target or the other goes into either the ./Dev/ or the ./App/ folder. Common code goes into other folders.
Recently I promoted one Dev-specific class to be used in both targets ... but hadn't yet moved the files out of the Dev folder. This was my problematic RouteStopsViewController. My project was compiling the right "AppDelegate.m", but Xcode was finding the 'wrong' (to my thinking) "AppDelegate.h" because it was looking first in the same folder as "RouteStopsViewController.m".
The fix was easy: move RouteStopsViewController out of the Dev-specific folder into one for code shared by both targets. Now Xcode uses the "AppDelegate.m" file it's compiling to find the matching "AppDelegate.h".
I knew at the time I should move that RouteStopsViewController class when I decided to reuse it in the App target, I just didn't get around to it. When it comes to writing code, trust your nose. If it smells funny, it very probably is.
I know there are questions on here similar to this, but I have not been able to sort it out.
I have a view that has this in the .m above the implementation
CFDataRef CopyImagePixels(CGImageRef inImage){
return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}
Everything works fine, but I have another view that needs to access that same function, if I put it in /that/ views .m file, same location.. the compiler throws a duplicate symbol error..
The only file these two have in common that they include is:
#import "MopalAppDelegate.h"
I thought maybe putting the code in the AppDelegate, but that throws the same error when I attempt to build..
I am still quite new to obj-c, any suggestions would be greatly appreciated.
Thanks.
Edit: The error:
ld: duplicate symbol _CopyImagePixels in /Users/critter/Library/Developer/Xcode/DerivedData/Mopal-dtgtjbahdowmuderbstlmsiznwsi/Build/Intermediates/Mopal.build/Debug-iphonesimulator/Mopal.build/Objects-normal/i386/ViewerController.o and /Users/critter/Library/Developer/Xcode/DerivedData/Mopal-dtgtjbahdowmuderbstlmsiznwsi/Build/Intermediates/Mopal.build/Debug-iphonesimulator/Mopal.build/Objects-normal/i386/StudyListDetailController.o for architecture i386
collect2: ld returned 1 exit status
Command /Xcode4/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gcc-4.2 failed with exit code 1
Your function is being declared and defined in the global scope. When you place the same function definition in two different .m files, you end up with two different functions (as far as the compiler is concerned) that have the same name in the same scope (global), leaving it with no way of knowing which one you intend to call when you say CopyImagePixels(myImage);. Hence the error message.
So there are a couple of options. One is to define your function as a private member function on the objects that use it, like:
#implementation MyClass
- (CFDataRef) copyImagePixels: (CGImageRef)inImage {
return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}
#end
The other (and less preferred) option is to move your function declaration to a shared header file, like MopalAppDelegate.h, and its implementation into your MopalAppDelegate.m file. This creates a function declaration that can be shared out to other classes, with a single definition in the .m file.
You want to put the function definition in a header that the two files share, implement it in the corresponding .m file, and import that header into both views.
But if that's all the function does, why not just call CGDataProviderCopyData directly in both places?
Just implementing my first delegate in objective-C. I thought I had everything in place however the call from my AddController back to ListController isn't being picked up in the ListController.
Given that I'm not getting an exception, and that I can see that the code does get to the point in the AddController where it calls the delegate, are there any fault finding tips?
So for example:
given the "delegate" call (see below) did not throw an exception can I assume that my delegate declarations in the same file are OK?
"[delegate newItemController:self didFinishWithSave:YES];"
given the parent controller so to speak does have the delegate specified in the *.h definition (see below), then this does implied I've correctly implemented the method in the *.m file, noting I get no build errors?
#interface RootViewController : UITableViewController {
is there a known way for delegate calls to go missing without an exception if certain items don't like up (i.e. if there is what should I check for)
thanks
Most common error I've seen for a delegate method not being called is a nil delegate property. In other words, forgetting to specify who the delegate is?
As for debugging tips, anytime I've seen a problem where a delegate is not being called is to set breakpoints throughout the code and step through the code. Then you can see where things are going and what is or isn't being called.
Also, you mention exceptions a lot. Objective C prefers not to use exceptions as they are a relatively expensive call in the language (unlike say Java). Objective C can and does use exceptions but they are rare. You might want to "beaf up" your understanding of error handling in objective c.
Have you set your delegate variable like this in your AddController:
self.delegate = <instance of ListViewController>
If it has not been set, then the delegate would be nil and the method call to the nil would result in nothing. Otherwise, delegate calls wouldn't really go missing like that.
I didn't understand your second point though.
I'm on this step of the "Your First iOS Application" tutorial from Apple.
However, the line [self setMyViewController:aViewController]; has an error and the app appears as a blank black screen. The error message is 'HelloWorldAppDelegate' may not respond to '-setMyViewController' (2)
I've been following the tutorial carefully. How can I make this error go away? Why is it so ambiguous (it "may" not respond? under what circumstances will it?) and why am I getting this error in the first place? What step did I miss?
Go to your header-file and add - (void)setMyViewController:(UIViewController *)viewController;
Maybe you need to rename the parameter.
And btw. it's a warning not an error. Warnings are just for signalize that it could have problems if you haven't included the method.
If you declare a method in your .h file, the .m file gives out a warning if it isn't included. Don't worry it's not wrong if you forgot to declare a method in your .h file.
Are you running your code right after following that step? You should hold off doing so until you have finished following all steps on the page.
The Housekeeping section lists a step that you have to follow in order to make your property setter work:
In the #implementation block of the class, tell the compiler to synthesize the accessor methods for the view controller:
#synthesize myViewController;
After you finish adding the code as described by that section, running your code should work. If not, check your imports and your HelloWorldAppDelegate.h file — you may have missed something else too like declaring your #property.
Why is it so ambiguous (it "may" not respond? under what circumstances will it?)
It will respond if the compiler can find an implementation of the method, and the method is declared in the header file (unless it's a property accessor), and work as normal. If it does not, your program crashes.
and why am I getting this error in the first place?
You typically get that warning (it's not an error) if the property is not synthesized. By synthesizing it, the compiler creates the myViewController and setMyViewController: accessor methods in order for your code to access that property.
That should be a warning not an error.
Take heed of warnings, and you're right to want to eliminate them, but the code will compile and may run with warnings.
The warning could be occurring because there isn't a prototype for the method setMyViewController before it is referenced. The prototype is usually defined in the associated header '.h' file.
The prototype looks like the entry line of the method, up to, but not including the first '{', and with a ';' on the end.
Adding a prototype allows the compiler to verify you're calling the method correctly (and just as importantly, you typed it correctly ;-)
So, I read this post, and it's pretty much exactly what I was looking for. However... it doesn't work. I guess I'm not going to go with the singleton object, but rather making the array in either a Global.h file, or insert it into the _Prefix file.
Both times I do that though, I get the error:
Expected specifier-qualifier-list before 'static'
and it doesn't work. So... I'm not sure how to get it to work, I can remove extern and it works, but I feel like I need that to make it a constant.
The end goal is to have this Mutable Array be accessible from any object or any file in my project. Help would be appreciated!
This is the code for my Globals.h file:
#import <Foundation/Foundation.h>
static extern NSMutableArray * myGlobalArray;
I don't think I need anything in the implementation file. If I were to put that in the prefix file, the error was the same.
EDIT
So, I removed the .m file from Globals, and I just have the code about in Globals.h. Assuming I am going to continue with this terrible practice of having global variables (I know it's bad, I just want to test this out), I now have a new error. It says:
"Multiple storage classes in declaration specifiers"
If I remove "extern" it works and if I remove "static" it works, but having both doesn't... what now?
****Double Edit****
Aright, so I've tried adding the array to my UIApplication Delegate, but I'm doing it wrong because it isn't working. Could someone give me some example code as to where to place it an access it? I don't know if it should go in the implementation, or somewhere else, and once the array is initialized how to access it from the other files... Do I set a new variable to the array, or something?
Just a general programming suggestion--don't share an array. You have no control over it and it will be virtually impossible to trace if something changes it at a time and in a way you aren't expecting.
Instead, create an object with the array inside it and make that object a singleton (or better yet, make a factory for it).
Whenever you want to modify your array, call methods on the object to do so. If you do this, I bet you will find a lot of redundant code you can factor into this object (for instance, searching the array for a value--make a "search" method in the object instead and pass in a value).
It may seem like a lot of work you shouldn't have to do, but you'll find it's fairly fun work, and you should find that you DO have to do it once you see how much code belongs in this object...
Just add the array as a property of the application delegate, and access it like:
[[UIApplication sharedApplication] myArray];
The two (main) ways of making an array global are separate -- either you have a class with a method
static NSMutableArray *foo;
+(NSMutableArray *)foo {
return foo;
}
(in the .m file) with the static piece NOT in the header file, or just
static extern NSMutableArray * myGlobalArray;
with out the singleton wrapper (which I think is better as it saves you from having an extra bit of unnecessary code)
Either way, it is still a bad practice that I would try to avoid.
In general, the presence of a "Globals.h" file is a bad smell that there's an antipattern at work.
I would even advise against Bill K's advice and not use a Singleton pattern at all.
Instead, create the array in your app delegate, and pass it to your root view controller(s), and along the hierarchy to the components that need access to it.
This is what I was looking for:
http://derekneely.com/tag/app-delegate/
Thank you for pointing me in the right direction!
#import <Foundation/Foundation.h>
static extern NSMutableArray * myGlobalArray;
#interface Globals : NSObject {
}
#end