Static string variable in Objective C on iphone - iphone

How to create & access static string in iPhone (objective c)?
I declare static NSString *str = #"OldValue" in class A.
If i assign some value to this in class B as str = #"NewValue".
This value persists for all methods in class B. But if I access it in class C (after assignment in B) I am getting it as OldValue.
Am I missing something? Should i use extern in other classes?
Thanks & Regards,
Yogini

Update: As of Xcode 8, Objective-C does have class properties. Note, it's mostly syntactic sugar; these properties are not auto-synthesized, so the implementation is basically unchanged from before.
// MyClass.h
#interface MyClass : NSObject
#property( class, copy ) NSString* str;
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
static NSString* str;
+ (NSString*) str
{
return str;
}
+ (void) setStr:(NSString*)newStr
{
if( str != newStr ) {
str = [newStr copy];
}
}
#end
// Client code
MyClass.str = #"Some String";
NSLog( #"%#", MyClass.str ); // "Some String"
See WWDC 2016 What's New in LLVM. The class property part starts at around the 5 minute mark.
Original Answer:
Objective-C doesn't have class variables, which is what I think you're looking for. You can kinda fake it with static variables, as you're doing.
I would recommend putting the static NSString in the implementation file of your class, and provide class methods to access/mutate it. Something like this:
// MyClass.h
#interface MyClass : NSObject {
}
+ (NSString*)str;
+ (void)setStr:(NSString*)newStr;
#end
// MyClass.m
#import "MyClass.h"
static NSString* str;
#implementation MyClass
+ (NSString*)str {
return str;
}
+ (void)setStr:(NSString*)newStr {
if (str != newStr) {
[str release];
str = [newStr copy];
}
}
#end

Unlike Java, where a static variable is scoped for all instances of a class, static in C means that a variable is accessible only from within the file where it is declared. It allows you to do things like declare a static variable inside a function, which sets the value only the first time through, like this.
One thing you haven't mentioned is the relationship between classes A, B, and C. If they are in an inheritance hierarchy, and you're expecting the static variable to be inherited as in Java, the method described by zpasternack will work.
If the three classes are unrelated, and you just want to access the value declared in A, then extern is a more appropriate way to go. In this case, you want to declare the variable as extern in ClassA.h, then define it in Class.m. As long as ClassB and ClassC import ClassA.h, they will be able to link against the same extern definition.
One fine point is that, instead of using extern by itself, it's more robust to use OBJC_EXPORT, which is defined in objc-api.h and handles compiling under C++ as well. Here's a code sample:
// ClassA.h
OBJC_EXPORT NSString* commonString;
...
// ClassA.m
NSString* commonString = #"OldValue";
// ClassB.m
#import "ClassA.h"
...
commonString = #"NewValue"; // Can be inside a function or method
Of course, using externed variables in this way creates an infamous, much-maligned global variable, which is fragile in that anyone can read or write it, and access is uncontrolled. This is the simple approach, and answers your question about using static vs. extern. However, as a design principle, the encapsulation provided by wrapping the variable with class methods is much safer, albeit more complex. In object-oriented languages, when the effect you're trying to achieve is that of a class-static method, encapsulation is probably the right way to go.

Related

Objective-C Constant variable not work, occurs "Property not found on object of type"

In Java, the following code is fine, no error.
class ConstantA{
public static String MY_TEST = "My Test";
}
import ConstantA;
Class TestClass{
public void test(){
System.out.println(ConstantA.MY_TEST); // it's work fine.
}
}
According to above java concept, it doesn't work on Objective-C,
in ConstantA.h file
extern NSString * const MY_TEST;
#interface ConstantA : NSObject
#end
in ConstantA.m file
NSString * const MY_TEST = #"My Test";
#implementation ConstantA
#end
in main.m file ( error occurs here)
#import "ConstantA.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSLog(#"%#",ConstantA.MY_TEST); // error: Property 'MY_TEST' not found on object of type 'ConstantA'
}
}
Can help me to solve this problem?
thanks so much
#import "ConstantA.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSLog(#"%#", MY_TEST); // error: Property 'MY_TEST' not found on object of type 'ConstantA'
}
}
Use directly the global variable, without class name:
MY_TEST and NOT ConstantA.MY_TEST.
Use NSLog(#"%#",MY_TEST);.
MY_TEST is not scoped by ConstantA in any way; it's simply a global variable. Objective-C doesn't support class-level "static variables" like you'd find in Java or C++. Classes can only have instance variables
You can sorta imitate a class variable with a global variable, but it's still just a global; it's not namespaced to the class in any way.
Put your Global string in the .h file
property (nonatomic, retain) NSString *gMyString;
In the .m file
synthetize gMyString;
It should work... I had the same problem last week !

Objective C - Getter and setter properties for enum

I am a complete newbie at Objective-C. I have an enum as follows:
typedef enum _XLBadgeManagedType {
XLInboxManagedMethod = 0,
XLDeveloperManagedMethod = 1
} XLBadgeManagedType ;
I want to have getter and setter methods for it, such that if something happens, I set XLInboxManagedMethod to 1. How would I go about doing it?
Your code is just defining an enum type. It's a static, compile-time constant that is not changed. You use enums by declaring an instance of one, then changing it to one of the constant values you defined. If your enum looks like:
typedef enum _XLBadgeManagedType {
XLInboxManagedMethod = 0,
XLDeveloperManagedMethod = 1
} XLBadgeManagedType;
Then your property could look like:
#property (nonatomic, assign) XLBadgeManagedType myEnum;
And its use may look like:
- (void)someMethod {
self.myEnum = XLInboxManagedMethod;
self.myEnum = XLDeveloperManagedMethod;
// etc...
}
You do not change the values of enums. They stay as they are.
They are symbolic constants. You can not change it.

Use of asterisk in variable names

The book "iPhone Programming. The Big Nerd Ranch Guide" cites the following method (page 96)
(void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *) views {
MKAnnotationView *annotationView = [views objectAtIndex:0];
id <MKAnnotation> mp = [annotationView annotation];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate], 250, 250);
[mv setRegion:region animated:YES];
}
I'm confused because of the asterisk usage. The line that begins with "MKAnnotationView" and the following one can be represented in an abstract fashion by:
ObjectType variableName = [object message];
Questions:
In the first case an asterisk precedes the variable name, but not in the second. Why?
In the case where the asterisk is used, should not be the pointer the assigned to nil?
Thanks.
I tend to think of it as what variable types require an asterisk, not what variable names require an asterisk. Objective C doesn't allow you to allocate objects on the stack like so:
// Declare an NSObject. Won't work.
NSObject myObject;
Instead, all objects must be dynamically allocated on the heap using pointers like so:
// Declare a pointer to an NSObject. Will work.
NSObject* myObject = [[NSObject alloc] init];
id is a special Objective C keyword that just means "A pointer to some Objective C object". This may or may not inherit from NSObject and is dynamically typed. What's important to note is that, while there is no asterisk, this is still a pointer to an object:
// Same as before. Will work.
id myObject = [[NSObject alloc] init];
The only difference is that the compiler has no information about what myObject is.
As a finishing note, id <MKAnnotation> is exactly the same as a regular id, but with some extra information for the compiler. Read it as "a pointer to some Objective C object that behaves like an MKAnnotation". MKAnnotation, in this case, is the name of a Protocol whose required methods you are declaring that particular id to implement.
id is already defined as a pointer to a struct. If you look at its definition in objc.h, you would that id is defined as,
typedef struct objc_object {
Class isa;
} *id;
Since it is already a pointer to an objc_object, you can create pointers to objects without using the asterisk as,
id myObject;
Also saying that an object is type id gives the compiler absolutely no information about the object except its class which comes from the isa property.
An NSObject on the other hand is defined as,
#interface NSObject <NSObject> {
Class isa;
}
To create a pointer to an object of NSObject or one of its subclass (such as MKAnnotationView), you would declare it as,
NSObject *myObject;
MKAnnotationView *myObject;
We are putting the asterisk here to denote that it is a pointer.
Specifying the protocol(s) next to the type gives the compiler more information for static-type checking.
You should check out this article for a brief introduction to the differences between id and NSObject. For an in-depth understanding, checkout this article on the Objective-C runtime.
ObjectType is normally something like "pointer to a MKAnnotationView", which is represented in Objective-C as it is in C: "MKAnnotationView *". Exceptions include the "id" type, various integer and floating point types (including their typedefs), enums (which are really integer types), and some small structs like CGRect.

declare global variable in nsobject class (objective C)

I declare a variable and some methods in the global nsobject class like
#interface classGlobal : NSObject {
NSString *myGuid;
}
#property(nonatomic,assign)NSString *myGuid;
and i synthesize in the .m class. but when i try to access the myGuid variable in the same class (classGlobal.m) then it shows the error "instance variable 'myGuid' accessed in class method".
So please suggest how i solve this issue.
It means that instance variables cannot be accessed from class methods. A class method is declared using a + instead of a -. If you need to use global variables I suggest you take a look at this question which answers it pretty well. And here is another one.
The compiler complains, that you are using myGuid in a scope, where it is not accessible/defined. The declaration of myGuid in the interface part does not define a global variable, but an instance member variable. If you need a global variable (say, becaue you have to access it from a class method declared with + instead of -), declare as usual in your .m file:
MyClass.m:
static NSString* myGuid = nil;
+ (void) someClassMethod {
if( myGuid == nil ) ...
}

Pass and access structures using objective-c

I want to know how to pass structures to another function and subsequently access that structure in the called function. I'm developing for the iPhone and the reason I'm using structs is so that I can eventually pass data as structs to a server being built in C.
Here's the structure:
struct userInfo{
NSString *firstName;
NSString *lastName;
NSString *username;
NSString *email;
NSString *ipAddress;
double latitude;
double longitude;
};
Here I'm simply fetching some user inputed data along with some CoreLocation data and the iPhone's IP Address:
- (IBAction)joinButton {
struct userInfo localUser;
localUser.firstName = firstName.text;
localUser.lastName = lastName.text;
localUser.username = username.text;
localUser.email = emailAddress.text;
localUser.ipAddress = localIPAddress.text;
localUser.latitude = currentLocation.coordinate.latitude;
localUser.longitude = currentLocation.coordinate.longitude;
[myNetworkConnection registerWithServer:&localUser];
}
function handling the struct:
- (void)registerWithServer:(struct userInfo*)myUser {
printf("First name is: %s", myUser.firstName);//error when compiling
}
the complier throws this error: request for member 'firstName' in something not a structure or union. Is that struct out of scope when I try to access it in the second function?
You are passing in a pointer to a struct. Use the -> operator, not the dot.
myUser->firstName
I can't help but think you should really make that a proper objective-C object with properties - more work but then it'll all behave better and live within the NSObject ecosystem.