Objective C, Linking Error with extern variable - objective-c

I have a very simple java code like this. I don't have any idea how to do this in Objective C. Especially, the static part which calls the getLocalAddress() method and assign it into the static string variable. I know how to set a static variable and a static method in Objective but I dont know how to implement that static { } part in java.
Thanks in advance...
public class Address {
public static String localIpAddress;
static {
localIpAddress = getLocalIpAddress();
}
public Address() {
}
static String getLocalIpAddress() {
//do something to get local ip address
}
}
I added this in my .h file
#import <Foundation/Foundation.h>
extern NSString *localIpAddress;
#class WifiAddrss;
#interface Address : NSObject {
}
#end
And my .m file looks like
#import "Address.h"
#import "WifiAddress.h"
#implementation Address
+(void)initialize{
if(self == [Address class]){
localIpAddress = [self getLocalIpAddress];
}
}
+(NSString *)getLocalIpAddress{
return address here
}
-(id)init{
self = [super init];
if (self == nil){
NSLog(#"init error");
}
return self;
}
#end
And Now I am getting a linking error and it complains about "extern NSString *localIpAddress" part. If I change the extern to static, it works fine. But what I wanted to do is that I want make the scope of "localIpAddress" variable as grobal. Since if I put "static" in front of a variable in Objective-C then the variable is only visible in the class. But this time, I want to make that as a grobal variable. So my question is how to make "localIpAddress" variable as a grobal variable which is initialized once when the first time Address class is created.. Thanks in advance...

You have declared the variable in your .h file (told the compiler that it exists and of which type it is), now you need to define it in your .m file (actually make it exist).
Just add NSString *localIpAddress; to your .m file, or better yet:
NSString *localIpAddress = nil;
(That is, give it a sane default value)
The extern keyword means: there is a variable of the given name and type, but actually lives in an "external" file that needs to be linked. So for each extern declaration, you need to actually define the variable in one of your implementation files (.c, .cxx/.c++/.cpp, .m ; this mechanism is part of the C standard on which Objective-C is standing).

Unless you want other modules to access localIpAddress directly without using your class, declare it as static inside your implementation (.m) file.
extern should be used in the following scenario:
A module defines the variable as global. That particular translation unit must not use extern
Other modules need to access that variable directly. Those particular translation units must use extern
Since this is not your case, do the following in your implementation (.m) file:
static NSString *localIpAddress;
// …
+(NSString *)getLocalIpAddress{
return localIpAddress;
}
and remove
extern NSString *localIpAddress;
from your header (.h) file.
Whenever you need to get that address, use
NSString *addr = [Address getLocalIpAddress];
By the way, the convention is that getter methods do not start with get. For instance, you could’ve named that method localIpAddress.

A quick fix would be to move the localIpAddress variable into your implementation file. Then you wouldn't need to use the extern keyword. Really, if you think about it, you have a static accessor, so there's no reason to have the variable declaration itself in the header.
Let me clarify:
Interface:
#import <Foundation/Foundation.h>
#interface Address : NSObject {
}
+(void) initializeLocalIpAddress;
+(NSString *) localIpAddress;
#end
Implementation:
#import "Address.h"
#import "WifiAddress.h"
NSString *localIpAddress;
#implementation Address
+(void) initializeLocalIpAddress
{
//get the local ip address here
localIpAddress = ...;
}
+(NSString *) localIpAddress
{
return localIpAddress;
}
-(id)init {
if ((self = [super init])) {
NSLog(#"init error");
}
return self;
}
#end

Related

Static NSLocalizedString

After checking a number of topics, I still can't figure something out : what is the (best?) way to use static NSLocalizedString, i.e. to statically allocate a NSLocalizedString and access it easily.
Ideally, I would want to do something like that (which won't compile, with an Initializer element is not a compile-time constant error):
//Somewhere in my header
static NSString* mystring = NSLocalizedString(#"min", nil); //Error : "Initializer element is not a compile-time constant"
#implementation myClass
(NSString*)aMethod
{
return myString;
}
#end
I know NSLocalizedString is a macro defined by #define NSLocalizedString(key, comment) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:#"" table:nil], but that is not helping much :S.
Why ?
Long story short, to prevent the multiple definition of the same string in multiple parts of a document (which would prevent a one-stroke edit in my whole app).
Consider the example, where the redundancy of the definition is quiet explicit :
//MyDelegate.h
#property IBoutlet NSTextField* myTextField;
//MyDelegate.m
#implementation MyDelegate.m
#synthetize myTextField;
-(void)setTextFieldToDefaultValue
{
[myTextField setStringValue:NSLocalizedString(#"Name",#"This field is used to write one's name");
}
-(BOOL)isTextFieldStringDefault:(NSString*)myString
{
return [[myTextField stringValue] isEqual:NSLocalizedString(#"Name",#"This field is used to write one's name")];
}
#end
Of course, it makes more sense in a project which is quiet dense and big, where the string would be used in difference methods, and in a context where you have use of a lot of similar localized strings.
Generally this should all be in your implementation file:
static NSString* myString = nil;
#implementation myClass
+ (void)initialize
{
myString = NSLocalizedString(#"min", nil);
}
- (NSString *)aMethod
{
return myString;
}
#end
Well one more way you can write the same without using initialize method above:-
static NSString* mystring=nil;
-(NSString*)aMethod
{
mystring = NSLocalizedString(#"min", nil);
return mystring;
}

Using ObjC class extensions' vars in class category

I declare a class extension interface adding vars to it. Is it possible to access those vars in a category of that class?
Sure - any variable is accessible through the runtime, even if it isn't visible in the #interface:
SomeClass.h
#interface SomeClass : NSObject {
int integerIvar;
}
// methods
#end
SomeClass.m
#interface SomeClass() {
id idVar;
}
#end
#implementation SomeClass
// methods
#end
SomeClass+Category.m
#implementation SomeClass(Category)
-(void) doSomething {
// notice that we use KVC here, instead of trying to get the ivar ourselves.
// This has the advantage of auto-boxing the result, at the cost of some performance.
// If you'd like to be able to use regex for the query, you should check out this answer:
// http://stackoverflow.com/a/12047015/427309
static NSString *varName = #"idVar"; // change this to the name of the variable you need
id theIvar = [self valueForKey:varName];
// if you want to set the ivar, then do this:
[self setValue:theIvar forKey:varName];
}
#end
You can also use KVC to get iVars of classes in UIKit or similar, while being easier to use than pure runtime-hacking.

acces c++ datamember in objective-c class

What I'm trying to do is quite simple but I probably have the syntax wrong.
I have an Objective-C Interface with a Note class parameter. Note.h is a C++ class that basically looks like this:
#include <string>
using namespace std;
class Note {
public:
string name;
Note(string name){
this->name = name; // ERROR: Cannot find interface declaration for 'Note'
}
};
This is my controller where is using Note. I changed the file extension to .mm
#class Note;
#interface InstrumentGridViewController : UIViewController {
#public
Note* note;
}
#property (nonatomic, retain) Note* note;
And this is how I'm using it:
#implementation InstrumentGridViewController
#synthesize note;
- (void)buttonPressed:(id)sender {
note = Note("fa"); // ERROR: Cannot convert 'Note' to 'Note*' in assignment
NSLog(#"naam van de noot is %s", note->name); // ERROR: Cannot find interface declaration for 'Note'
}
I'm getting these three errors though (I've added them as comments on the right lines)
Any idea how what I'm doing wrong?
You need to allocate the Note object using new:
- (void)buttonPressed:(id)sender
{
if (note != 0)
delete note;
note = new Note("fa");
NSLog(#"naam van de noot is %s", note->name.c_str());
}
However it doesn't seem right to be doing that in the button press action method...
Also don't forget to delete it in your object's dealloc method:
- (void)dealloc
{
delete note;
[super dealloc];
}
Lastly your #property attribute retain is wrong as it's not an Objective-C object; instead use assign and better still make it readonly.
A better way to initialise most objects is by using a const reference to them rather than a copy:
Note(const string &name)
{
this->name = name;
}
Your Note C++ class is not valid. Change its declaration to be instead:
class Note {
public:
string name;
Note(string aName) {
name = aName;
}
};
Also change your InstrumentGridViewController:
- (void)buttonPressed:(id)sender {
note = new Note("fa");
NSLog(#"naam van de noot is %s", note->name);
}
- (void)dealloc {
delete note;
[super dealloc]; // Use this only if not using ARC
}
The Cannot find interface declaration for 'Note' error was caused because of #class Note in my Obj-C controller .h file. It's strange because I have a working sample project where #class is used and it works fine.
I fixed it using forward declarations the way they are described here and here.
// used typedef struct <classname> <classname>
typedef struct Note Note;
// instead of
#class Note
The above goes in the Obj-C header file. In the .mm file goes the #import "Note.h" statement

subclassing issue with static constant variable in iOS

I have Class A and Class B.
Class B subclasses Class A
Class A has a static constant NSString variable
In a method of Class B I need to use the static constant NSString variable of Class A. What are my options ?
I tried declaring the same thing again, but caused problem (internal inconsistency), tried without using (says, variable undeclared).
Any idea how I can solve this problem ?
Thanks.
Make a class method returning that constant, like this:
+(NSString*) constString {
return myConstString;
}
You can declare static constant strings in the .h file. That way they are public and can be used by other classes that import the header file, including your subclass.
Alternatively, you can declare a reference to the string in your Class B using extern:
extern NSString *const MyString;
That basically tells the compiler that the value of that string is defined elsewhere in the code and it doesn't have to worry about it.
By "Class A has a static constant NSString variable", I assume you're referring to having something like the following defined in your Class A .m file:
static NSString * const MyString = #"MyString";
#implementation ClassA
#end
To allow Class A and its subclasses to see the value, you can do something like this:
MDClassAPrivate.h:
#import <Foundation/Foundation.h>
static NSString * const MDBlahBlahKey = #"MDBlahBlah";
MDClassA.h:
#import <Foundation/Foundation.h>
#interface MDClassA : NSObject {
}
#end
MDClassA.m:
#import "MDClassA.h"
#import "MDClassAPrivate.h"
#implementation MDClassA
#end
MDClassB.h:
#import "MDClassA.h"
#interface MDClassB : MDClassA {
}
#end
MDClassB.m:
#import "MDClassB.h"
#import "MDClassAPrivate.h"
#implementation MDClassB
#end
By moving the static const variables to a separate file, you can allow any class to import them in the implementation file.
My personal solution to this problem would be a pseudo-protected method that returns the constant string.
// ClassA.m
#interface ClassA ()
- (NSString *)constantString;
#end
#implementation ClassA
- (NSString *)constantString {
return #"MyConstantString";
}
#end
Then following on into your subclass:
// ClassB.m
#interface ClassA ()
// This is a method redeclaration to avoid build warnings
- (NSString *)constantString;
#end
#implementation ClassB
- (void)someMethod {
NSString *theConstantString = [self constantString];
// do stuff...
}
#end
The assumption here is that your constant string really is constant, if you want it to be dynamic you would need to modify this solution slightly, but it can still handle it.
You can use 'userDefaults' to set a value that is accessible in the whole application.
[[NSUserDefaults standardUserDefaults] setObject:object forKey:#"objectKey"];

Objective-C Static Class Level variables

I have a class Film, each of which stores a unique ID. In C#, Java etc I can define a static int currentID and each time i set the ID i can increase the currentID and the change occurs at the class level not object level. Can this be done in Objective-C? I've found it very hard to find an answer for this.
Issue Description:
You want your ClassA to have a ClassB class variable.
You are using Objective-C as programming language.
Objective-C does not support class variables as C++ does.
One Alternative:
Simulate a class variable behavior using Objective-C features
Declare/Define an static variable within the classA.m so it will be only accessible for the classA methods (and everything you put inside classA.m).
Overwrite the NSObject initialize class method to initialize just once the static variable with an instance of ClassB.
You will be wondering, why should I overwrite the NSObject initialize method. Apple documentation about this method has the answer: "The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.)".
Feel free to use the static variable within any ClassA class/instance method.
Code sample:
file: classA.m
static ClassB *classVariableName = nil;
#implementation ClassA
...
+(void) initialize
{
if (! classVariableName)
classVariableName = [[ClassB alloc] init];
}
+(void) classMethodName
{
[classVariableName doSomething];
}
-(void) instanceMethodName
{
[classVariableName doSomething];
}
...
#end
References:
Class variables explained comparing Objective-C and C++ approaches
As of Xcode 8, you can define class properties in Obj-C. This has been added to interoperate with Swift's static properties.
Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as: #property (class) NSString *someStringProperty;. They are never synthesized. (23891898)
Here is an example
#interface YourClass : NSObject
#property (class, nonatomic, assign) NSInteger currentId;
#end
#implementation YourClass
static NSInteger _currentId = 0;
+ (NSInteger)currentId {
return _currentId;
}
+ (void)setCurrentId:(NSInteger)newValue {
_currentId = newValue;
}
#end
Then you can access it like this:
YourClass.currentId = 1;
val = YourClass.currentId;
Here is a very interesting explanatory post I used as a reference to edit this old answer.
2011 Answer: (don't use this, it's terrible)
If you really really don't want to declare a global variable, there another option, maybe not very orthodox :-), but works... You can declare a "get&set" method like this, with an static variable inside:
+ (NSString*)testHolder:(NSString*)_test {
static NSString *test;
if(_test != nil) {
if(test != nil)
[test release];
test = [_test retain];
}
// if(test == nil)
// test = #"Initialize the var here if you need to";
return test;
}
So, if you need to get the value, just call:
NSString *testVal = [MyClass testHolder:nil]
And then, when you want to set it:
[MyClass testHolder:testVal]
In the case you want to be able to set this pseudo-static-var to nil, you can declare testHolder as this:
+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
static NSString *test;
if(shouldSet) {
if(test != nil)
[test release];
test = [_test retain];
}
return test;
}
And two handy methods:
+ (NSString*)test {
return [MyClass testHolderSet:NO newValue:nil];
}
+ (void)setTest:(NSString*)_test {
[MyClass testHolderSet:YES newValue:_test];
}
Hope it helps! Good luck.
On your .m file, you can declare a variable as static:
static ClassName *variableName = nil;
Then you can initialize it on your +(void)initialize method.
Please note that this is a plain C static variable and is not static in the sense Java or C# consider it, but will yield similar results.
In your .m file, declare a file global variable:
static int currentID = 1;
then in your init routine, refernce that:
- (id) init
{
self = [super init];
if (self != nil) {
_myID = currentID++; // not thread safe
}
return self;
}
or if it needs to change at some other time (eg in your openConnection method), then increment it there. Remember it is not thread safe as is, you'll need to do syncronization (or better yet, use an atomic add) if there may be any threading issues.
As pgb said, there are no "class variables," only "instance variables." The objective-c way of doing class variables is a static global variable inside the .m file of the class. The "static" ensures that the variable can not be used outside of that file (i.e. it can't be extern).
Here would be an option:
+(int)getId{
static int id;
//Do anything you need to update the ID here
return id;
}
Note that this method will be the only method to access id, so you will have to update it somehow in this code.
(Strictly speaking not an answer to the question, but in my experience likely to be useful when looking for class variables)
A class method can often play many of the roles a class variable would in other languages (e.g. changed configuration during tests):
#interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
#end
#implementation
+ (NSString*)theNameThing { return #"Something general"; }
- (void)doTheThing {
[SomeResource changeSomething:[self.class theNameThing]];
}
#end
#interface MySpecialCase: MyCls
#end
#implementation
+ (NSString*)theNameThing { return #"Something specific"; }
#end
Now, an object of class MyCls calls Resource:changeSomething: with the string #"Something general" upon a call to doTheThing:, but an object derived from MySpecialCase with the string #"Something specific".
u can rename the class as classA.mm and add C++ features in it.
Another possibility would be to have a little NSNumber subclass singleton.