1

According to this answer, I'm trying to declare and define global variable and getting this error:

   Undefined symbols for architecture armv7:
     "_loggedIn", referenced from:
      -[XYZAppDelegate application:didFinishLaunchingWithOptions:] in XYZAppDelegate.o
      -[XYZFolderViewController viewDidAppear:] in XYZFolderViewController.o
      -[XYZFolderViewController loginViewController:didEnterUsername:password:] in XYZFolderViewController.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I've found many answers here, but all of them are referring to different problem(s) (as much as I can say).

XYZAppDelegate.h

...
extern BOOL loggedIn;
...

XYZAppDelegate.m:

...
@implementation XYZAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    loggedIn = 0;
    ...
}
...

XYZFolderViewController.h

...
extern BOOL loggedIn;
...

XYZFolderViewController.m

...
@implementation
...
- (void)loginViewController:(XYZLoginViewController *)loginViewController
           didEnterUsername:(NSString *)username
                   password:(NSString *)password
{
...

    if ([username isEqualToString:theUsername] && [password isEqualToString:thePassword])
    {
        loggedIn = 1;  /* 0 == not logged in. 1 == logged in. */
        ...
    }
...
}

Point of this global variable is holding information about user being logged in (or not) into app. I am aware that using extern in Objective-C is not clean approach. However, besides extern usage, I'd appreciate hints for solutions of other kind. Expecting this is rookie question.

Community
  • 1
  • 1
daneel
  • 85
  • 1
  • 9
  • 1
    What do you think `extern` means and is doing for you? – Wain Jul 04 '13 at 20:03
  • Well, it "makes visible" loggedIn variable globally... – daneel Jul 04 '13 at 20:07
  • Try to avoid global variables. If you need something like that, then your best guess is the AppDelegate. You probably don't have to bother with extern declarations unless you really know what they are useful for. – allprog Jul 04 '13 at 20:08
  • `extern` is not a "globalizer" – allprog Jul 04 '13 at 20:10
  • To be honest, I've also tried [this answer](http://stackoverflow.com/questions/10243635/variable-of-appdelegate-used-as-global-variable-doesnt-work), so used something like this from XYZFolderViewController: `XYZAppDelegate *appDelegate = (XYZAppDelegate *)[[UIApplication sharedApplication] delegate];` But `appDelegate` haven't had AppDelegate properties accessible at all. – daneel Jul 04 '13 at 20:16
  • You simply have to define the properties on `XYZAppDelegate` (in the .h file). You'll have the properties then. The .h file is where you define the public interface of your class. Anything that is declared there will be visible for the other classes. – allprog Jul 04 '13 at 20:23
  • @allprog , as I've indicated above, originally, I put property to `XYZAppDelegate.h`, but had no success in accessing it from `XYZFolderViewController.m` using `appDelegate`. Is this what you mean or I'm not getting it a bit? – daneel Jul 04 '13 at 20:45

3 Answers3

6

extern means: "this thing is defined somewhere else"

So, you aren't declaring a global variable. You're just saying that someone else is and that you want to use it. So when the compiler tries to find it and comes up blank it tells you it couldn't find the definition.

Not that you should be trying to use a global for this, you shouldn't, but it would be done using static.

You should make the loggedIn flag an instance variable and provide proper, read only, access to the value. This means publicly read only, but privately writeable. So that the status can only be changed inside the class (as a result of loginViewController:...).

So, in the .h file you would have:

@property (assign, nonatomic, readonly) BOOL loggedIn;

And then in the .m file:

@property (assign, nonatomic, readwrite) BOOL loggedIn;
Wain
  • 118,658
  • 15
  • 128
  • 151
  • Thanks @Wain, I deleted `extern` from `XYZAppDelegate.h` and voila, it works. As you and @allprog object (and I admin from the first moment in original question), this (global variables) is not clean way to go. You suggest make `loggedIn` an instance variable with read-only access. Could you be more specific, please? Also I don't get read-only access, because I need to change its state in `XYZFolderViewController` when user enter proper username and password. – daneel Jul 05 '13 at 14:34
1

Making state globally available in your app is generally not a good idea. You should always try to keep mutable state to the minimum. Take on this read or check out the Functional Programming Priciples Course these are excellent to get you started on becoming a better programmer.

The extern keyword is best for making constant objects available everywhere. E.g. NSStrings. Like this:

Constants.h

extern NSString* kAKeyPath; 
extern double kPI;

Constants.m

NSString * const kAStringContant = @"contact.name";
const double kPI = 3.14159265359;

Using values in this way is advantageous as the compiler can check it for you (it's not just a string or int value you need to remember) and it will be stored in one instance in the program data store (compared to storing it as many times as they appear if extern is not used).

allprog
  • 16,540
  • 9
  • 56
  • 97
0

one issue could be if you have multiple targets, you have imported the .h file which includes a promise that a variable exists, but you aren't including the .c or.m so you are missing the compilation unit that has the symbol. Of course you could manage to get into this state without multiple targets, but since Xcode will add the .m automatically to the default target, you typically won't get into that state.

Grady Player
  • 14,399
  • 2
  • 48
  • 76