Using global variables in Objective-C
Asked Answered
A

6

9

First of all, I tried almost all the solutions given in stackoverflow but I didn't succeed in implement global vars, I even did a step by step tutorial and still I get the same errors.

heres the issue:

I have a program with severals views, I'd like to use variables on all of the views, so I used global variables,

I have:

GlobalVars.h

#import <UIKit/UIKit.h>
extern NSArray *truckBoxes;
extern NSArray *farmerlist;
extern NSString *farmerCardNumber;

extern NSString *fName;

@interface GlobalVars : NSObject
{
}

@end

GlobalVars.m

#import "GlobalVars.h"

NSArray *farmerlist;
NSArray *truckBoxes;
NSString *farmerCardNumber;
NSString *fName;
@implementation GlobalVars
{

}
@end

I include the file in the class (that is connected to the view controller): under the .h file I add #import "GlobalVars.h

I can access farmerlist and truckBoxes from all over the app, but cant access fName or farmerCardNumber.

I can write to farmerCardNumber and read from the same view, but if I try to access it later on from another view I get

2013-10-26 11:11:45.612 C3Cms[10427:70b] Branch : 11558
2013-10-26 11:11:59.459 C3Cms[10427:70b] -[__NSArrayM length]: unrecognized selector sent to instance 0x8b7fbc0
2013-10-26 11:11:59.463 C3Cms[10427:70b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM length]: unrecognized selector sent to instance 0x8b7fbc0'
*** First throw call stack:
(
    0   CoreFoundation                      0x02a3b5e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x014d58b6 objc_exception_throw + 44
    2   CoreFoundation                      0x02ad8903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
    3   CoreFoundation                      0x02a2b90b ___forwarding___ + 1019
    4   CoreFoundation                      0x02a2b4ee _CF_forwarding_prep_0 + 14
    5   Foundation                          0x00f0db2d -[NSConcreteMutableAttributedString replaceCharactersInRange:withString:] + 39
    6   Foundation                          0x00f0e79a -[NSConcreteMutableAttributedString initWithString:attributes:] + 293
    7   UIKit                               0x001e8116 -[UILabel _setText:] + 97
    8   UIKit                               0x001e82d4 -[UILabel setText:] + 40
    9   C3Cms                               0x00001c1d -[printForm viewDidLoad] + 77
    10  UIKit                               0x0014e9a8 -[UIViewController loadViewIfRequired] + 696
    11  UIKit                               0x0014ec44 -[UIViewController view] + 35
    12  UIKit                               0x00168a72 -[UINavigationController _startCustomTransition:] + 778
    13  UIKit                               0x00175757 -[UINavigationController _startDeferredTransitionIfNeeded:] + 688
    14  UIKit                               0x00176349 -[UINavigationController __viewWillLayoutSubviews] + 57
    15  UIKit                               0x002af39d -[UILayoutContainerView layoutSubviews] + 213
    16  UIKit                               0x000a5dd7 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355
    17  libobjc.A.dylib                     0x014e781f -[NSObject performSelector:withObject:] + 70
    18  QuartzCore                          0x030d772a -[CALayer layoutSublayers] + 148
    19  QuartzCore                          0x030cb514 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    20  QuartzCore                          0x030cb380 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
    21  QuartzCore                          0x03033156 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
    22  QuartzCore                          0x030344e1 _ZN2CA11Transaction6commitEv + 393
    23  QuartzCore                          0x03034bb4 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
    24  CoreFoundation                      0x02a0353e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    25  CoreFoundation                      0x02a0348f __CFRunLoopDoObservers + 399
    26  CoreFoundation                      0x029e13b4 __CFRunLoopRun + 1076
    27  CoreFoundation                      0x029e0b33 CFRunLoopRunSpecific + 467
    28  CoreFoundation                      0x029e094b CFRunLoopRunInMode + 123
    29  GraphicsServices                    0x029979d7 GSEventRunModal + 192
    30  GraphicsServices                    0x029977fe GSEventRun + 104
    31  UIKit                               0x0003b94b UIApplicationMain + 1225
    32  C3Cms                               0x00002882 main + 130
    33  libdyld.dylib                       0x019bc70d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Arraign answered 26/10, 2013 at 8:16 Comment(3)
You must initialise arrays before using themMicroanalysis
@iOSDeveloper: An uninitialised array is nil and would not cause a "unrecognized selector" exception.Qumran
possible duplicate of How to define a global variable that can be accessed anywhere in my application?Policlinic
K
33

One way to implement global variables, and to manage their lifetime (i.e. that they are initialized) and even to provide global methods is to implement a class exposing those variables/methods and to use the singleton pattern:

GlobalVars.h:

#import <Foundation/Foundation.h>

@interface GlobalVars : NSObject
{
    NSMutableArray *_truckBoxes;
    NSMutableArray *_farmerlist;
    NSString *_farmerCardNumber;
    NSString *_fName;
}

+ (GlobalVars *)sharedInstance;

@property(strong, nonatomic, readwrite) NSMutableArray *truckBoxes;
@property(strong, nonatomic, readwrite) NSMutableArray *farmerList;
@property(strong, nonatomic, readwrite) NSString *farmerCardNumber;
@property(strong, nonatomic, readwrite) NSString *fName;

@end

GlobalVars.m:

#import "GlobalVars.h"

@implementation GlobalVars

@synthesize truckBoxes = _truckBoxes;
@synthesize farmerList = _farmerList;
@synthesize frameCardNumber = _frameCardNumber;
@synthesize fName = _fName;

+ (GlobalVars *)sharedInstance {
    static dispatch_once_t onceToken;
    static GlobalVars *instance = nil;
    dispatch_once(&onceToken, ^{
        instance = [[GlobalVars alloc] init];
    });
    return instance;
}

- (id)init {
    self = [super init];
    if (self) {
        _truckBoxes = [[NSMutableArray alloc] init];
        _farmerlist = [[NSMutableArray alloc] init];
        // Note these aren't allocated as [[NSString alloc] init] doesn't provide a useful object
        _farmerCardNumber = nil;
        _fName = nil;
    }
    return self;
}

@end

You can then use these global variables like this, for example:

GlobalVars *globals = [GlobalVars sharedInstance];
globals.fName = @"HelloWorld.txt";
[globals.farmerList addObject:@"Old Macdonald"];
[self processList:[globals farmerList]];

However, please consider:

  • You don't need to use global variables like this; you should be able to create a model object which is created as necessary and reference to it passed to the views. This is MVC.
  • You also posted a stack trace of an unrelated issue which is extremely common with Objective-C; only you can fix this error, once you realize what it is.
Kreis answered 26/10, 2013 at 9:5 Comment(6)
thanks for the comment, I get Framework/Framework.h not foundArraign
Amazing! it works! thanks! for future references - please change @property(strong, nonatomic, readwrite) NSArray *farmerCardNumber; to @property(strong, nonatomic, readwrite) NSString *farmerCardNumber;Arraign
A follow up question, what do I do if I want int or double? if I do @property(strong, nonatomic, readwrite) double *farmerWeight; it says that property with strong must be object of type.Arraign
No, use assign instead of strong and drop the pointer (double instead of double *).Kreis
thanks! you should have ways more votes for this...!!Mezuzah
Can C files access these global variables?Delmore
Q
4

The error has nothing to do with global variables. The error message

-[__NSArrayM length]: unrecognized selector sent to instance 0x8b7fbc0

indicates that you somewhere in your code assigned a NSMutableArray to farmerCardNumber instead of a NSString.

For example, if you do something like

farmerCardNumber = [someDictionary objectForKey:@"someKey"];

and [someDictionary objectForKey:@"someKey"] happens to be an array and not a string, then farmerCardNumber points to that array, even if it was declared as pointer to a string.

Qumran answered 26/10, 2013 at 8:28 Comment(10)
It cannot be, I just searched for farmerCardNumber in my entire code and it only appears in: farmerCardNumber = txtFarmerCardNumber.text; if([firstBranchName isEqualToString: farmerCardNumber ]) and printFarmerCardNumber.text=farmerCardNumber; which is in the other viewArraign
Insert NSLog(@"class=%@", [farmerCardNumber class]); shortly before you read the variable, and check the output ...Qumran
I just added a new var called testVar, and I get the same error.Arraign
Your observation was right, it says "2013-10-26 11:46:32.764 C3Cms[10593:70b] class=__NSDictionaryM", but what would change it? I did the exact thing with a new var that is only stated 2 times in the entire code and it says the same thing. what would overwrite it?Arraign
@izzy: That is strange. How do you assign to testVar? Is the class correct immediately after writing to it?Qumran
edited: right after I write to it the class is correct Ive added [super viewDidLoad]; NSLog(@"class=%@", [testVar class]); In another view, and it gives an exception - Thread 1: EXC_BAD_ACCESS (code=1, address=0x808d721a)Arraign
@izzy: In your question is was an __NSArrayM, then in above comment a __NSDictionaryM, and now a __NSSetM? Are you sure that it is the same variable when you write to it and when you read it? Compare the pointers!Qumran
Are you sure that you don't have another variable with the same name declared locally? Print NSLog(@"ptr=%p", farmerCardNumber); directly after writing to it, and directly before reading it, to check if it is the same instance.Qumran
I will do that, but this is a new variable with a name I haven't used before (testVar)..Arraign
@izzy: You are welcome. - But I was really curios to know what went wrong in your case!Qumran
P
1

I am using in all my project and working fine.

GlobalResource.h

#import <Foundation/Foundation.h>

@class GlobalResource;

extern GlobalResource * R;

@interface GlobalResource : NSObject
{
    NSString *myVar;
}
+ (void) loadGlobalResources;

@property (strong, nonatomic, readwrite) NSString *myVar;

@end

GlobalResource.m

#import "GlobalResource.h"

GlobalResource *R;

@implementation GlobalResource

@synthesize myVar;

- (void) loadGlobalResources
{
    R = [[GlobalResource alloc] init];
}

- (id)init
{
    self = [super init];
    if (self) {
        myVar = @"My Value";
    }

    return self;
}

@end

How to use

#import "GlobalResource.h"

//then access value as bellow

[GlobalResource loadGlobalResource];
NSLog(@"%@",R.myVar); //value will print here
Pastorale answered 2/3, 2016 at 7:11 Comment(0)
H
0

Why donot you try something like:

#import "GlobalVars.h"

NSArray *farmerlist;
NSArray *truckBoxes;
NSString *farmerCardNumber = nil;
NSString *fName = nil;
@implementation GlobalVars
{

}
@end

or

#import "GlobalVars.h"

NSArray *farmerlist;
NSArray *truckBoxes;
NSString *farmerCardNumber = @"";
NSString *fName = @"";
@implementation GlobalVars
{

}
@end

AND it seems like you have passed mutable array to your string varibale, that is why you are getting that error.

Helluva answered 26/10, 2013 at 8:38 Comment(0)
A
-1

Remove the line

#import "GlobalVars.h"

from GlobalVars.m.

Alcaraz answered 26/10, 2013 at 8:23 Comment(4)
I get lots of errors if I do that, including "Class 'globalvars' defined without specifying a base class"Arraign
An implementation file should always import its own interface. I see no problem with that and also do not understand how not importing it would solve the above problem.Qumran
Your problem is that GlobalVars.m both defines your global variables and also declares them as external. Your GlobalVars.h file cannot be included as it stands. You may have other issues because GlobalVars.m requires other declarations in GlobalVars.h, but that is another matter.Alcaraz
There is no problem if a variable is both declared as external and defined in the same compilation unit, and it is common practice as far as I know. Have a look at the first example in https://mcmap.net/q/17012/-how-do-i-use-extern-to-share-variables-between-source-files.Qumran
A
-1

You can't put extern variables to *.h file.

So in GlobalVariables.m you have:

extern NSArray *farmerlist;
NSArray *farmerlist;

And:

@interface GlobalVars : NSObject
{
}

@end

@implementation GlobalVars
{

}
@end

are not needed

[edit]

for example:

Other.m

#import "GlobalVars1.h"

GlobalVars1.h

extern NSArray *truckBoxes;
extern NSArray *farmerlist;
extern NSString *farmerCardNumber;

extern NSString *fName;

GlobalVars.h

#import <UIKit/UIKit.h>

@interface GlobalVars : NSObject
{
}

@end

GlobalVars.m

#import "GlobalVars.h"

NSArray *farmerlist;
NSArray *truckBoxes;
NSString *farmerCardNumber;
NSString *fName;
@implementation GlobalVars
{

}
@end
Aixenprovence answered 26/10, 2013 at 8:25 Comment(7)
Where should I put the NSArray and the extern? (I mean, inside the m file..)Arraign
Sorry, but this is plain wrong. The extern declaration has to be in the .h file, so that other compilation units including this file know about it.Qumran
remove line with (Philip's post) h from this m, and write extern or this line with h in other *.m'sAixenprovence
@izzy, you must write two files *.m to make sense with externAixenprovence
@skippy: The GlobalVars.h and GlobalVars.m posted in the question are correct and there is no problem with it (as far as I can see).Qumran
@Martin, no. In m file is: NSArray *farmerlist; and in h: extern NSArray *farmerlist; h is a part of m. Is this correct?Aixenprovence
@skippy: That is correct (have a look at the first example in https://mcmap.net/q/17012/-how-do-i-use-extern-to-share-variables-between-source-files). It is also good practice because it ensures that the definition of the variable uses the same type as the declaration.Qumran

© 2022 - 2024 — McMap. All rights reserved.