I'm pretty new to cocoa and Xcode, I've done some basic C coding, but I pretty much suck at objective-c and cocoa, so please excuse me for any stupid mistakes I make. My problem is with these global variables I'm using.
I have a global NSString variable declared in the header file, and it's used in the main file like so:
//AppController.h
-(IBAction)button1:(id)sender;
-(IBAction)button2:(id)sender;
extern NSString *hi
//AppController.m
-(IBAction)button1:(id)sender
{
NSString *const hi = #"Hello";
}
-(IBAction)button2:(id)sender;
{
NSLog (#"%#", hi);
}
However when I click run the build fails and I get the error message:
"_hi", referenced from:
Some extra info:
Undefined symbols for architecture x86_64: "_hi", referenced from: -[AppController gallery:] in AppController.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
If you know what this means and/or how to fix it please help me. Thanks
You need to provide a global definition for hi. Move your declaration:
NSString *const hi = #"Hello";
to someplace outside of any method. I'm not really sure what you want button1: to do, but it doesn't seem necessary at all for your implementation.
I assume Luke likes to:
Set the string to a specific value after button one is clicked,
and retrieve it again after button two is clicked.
AppController.h
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject{
NSString * string;
}
-(IBAction)button1:(id)sender;
-(IBAction)button2:(id)sender;
#end
AppController.m
#import "AppController.h"
#implementation AppController
-(IBAction)button1:(id)sender
{
string = #"Hello";
}
-(IBAction)button2:(id)sender;
{
NSLog (#"%#", string);
}
#end
When defining global variables and constant strings, etc., this is usually how I do it:
MDAppController.h:
#import <Cocoa/Cocoa.h>
extern NSString * const MDShouldShowInspectorKey;
extern NSString * const MDShouldShowViewOptionsKey;
extern BOOL MDShouldShowInspector;
extern BOOL MDShouldShowViewOptions;
#interface MDAppController : NSObject <NSApplicationDelegate> {
IBOutlet NSWindow *window;
}
- (IBAction)hideInspector:(id)sender;
#end
MDAppController.m:
#import "MDAppController.h"
NSString * const MDShouldShowInspectorKey = #"MDShouldShowInspector";
NSString * const MDShouldShowViewOptionsKey = #"MDShouldShowViewOptions";
BOOL MDShouldShowInspector = NO; // default value
BOOL MDShouldShowViewOptions = YES; // default value
#implementation MDAppController
+ (void)initialize {
NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
[defaultValues setObject:
[NSNumber numberWithBool:MDShouldShowInspector]
forKey:MDShouldShowInspectorKey];
[defaultValues setObject:
[NSNumber numberWithBool:MDShouldShowViewOptions]
forKey:MDShouldShowViewOptionsKey];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSUserDefaults *uD = [NSUserDefaults standardUserDefaults];
MDShouldShowInspector = [[uD objectForKey:MDShouldShowInspectorKey] boolValue];
MDShouldShowViewOptions = [[uD objectForKey:MDShouldShowViewOptionsKey] boolValue];
}
- (IBAction)hideInspector:(id)sender {
NSLog(#"MDShouldShowViewOptionsKey == %#", MDShouldShowViewOptionsKey);
MDShouldShowInspector = NO;
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithBool:MDShouldShowInspector]
forKey:MDShouldShowInspectorKey];
}
#end
My question is why do you want to be extern? The best way here is to create a singleton, you should have all members as part of a class and avoid any global.
Hope this helps
Related
Can anyone tell me where I'm going wrong here please.
I have created an NSobject called BeaconData. The header file is:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface BeaconData : NSObject
#property (nonatomic, strong) NSMutableArray * jsonArray;
#property (nonatomic, retain) NSString * bMajor;
#property (nonatomic, retain) NSString * bMinor;
#property (nonatomic, retain) NSString * bUUID;
-(void) getData;
#end
The implementation file is then:
#import "BeaconData.h"
#define getDataURL #"http://www.eventav.biz/websitebeacons/library/json/files/beacons.txt"
#implementation BeaconData
#synthesize jsonArray, bUUID, bMajor, bMinor;
//Retrieve data
-(void) getData
{
extern NSString * bUUID;
extern NSString * bMajor;
extern NSString * bMinor;
NSURL * url = [NSURL URLWithString:getDataURL];
NSData * data = [NSData dataWithContentsOfURL:url];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Loop through Json Array
for (int i = 0; i < jsonArray.count; i++)
{
bUUID = [[jsonArray objectAtIndex:i] objectForKey:#"i_uuid"];
bMajor = [[jsonArray objectAtIndex:i] objectForKey:#"i_major"];
bMinor = [[jsonArray objectAtIndex:i] objectForKey:#"i_minor"];
}
}
#end
Next I try to call the Global variable bMajor in the main viewController.m file and print it out - just to see if it works, like this:
- (void)viewDidLoad {
[super viewDidLoad];
extern NSString * bMajor;
NSInteger beaconMajorInt = [bMajor integerValue];
NSLog (#"Beacon bMajor is %li", (long)beaconMajorInt);
But all I get is the following error:
Undefined symbols for architecture x86_64:
"_bMajor", referenced from:
-[ViewController viewDidLoad] in ViewController.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
You have declared your bMajor variable as a class property. This means that you must instantiate an instance of your BeaconData class to access the variable unless you include a class method.
However, in your code I see that you also want to make these variables global. It is redundant to declare a variable as a class property and then try to make it global. In objective-c simply declaring a variable outside the implementation section will make it global for all modules that import the file with the declaration. You do it like this:
NSString *bMajor = #"Your String";
#implementation BeaconData
// YOUR CLASS CODE
#end
You're using the extern keyword incorrectly. It should be used in .h class files to let anything that imports it know that they have access to this variable. You also must declare it like I showed in the .m class file
.h looks like this:
extern NSString *bMajor;
#interface BeaconData : NSObject
#end
Just because you can do this doesn't mean you should. Based on your code I would suspect what you want to do is turn your -getData instance method into a class method for a singleton that allows the class to manage these "global" variables while keeping with good coding practice.
This SO Q/A should provide you exactly what you need to create your singleton. I recommend you do it this way.
Then in your viewController you would access these variables by getting an instance of your class using the class method.
So I am quite new on OC programming, I come from Front-end background (i.e. HTML/CSS/JavaScript ...), so I understand basic concepts of programming :)
Basically I created a console application, with a simple FooClass.
FooClass.h
#import <Foundation/Foundation.h>
#interface FooClass : NSObject
#property (strong, nonatomic) NSString *username;
- (NSString *) username;
- (void) setUsername:(NSString *)username;
#end
FooClass.m
#import "FooClass.h"
#implementation FooClass
#synthesize username = _username;
- (instancetype) init
{
self = [super init];
if (self) {
}
return self;
}
- (NSString *) username
{
return _username;
}
- (void) setUsername:(NSString *)username
{
_username = username;
}
#end
And in the main.m file, where the app bootstraps.
#import <Foundation/Foundation.h>
#include "FooClass.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
FooClass *foo = [[FooClass alloc] init];
foo.username = #"a";
}
return 0;
}
XCode tells me that it cannot find property username on object of type FooClass. And I don't really have idea about it. Any one could help?
I am a bit late in posting the answer. Here are few things that you should consider.
Since you have a property username. You are not required to create methods for setters and getters. The compiler will create them for you. Simply remove the two statements.
No need to synthesize in .m as well.
Instead of #include use #import. import takes only one copy even if you try to add the file(s) directly or indirectly from other files as compared to include.
I'm steadily getting the hang of Objective-C, but am still very much a beginner and have a beginner-level question hopefully someone could shed some light on:
If I have a very simple project and want to set a constant that I'll use throughout—say, a NSDictionary with keys being month names and values being days in that month—how is this done? (I.e., what command form and where to put it?)
NOTE: If this example is already possible using built-in functions, perhaps we could just pretend it isn't for the purposes of this question ;)
The answer depends on the type of your constant. If all you need is an int or a double, you can use preprocessor and the #define CONST 123 syntax. For Objective C classes, however, you need to do a lot more work.
Specifically, you would need to hide the constant behind a class method or a free-standing function. You will also need to add a prototype of that method or function in the header file, provide a function-scoped static variable to store the constant, and add code to initialize it.
Here is an example using a simple NSDictionary:
Header: MyConstants.h
#interface MyConstants
+(NSDictionary*)getConstDictionary;
#end
Implementation: MyConstants.m
+(NSDictionary*)getConstDictionary {
static NSDictionary *inst = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
inst = #{
#"key1": #"value1",
#"key2": #"value2",
#"key3": #"value3"
};
});
return inst;
}
Usage:
NSString *val = [[MyConstants getConstDictionary] objectForKey:#"key2"];
The accepted answer is correct, but if you prefer operate with variable (not trough method). I can suggest this pattern:
#implementation MyClass
static NSSet *mySetOfObjects;
+ (void)initialize {
mySetOfObjects = [[NSSet alloc] initWithObjects:#"one", #"two", #"three", nil];
}
// Example usage:
+ (BOOL)isRecognizedString:(NSString *)searchItem {
return [mySetOfObjects containsObject:searchItem];
}
#end
As for me - it looks better.
For more details the source is here.
Let's assume you want to declare an NSString constant in your class that holds a url. In your header .h file you will need the following:
#import
extern NSString * const BaseURL;
#interface ClassName : NSObject {
You will then need to set it's value in your main .m file as follows:
#import "ClassName.h"
NSString * const BaseURL = #"http://some.url.com/path/";
#implementation ClassName
You can now access this constant throughout your class or subclasses. Here's an example of usage:
NSString *urlString = [NSString stringWithFormat:#"%#%#", BaseURL, #"filename.html"];
If your constants are strings then you can use this form:
MyObject.h:
extern NSString *const kJanuary;
....
extern NSString *const kDecember;
#interface MyObject : NSObject
{
...
}
#end
MyObject.m:
NSString *const kJanuary = #"January";
....
NSString *const kDecember = #"December";
#implementation MyObject
....
#end
You can then use the constant kJanuary, for example, from anywhere when using your class.
I want store a number as a global variable. What syntax do I use, and how can other parts of my application access that variable?
For a standard global variable (not persistent when the app is terminated and restarted) add this to a header file (*.h) of your choice:
extern NSInteger MYGlobalVariable;
Then put this in the implementation file; (*.m, *.c, *.cpp):
MYGlobalVariable = 0; // Or any other default value.
That is how you do a bread and butter global variable.
You probably want to use NSUserDefaults for this :
From anywhere in your code, you can set a value for a key :
int userAge = 21; // Just an example
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:[NSNumber numberWithInt:userAge] forKey:#"age"];
[standardUserDefaults synchronize];
}
And get it back from any other place :
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *age = nil;
if (standardUserDefaults)
age = [standardUserDefaults objectForKey:#"age"];
userAge = [age intValue]
You can also set an initial value :
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary
dictionaryWithObject:[NSNumber numberWithInt:13] forKey:#"age"];
[defaults registerDefaults:appDefaults];
Also, if you have complex data, you may want to create a wrapper class with setters and getters.
Define the variable in AppDelegate.h file. Create a property in .h file
#property (retain, nonatomic) NSString *str;
Then synthesize in AppDelegate.m file;
#synthesize str;
Later define a variable in you project prefix.pch file
#define DELEGATE ((AppDelegate*)[[UIApplication sharedApplication]delegate])
Use the value anywhere in your project
AppDelegate *a = DELEGATE;
a.str = #"value";
NSLog(#"value of variable str : %#",a.str);
To make a Variables that can be seen by the whole files in Objective c
for example you want to set your base url one time and in each class you append on it some extra strings,
go to main.m file, because this is the place the whole app will see it.
then outside main function, put your base url
NSString *baseurl = #"staging.nabdanet.com";
int main(int argc, char * argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
and when you want to access these class, all what you will do is this,
SomeClass.m
extern NSString *baseurl;
with the same name in both ;)
For persistent vars, use NSUserDefaults. This gets written on a file in the app sandbox.
For session vars (non-persistent), I use a singleton class with an NSMutableDictionary property to store variables.
To declare a global variable in either objective-C or Swift, you simply declare it outside the scope of any class/interface.
Objective-C:
#import "headerFile1.h"
#import "headerFile2.h"
BOOL isTrue = true;
int x = 1;
#interface exampleInterface (){
...
}
#end
#implementation exampleClass
...
isTrue= false; // can be used in the same way anyplace in your code
x=3; // anyplace means anyplace, even from other controllers
#end
Swift:
import UIKit
var x=45;
class SomeClass {
...
x=0; // This is also available from anyplace in your project
...
}
I am working on an example from a book that I got and it doesnt seem to be working I am getting the warning Incomplete implementation. When I run the program I get an error singal "EXC_BAD_ACCESS". The warning is in the .m file at the line return [NSString stringWithFormat:#"Name:... Does anyone know what I am doing wrong?
my .m file
#import "RadioStation.h"
#implementation RadioStation
+ (double)minAMFrequency {
return 520.0;
}
+ (double)maxAMFrequency {
return 1610.0;
}
+ (double)minFMFrequency {
return 88.3;
}
+ (double)maxFMFrequency {
return 107.9;
}
- (id)initWithName:(NSString *)newName atFrequency:(double)newFreq atBand:(char)newBand {
self = [super init];
if (self != nil) {
name = [newName retain];
frequency = newFreq;
band = newBand;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:#"Name: %#, Frequency: %.1f Band: %#", name, frequency, band];
}
- (void)dealloc {
[name release];
[super dealloc];
}
#end
My .h file
#import <Cocoa/Cocoa.h>
#interface RadioStation : NSObject {
NSString *name;
double frequency;
char band;
}
+ (double)minAMFrequency;
+ (double)maxAMFrequency;
+ (double)minFMFrequency;
+ (double)maxFMFrequency;
-(id)initWithName:(NSString*)name
atFrequency:(double)freq
atBand:(char)ban;
-(NSString *)name;
-(void)setName:(NSString *)newName;
-(double)frequency;
-(void)setFrequency:(double)newFrequency;
-(char)band;
-(void)setBand:(char)newBand;
#end
radiosimulation.m file:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSMutableDictionary* stations = [[NSMutableDictionary alloc] init];
RadioStation* newStation;
newStation = [[RadioStation alloc] initWithName:#"Star 94"
atFrequency:94.1
atBand:'F'];
[stations setObject:newStation forKey:#"WSTR"];
[newStation release];
NSLog(#"%#", [stations objectForKey:#"WSTR"]);
newStation = [[RadioStation alloc] initWithName:#"Rocky 99"
atFrequency:94.1
atBand:'F'];
[stations setObject:newStation forKey:#"WKFR"];
[newStation release];
NSLog(#"%#", [stations objectForKey:#"WKFR"]);
[stations release];
[pool drain];
return 0;
You are declaring the following property accessor/mutators (getter/setters) but are not implementing them in your .m file.
-(NSString *)name;
-(void)setName:(NSString *)newName;
-(double)frequency;
-(void)setFrequency:(double)newFrequency;
-(char)band;
-(void)setBand:(char)newBand;
You need to implement all 6 of these methods in the .m file if you want to remove the warning about incomplete implementation.
You are effectively saying in the .h file that this is what your object is going to do, then not doing it in the .m. It won't generate an error, as objective-c messaging means that the message will be handed up to NSObject to deal with, which will also not have any matching implementation, and the messages will just be silently ignored. I don't like the way that this is only shown as a warning - but there you go.
That said, I wouldn't create the properties like this (there are neater ways of doing this in objective-c using #property), I would remove those method declarations in the .h and replace them with:
#property (nonatomic, retain) NSString *name;
#property (nonatomic, assign) double frequency;
#property (nonatomic, assign) char band;
These property declarations go in the same place as method declarations.
and then add the following to the .m file:
#synthesize name;
#synthesize frequency;
#synthesize band;
This will avoid having to write all that boilerplate accessor/mutator code that you are currently missing. Again, these go in the same region of the code as method implementations. Effectively the compiler is going to create name and setName methods automatically.
This code is untested - but should point you in the right direction for tidying up the incomplete implementation. It may fix your access error too - but that may require more detailed look at a stack trace.
Another point I'm not sure the code as written even needs to use get/set methods or properties. You might try removing the method declarations from the .h and see if it works. It seems that all the accesses to name, frequency and band are all from within the object.