Extern Variable initialization - objective-c

In my project I am working on it since last 6 months , i was using #define to define all URL in a FileManager class.but Now I came to know that I have to use two different URL depend on condition so dynamically I can not change #define values.so I decided to Use extern variables in FileManager class instead of #define.
as
in interface file
extern NSString *Url;
extern NSString *loginUrl;
in m file
#implementation UrlManager
+ (void) initialize{
Url=#"http://xxxxxxxxxx.com";
loginUrl=[NSString stringWithFormat:#"%#%#",Url,#"/ipad.php?method=audLogin&username="];
}
but when I am importing URLManager.h , and using loginUrl, still its null , please help me how can I initialize extern variables so that I can use these in app.

First, if you have a URL, use NSURL to reference it.
Secondly, name your globals consistently and descriptively. AliRootURL and AliLoginURL would be descriptive and has a prefix (Ali -- change it) to make it easy to recognize.
Finally, don't use globals at all. Instead, have class methods on your URLManager class (which really should be prefixed, too) that return the URLs:
#interface AliURLManager:NSObject
+(NSURL*)rootURL; // no need to prefix methods in your custom classes
+(NSURL*)loginURL;
#end
That way, you can implement those methods to initially return URLs directly:
+(NSURL*)rootURL
{
// use a static and dispatch_once() if you want only one instance
return [NSURL URLWithString:#"http://foobar.com/"];
}
+(NSURL*)loginURL
{
NSURL* root = [self rootURL];
NSURL* loginURL;
... calculate loginURL here... (http://stackoverflow.com/questions/718429/creating-url-query-parameters-from-nsdictionary-objects-in-objectivec)
return loginURL;
}
That way, you could easily refactor your code to, say, read URLs from the defaults database or have a different configuration for development.
Bummer -- but I can understand the desire to not change lots of files. Should be search and replaceable, though (at least, would be if your variables were a bit more descriptive).
If you are going to initialize the globals via +initialize, you need to ensure that the class is tickled. Add [URLManager class]; to your applicationDidFinishLaunching:

Related

Using an "extern" global before it is properly initialized

I'm trying to determine if there is an elegant solution to this issue.
Say I have a global defined in some header:
Constants.h:
extern NSString *someGlobal;
And then I wish to use this global in some other class:
Foo.m
NSString *localVariable = someGlobal;
This all works just fine if I initialize the global like this:
Constants.m:
NSString *someGlobal = #"Some String Literal";
But lets say I need to initialize the global to something that isn't a compile-time constant. In such cases I typically do this:
Constants.m:
#implementation Constants
+ (void)initialize {
someGlobal = ... // some non-trivial initialization
}
#end
Now I have a potential problem in Foo.m. If no reference has been made to the Constants class when I try to use someGlobal, the result is nil. A workaround is to do:
Foo.m (or in some app startup code):
[Constants class];
That will trigger the initialize method of the Constants class and someGlobal will be properly initialized. As long as this is done before any runtime use of someGlobal, things work fine.
Is there a better way to initialize extern globals with non-compile time constants without the need to call code such as [Constants class] at app startup?
A more idiomatic way in Objective-C is using a singleton instead of multiple globals. Here is how:
#interface Globals
#property (readwrite,nonatomic) NSString *myString;
#property (readwrite,nonatomic) int myInt;
+(Globals*) instance;
#end
+(Globals*) instance {
static dispatch_once_t once;
static Globals *inst;
dispatch_once(&once, ^{
inst = [[Globals alloc] init];
inst.myString = #"Some String Literal";
inst.myInt = 42;
});
return inst;
}
Now you can use your globals like this:
NSLog(#"Global string: %#", [Globals instance].myString);
NSLog(#"Global string: %d", [Globals instance].myInt);
No, there is no better way. Logically, if some piece of code must execute before a variable is intialized, you have to take steps to make sure that happens.
You could arrange the flow of your program's code so as to guarantee that the Constants class get initialized before any other piece of code executes which needs it. For example, by tweaking the order in which things are initialized in your program and following the order of code execution from main() on down to prove to yourself that it works. But short of that (and the safest thing in any case), you would use your technique to force it to be made valid right before you use it.
Like dasblinkenlight's answer, this may not be exactly what you are looking for but it's another approach.
I would make class methods that returns the value you are looking for like this:
+(NSString *)someConstant {
static NSString *constant;
if(constant == nil)
constant = //your initialization here;
return constant;
}
Then where you need to use it just call [Constants someConstant];
Other random thoughts:
A constant that isn't some compile time value isn't really what extern variables are for and this method insures that the variable is initialized every time you use it. The class using the constant has to know about your class anyway or it wouldn't have imported its header file

About declaring constant in objective-c

if you don't take #define into account, there are two notorious ways for declaring constants in objective-c project:
// Util.h
extern NSString * const MyConstant;
// Util.m
NSString * const MyConstant = #"value";
or the other directly in header file
// Util.h
static NSString *const MyConstant = #"value";
Now, there comes two questions:
1)
Both works, the second method is quite convenient, as I have only one place for editing values.
However as I saw from Apple .h files, the first method is always preferred, and I wonder if there are any downside, with the static method.
2) Looking at Apple docs, we often encounter very long constant name like: NSTextInputContextKeyboardSelectionDidChangeNotification. In the case you used a long constant name like that, what convention would you generally use to assign a value. If I want to use something descriptive I could use #"nsTextInputContextKeyboardSelectionDidChangeNotification", but sounds little odd.
I won't explain you all the specifics of the two types of constant declaration - in short:
The first one separates constant declaration from constant definition. Only declaration is included by header files. It also conveniently hides the actual value of the constant.
The second one is more problematic - the header file contains both declaration and definition of the constant. That means that whenewer you include the header, the constant is created again. I don't think this will work correctly when included from multiple files.
The second question - no problem with long constant names. Your example is a bit extreme but there is nothing wrong with it.
EDIT: Adding more information about the static NSString* const in header.
Let's have a header A.h:
//A.h
static NSString *const MyConstant = #"value";
and file including it A.m
//A.m
#import "A.h"
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
First note that headers are removed by the preprocessor before compilation. That means that before compilation there won't be any A.h file and A.m will look like this:
//A.m
static NSString *const MyConstant = #"value";
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
Let's create another constant header B.h and implementation file B.m with exactly the same contents:
//B.h
static NSString *const MyConstant = #"value2";
//B.m
#import "B.h"
printB() {
NSLog(#"B.h Constant: %#", MyConstant);
}
Note that the constant is declared twice, with the same name and different values. This is possible because static makes the constant private for the file that includes it. If you remove the static, you'll get a compilation error because the compiler will find two public global constants with the same name.
In theory it is possible to use static NSString* const in header files and everything will work correctly but as you can see, it doesn't do exactly what you want and can be a source of difficult-to-find bugs. That's why you should use static only from implementation files.
Just to add - the second way that you have shown doesn't make much sense.
If you are going to declare and define a constant in one place such as this, it's usually done in the .m file and not the .h file.
You do it this way for constants that are only going to be used in the class itself and not exposed.

So very lost on initializing const objects

I have tried many times to understand the const keyword, but it just doesn't work out for me.
I want to declare an object that cannot be changed, which is to say, a constant object. For example, in the .h file:
extern MyClass *use_this_object;
and in the .m file:
MyClass *use_this_object;
+ (void) Initialize {
use_this_object = [MyClass new];
}
Now, where can I put a const so that other classes can access use_this_object but not modify it (assuming MyClass is immutable), while the MyClass class can initialize the variable?
Is this even possible? Or should I be using a static method to retrive the constant and not declare it extern at all?
There is no such thing as a "const object" in Objective-C. There are const pointers and there are immutable objects. A const pointer to an immutable object is what you're talking about, but you can't allocate those at run time.
For objects that can be allocated at compile time (and I only know of one, NSString), you can do this:
NSString * const kMyString = #"string";
This is a constant pointer to an (immutable) NSString. You read these things right-to-left.
To create what you want, you need a function or method with an internal static like this:
+ (Something *)something {
static Something *something = nil;
if (! something) {
something = [Something new];
}
return something;
}
This is preferable to using globals anyway for things other than true constants (like strings and integers).
It is up to you to make sure that Something is immutable.
EDIT Just a note about the above code. This is just an example of how to create a static object at runtime. There are many ways to do it with various trade-offs including using +initialize with a file static (which is currently my preferred way to create a singleton). Don't take the above code as the one-and-only-way. It's just the way that is closest to const because no other part of the program can get directly to the pointer.
I’d use the static method, seems much simpler.

Key for objc_setAssociatedObject keeps changing address

I'm trying to make a key for use in objc_setAssociatedObject, as in this question:
How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
I have a MyConstants.h file which defines
static NSString *myConstant = #"MyConstant";
This file is then included in MyFramework via MyFramework.h
#import ...;
#import "MyConstants.h"
#import ...;
Then in my project, the framework is included in all files via the .pch header.
#import <Cocoa/Cocoa.h>
#import <MyFramework/MyFramework.h>
This all works as expected. The problem is that when I call objc_setAssociatedObject or objc_getAssociatedObject with myConstant as the key, it doesn't work. The reason is clear enough from the log:
- (void)doSomething:(id)key { //Gets called with myConstant
NSLog(#"Associating with key at %p", &key);
objc_setAssociatedObject(self, &key, object, policy);
}
- (void)doSomethingElse:(id)key { //Gets called with myConstant
NSLog(#"Seeking association for key at %p", &key);
NSLog(#"Will find %#", objc_getAssociatedObject(self, &key));
}
Which logs:
Associating with key at 0x7fff5fbfecdf
Seeking association for key at 0x7fff5fbfecef
Will find (null)
You'll notice that the pointers shift.
However, calling the same method again shows the same pointers. It's as if the constant IS a constant, in the file that imports it, but not for the whole project.
Q: How can I correctly define and import headers with constants so that a constant is actually at a constant place in memory?
If two files have static char something, each one will have its own copy--they do not represent the same piece of memory. If you want two files to have access to the key, you must instead do this in your header:
extern const char MyConstantKey;
And this in one implementation file (.c or .m):
const char MyConstantKey;
This defines a single one-char-wide location in memory, to which all files will point when you use &MyConstantKey.
I have edited the original post to show what I think the error was. The other poster's answer may well have also been an excellent solution, but how I resolved the problem was like so:
As the &key indicates, I was passing key directly to the methods, as in myMethod:(id)key. Somehow, de-referencing this pointer was different in different cases. By simply changing all the methods to myMethod:(void *)key and passing in &key, the problem immediately went away. Or ensuring I was using a static char and not a static NSString *
I don't understand it fully, but it works.

Large Hidden Constant in Objective C

I have a large constant (an NSString with 10^6 values). Because of its size I would like to declare it at the end of the source file (so I don't have to scroll through it every time I want to edit my code). Also because of its size I would like it to be a constant so I can load it at compile time instead of runtime. Also, because I do not want it accessible to outside users I do not want to declare it as extern in the header file.
I have it declared as a constant using the code below in the implementation file, however it is giving me a "Use of undeclared identifier 'hugeConstantString'" if I move it past the #end of the implementation (for obvious reasons).
NSString *const hugeConstantString = #"a_whooooooole_lotta_characters";
I've checked this out: Constants in Objective-C but it didn't tell me anything I didn't know already. Maybe my brain is fried, but: is there any way that I can define this huge constant AFTER my implementation and still have it accessible? Or if I declare it in another header file and import it, will it then be accessible to others?
Thanks!
I'm not sure such a large string is a good idea, but if you're going to use it, I suggest putting it in its own header file.
MyLongStringConstant.h
#define kLongString #"..."
MyClass.h
....
#import "MyLongStringConstant.h"
...
//Do something with kLongString
...
If you want to have it accessible in every file of your app, import the header inside your apps myApp_Prefix.pch file, which is imported into every file.
I am going to save the conversation of Why are you doing that and just post a simple solution for you. Thanks to Tommy in the comments here is a simpler version.
#import "LargeStringTest.h"
#implementation LargeStringTest
//Declare the string
static NSString *hugeConstantString;
- (id)init {
self = [super init];
if (self) {
NSLog(#"Large String %#", hugeConstantString);
}
return self;
}
//Place all other code here
//Assign the string
static NSString *hugeConstantString = #"a_whooooooole_lotta_characters";
#end