Objective C Global Variable - objective-c

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.

Related

Having trouble with class extension in Objective C, mac OS. Getting error 'NSInvalidArgumentException', no visible #interface declares the selector

I'm working on exercise 3 on page 76 in Apple's developer pdf in the class categories and extensions section, "Programming with Objective C", found here: (https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html)
my XYZPerson header looks like this:
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property (readonly) NSNumber *height;
#property (readonly) NSNumber *weight;
-(NSNumber *)measureHeight;
-(NSNumber *)measureWeight;
#end
My implementation file looks like this:
#import "XYZPerson.h"
#property (readwrite) NSNumber *height;
#property (readwrite) NSNumber *weight;
#end
/////////////////////
#implementation XYZPerson
-(NSNumber *)measureHeight{
if(!_height){
_height = [[NSNumber alloc] init];
}
return _height;
}
-(NSNumber *)measureWeight{
if(!_weight){
_weight = [[NSNumber alloc] init];
}
return _weight;
}
#end
And then in my main file I have:
#import <Foundation/Foundation.h>
#import "XYZPerson.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
XYZPerson *aPerson = [[XYZPerson alloc] init];
[aPerson setHeight: [NSNumber numberWithInt:72] ];
[aPerson setWeight: [NSNumber numberWithInt:190] ];
NSLog(#"%# %#",[aPerson measureHeight],[aPerson measureWeight]);
}
return 0;
}
There might be more than one error unrelated to the issue I brought up, I'm a huge novice at Objective C right now. The exact compiler error I am getting is on the line that says,
[aPerson setHeight: [NSNumber numberWithInt:72] ];
The compiler error reads, "ARC Semantic Issue. No visible #interface for 'XYZperson' declares the selector 'setWeight:'.
Oh doh.
You will not be able to call that method from OUTSIDE of your class implementation.
Overriding properties in class extensions in the .m file doesn't expose that to the world.
At least not to the compiler.
That's the whole point.
It's for when you want properties that are readonly to the world outside of that object, but internally to the object you still want to be able to conveniently do things.
You declared the property read-only in the header file, so to everything outside of the class, the properly is read-only, and thus a setHeight: method doesn't exist.
You have made he properties height and weight read only. Then in the main function you are using setWeight and setHeight.
You can't do that as that would be writing the weights but you explicitly set them to read only.
Place your properties in .m with class extension:
#interface XYZPerson ()
#property (readwrite) NSNumber *height;
#property (readwrite) NSNumber *weight;
#end
See Apple docs about class extension:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

Why I cannot do self.strID=nil;?

self.strID=nil;
It pops error saying:
reason: '-[BGFacebookHandler setStrID:]: unrecognized selector sent to instance 0x20525240'
* First throw call stack:
However, I declared the property as read write privately:
#property (nonatomic) NSString * strID;
The catch is it's also declared as read only publicly through protocol. Basically I am implementing that public property with a private property.
What's wrong with my approach?
Obviously I can do #synthesize, but doing so means using obsolete version of objective-c
This is a run time error. Not a compile error. The code compile just fine by the way.
Declaring your implementation property as part of your interface or a private extension changes nothing at runtime. There must me something else missing in your code because I have the following code fragment that works fine:
#protocol Facebook<NSObject>
- (NSString*) strID;
#end
#interface BGFacebookHandler : NSObject<Facebook>
#property (retain) NSString * strID;
#end
#implementation BGFacebookHandler
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
BGFacebookHandler *object = [[BGFacebookHandler alloc] init];
object.strID = #"Hello";
NSLog(#"strID = %#",object.strID);
}
return 0;
}

Can't access to extern variable from another class

I've created a phonegap plugin to give a variable to native code. That works fine. Now I need to access to this value from other classes so I decided to create an extern NSString.
.h
extern NSString *lkwid;
#interface MyPlugin : CDVPlugin {
}
#property (retain, nonatomic) NSString *lkwid;
-(void) setMyValue:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
.m
#import "MyPlugin.h"
#implementation MyPlugin
#synthesize lkwid;
NSString *lkwid = #"";
-(void) setMyValue:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
NSString* callbackId = [arguments objectAtIndex:1];
lkwid = callbackId;
NSLog(#"set value %# ",lkwid);
//NSLog shows the correct value from Javascript
}
Now I want to access to lkwid from MainViewController.m (MyPlugin.h is imported) but lkwid is empty. Why?
I think that you are confusing instance variables and static (class) variables. The extern NSString *lkwid declares a globally accessible variable. #property (retain,nonatomic) NSString *lkwid declares an instance variables to your object. If you want it to set the global variable, which would then be shared by ALL the instances, then you should not synthesize the property andimplement the getter and setter yourself in the following manner:
- (NSString*) lkwid
{
return lkwid;
}
- (void) setLkwid:(NSString*)value
{
lkwid = value;
}
Make sure that you don't also declare an instance variable with the name lkwid.

Incomplete Implementation Example Help!

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.

Objective-C dot syntax or property value?

I keep reading that dot syntax is possible but I keep getting errors that the struct does not contain members I am referencing. Perhaps its not the dot syntax so I have included details of what I am doing in hopes of a solution:
// MobRec.h - used as the objects in the MobInfo array
#import <Foundation/Foundation.h>
#interface MobRec : NSObject {
#public NSString *mName;
#public int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
// MobDefs.h - array of MobRecords
#interface Mobdefs : NSObject {
#public NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo; // is this the right property?
-(void) initMobTable;
#end
// MobDefs.m
#import "Mobdefs.h"
#import "Mobrec.h"
#implementation Mobdefs
#synthesize mobInfo;
-(void) initMobTable
{
// if I use traditional method I get may not respond
[mobInfo objectAtIndex:0 setmName: #"doug"];
// if I use dot syntax I get struct has no member named mName
mobInfo[1].MName = #"eric";
}
// main.h
MobDefs *mobdef;
// main.m
mobdef = [[Mobdefs alloc] init];
[mobdef initMobTable];
although both methods should work I get erros on both. What am I doing wrong? My best thoughts have been that I am using the wrong #property but I think I have tried all. I am performing alloc in main. Ideally I would like to for this use dot syntax and cant see why its not allowing it.
A couple of things: (edit: original point #1 removed due to error)
Although the dot syntax is supported, the array index syntax for NSArray is not. Thus, your call to mobInfo[1] will not be the same as [mobInfo objectAtIndex:1]; Instead, mobInfo will be treated as a simple C-style array, and that call would be almost guaranteed to result in a crash.
You should not define variables in your header file as you do in main.h. The line MobDefs *mobdef; belongs somewhere in main.m.
edit: Here is how it should look:
MobRec.h
#interface MobRec : NSObject {
NSString *mName;
int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
MobRec.m
#implementation MobRec
#synthesize mName;
#synthesize mSpeed;
#end
MobDefs.h
#interface MobDefs : NSObject {
NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo;
-(void) initMobTable;
#end
MobDefs.m
#import "MobDefs.h"
#import "MobRec.h"
#implementation MobDefs
#synthesize mobInfo;
-(void) initMobTable
{
// option 1:
[(MobRec*)[mobInfo objectAtIndex:0] setMName:#"doug"];
// option 2:
(MobRec*)[mobInfo objectAtIndex:0].mName = #"eric";
// option 3:
MobRec *mobRec = [mobInfo objectAtIndex:0];
mobRec.mName = #"eric";
}
main.m
MobDef *mobdef = [[MobDefs alloc] init];
[mobdef initMobTable];
...
[mobdef release]; // don't forget!
You need to either cast the object returned by -objectAtIndex:, or use a method call on it:
[[mobInfo objectAtIndex: 0] setMName: #"doug"];
or
((Mobrec *) [mobInfo objectAtIndex: 0]).MName = #"doug";
[mobInfo objectAtIndex:0 setmName: #"doug"];
There is no objectAtIndex:setmName method, so you're going to have to explain what you think this is even supposed to do.
mobInfo[1].MName = #"eric";
Use objectAtIndex to look something up in an NSArray object.