uninitialized extern NSString usage - objective-c

In the following class:
MGTileMenu
various extern NSString's are defined in the following way for use as notifications:
.h
extern NSString *MGTileMenuWillDisplayNotification; // menu will be shown
.m
NSString *MGTileMenuWillDisplayNotification;
It gets used as follows:
[[NSNotificationCenter defaultCenter] postNotificationName:MGTileMenuWillDisplayNotification
object:self
userInfo:nil];
My question is this: The extern NSString MGTileMenuWillDisplayNotification never gets initialized to any value - but this code works. I would have expected the implementation in the .m file to be:
NSString *MGTileMenuWillDisplayNotification = #"MGTileMenuWillDisplayNotification";
Why is this not necessary and what is going on here?

This means that the actual variable is defined in other parts of the program. Probably within some framework or library. You do not even have to have the related source.
The extern keyword tells the linker to look up the symbol table for a symbol named MGTileMenuWillDisplayNotification . (I think that would be a static variable, but not sure whether it coud be something else.)
NSString* tells the compiler to tread the memory where your pointer points to as NSString object. As usual. It is just that it is declared somewhere else and most probalby properly initialized somewhere else. It is in your scope of responsibility to ensure that it really is an NSString object which the documentation of the framework/library should tell you.

Related

Equivalent of public static final variables

I understand that placing the word extern before a variable declaration in a header file declares the existence of a global static variable without initialising it. I also understand that if I import the file containing the extern variables, I can reference them without a class/file name. But where does one define them and their values?
What I am trying to do is create a class of constants with global constants that I want to use throughout an iOS application's code.
Does one put them inside the interface like this?
Example.h
#import <Foundation/Foundation.h>
#interface Constraints : NSObject
{
extern NSString * const PREFS_NAME;
}
Or does one put then outside of the interface like this
Example.h
#import <Foundation/Foundation.h>
extern NSString * const PREFS_NAME;
#interface Constraints : NSObject
{
}
Then in the implementation .m file how would one initialise the extern values?
Inside the implementation area like this?
Example.m
#import "Constraints.h"
#implementation Constraints
/**PRefecences name for the application**/
const NSString * PREFS_NAME = #"MyApp_Prefs";
#end
Or do initialise them outside of the implementation area like this:
Example.m
#import "Constraints.h"
/**PRefecences name for the application**/
const NSString * PREFS_NAME = #"MyApp_Prefs";
#implementation Constraints
#end
Or do I provide them their initial values in a constructor? or some arbitrary a static style method with + in front of it i.e. +(void) setAppConstraints;
I have tried several combinations, but always run into errors, such as "Redefinition of 'xVariable' with a different type". Or a something about "extern does not have an initialise interface" (or something like that, I forget). So I want to know how to declaire and initialise them properly to form the same role as public static final variables in Java.
Also what are the limits of the extern command? I know I can extern an NSInteger or NSString, but what about NSArray?
I am asking this question because there seems to be to much misleading, or incomplete, information regarding the use of extern in Objective-C. Many of the answers seem speculatory. My hope is for this question to be a good resource not only for me, but to limit further similar questions about the basics of extern.
You define it's value in the file inside which it's declared, which in your case is Example.m; You can still re-assign this variable, so the declaration in Example.h would look like this:
extern NSString * PREFS_NAME;
This way every file that imports Example.h has access to this variable. The equivalent of public static final in Objective-C is const. If you also want it to be public you should make it be a class instance variable, but in this case you don't need it because it's already accessible everywhere. So in this case it would be:
// .m file
NSString* const PREFS_NAME = #"MyApp_Prefs";
// .h file
extern NSString* const PREFS_NAME;
Also notice that const NSString* is different from NSString* const. The latter is a const pointer to NSString. The former hasn't sense even if it's a correct syntax. In Objective-C the const qualifier doesn't affect objects, instead there are mutable and immutable classes. It would have sense in C++ meaning that you can use just const methods on the instance.
extern is used to signal the compiler that you will be using a variable or a function that is defined in another compilation unit.
When you say extern const NSString *PREFS_NAME, you're saying "Replace all references in this compilation unit to PREFS_NAME to the variable PREFS_NAME as it is defined in another file." So when you try to assign PREFS_NAME in your .m, all you're doing is trying to assign a variable that, though it has a name, it doesn't exist. Declaring a variable extern is only a declaration of a variable or function, not a definition of that variable or function. It lets the compiler know that the name is in use, and that the linker will take care of what to do with it, but even if you provide a type here, it doesn't actually set aside space for the variable, it's expecting the space to be set aside in the compilation unit that's actually defining the variable.
You compile three or four different source code files together, three of them may declare:
extern int buffer[];
And one may declare
int buffer[BUFSIZE];
In its global scope, and the linker's job is to resolve the three declared references to extern buffer to the fourth's actual definition of the buffer.
extern is to C variables and functions much as #class is to Objective-C classes, it's a forward declaration, a promise to the compiler that you don't have to freak out when you see a name that's undefined here, because the linker will answer whatever lingering questions you may have.

Conditional compilation:initializer element is not a compile-time constant

I have to assign a variable to a constant like this (the code below is at the beginning of my file code, before #implementation):
#ifdef DEBUG
NSString *hostStr=[[NSString alloc]init];
hostStr=#"xxx.mycompany.com";
static NSString * const host = hostStr;
#endif
If i do like so:
#ifdef DEBUG
static NSString * const host = #"xxx.mycompany.com";
#endif
That will work.
Actually, in my real case, host will contain the value of a global value (declared in the app delegate and initialized in another view controller). But for the sake of simplifying my problem, i use this example (since both cases give me the same error).
How can i fix this problem please. Thanx in advance.
There are a couple of issues here.
Constants that are set outside of any function cannot be "dynamic". This means that the compiler has to know what the constant value is before the program is run. If you say something like this:
static int x = myFunction(459);
The compiler can't know what myFunction() will return until the program is actually run.
This is why:
NSString *hostStr=[[NSString alloc]init];
causes a syntax error. The compiler will not execute any code when making a constant.
The solution is simple:
NSString *host=#"www.mycompany.com";
Notice that I didn't use the "static" qualifier. That would make "host" only available to the code in the file it was declared it. Dropping the "static" qualifier makes it global.
To access this global variable from another file, the other file needs to declare
extern NSString *host;
at which point the other file will be able to see the contents of "host".
Another thing to point out, is that this:
NSString *hostStr=[[NSString alloc]init];
hostStr=#"xxx.mycompany.com";
doesn't really do much. You create an NSString with alloc/init, then immediately assign
a constant to it, moving the NSString you created aside, without disposing of it, creating a memory leak. (If you have ARC enabled, then it's a non-issue. ARC knows all.)

How to declare a pointer member variable in a class with ARC in Objective C?

I have a class RemoteImageLoader which has a method loadImage:
- (void) loadImage: (NSString*) url setTarget:(NSData **)target;
I used a NSData** here because I need to indirectly return NSData* , just like some typical method:
- (BOOL)save:(NSError**)
since the method will actually invoke another asynchronous method, I have to save the target as a member variable so I can access it later. but when I define a NSData ** member variable:
#interface RemoteImageLoader : NSObject
#property NSData** target;
#end
the compiler complains that "Pointer to non-const type 'NSData*' with no explicit ownership". I've done some search on google, but find no answer of it. Could anyone help me with this? Thanks a lot
and I've tried to replace the declaration to
#interface RemoteImageLoader : NSObject
#property NSData * __autoreleasing * target;
#end
but the problem still exists
The ARC Transition Notes recommend you declare indirect pointers to NSData and NSError objects in your case with __autoreleasing, like (NSData * __autoreleasing *)target;
__autoreleasing is known as a lifetime qualifier which tells the compiler to return an autoreleased instance of the object.
Because of this, a rewrite of your method signature is required.
- (void) loadImage: (NSString*) url setTarget:(NSData* __autoreleasing *)target;
Be warned, __autoreleasing objects are extremely short lived. Declare your NSData** as __strong to override the default __autoreleasing for a longer lifetime.
I think your method signature is going to cause trouble. A caller is very likely to assume that the pointed-to pointer is filled in as soon as your method returns. Similarly, they are very likely to pass the address of a stack variable which won't be valid for long. Lastly, your method provides no means for the caller to know when the pointed-to pointer has been filled with a value.
You are probably better off taking a completion block from the caller. The completion block will receive an NSData pointer as an argument. Something like:
- (void) loadImage: (NSString*) url completionHandler:(void (^)(NSData* data))block;
This also mirrors the underlying framework API I presume you're using, which is always good for reducing "impedance mismatch".
As for the specific narrow issue you're encountering from the compiler, I suspect the issue is that the compiler can't know if it should emit retains and releases when you assign to *target. It wants you to explicitly declare the ownership characteristic of the pointed-to pointer. I can't check at the moment, but I guess that declaring it as __strong NSData** target would work. That is, it's not interested in whether target owns what it's pointing at, since one can't own a pointer. It's interested in whether the NSData* pointer to which target points owns the NSData object to which it points.
I can't be sure what you are trying to do without seeing your code, but why are you trying to create a pointer to an NSData object (Which is a pointer to NSData). Because you are creating a pointer to a pointer which is probably why you are getting the error. Try removing one of the pointers and see what happens.
Generally, when you do this sort of thing outside of ARC you do something like:
NSData* returnedParm;
[someObj doSomething:&returnedParm];
on the caller's side. I don't see your equivalent of my returnedParm above. (I've never tried this with ARC, but I'd think the basics would have to be similar.)
Declaring a property as NSData** is declaring a pointer to a non-object and it would not be retained (because there's no object to retain).
My guess is that you should prototype your function as:
-(void)doSomething:(NSData* __autoreleasing*)theReturnParm
and assign to it inside that function using *theReturnParm = something;.
Then on the calling side you'd have your returnedParm as an autoreleased value (so if you want to preserve it you should relatively quickly assign it to a strong pointer).

Clarification on Asterisks and Underscores in Cocoa Syntax

I recently picked up the BigNerdRanch book on Cocoa for Mac OSX and am looking for a good explanation around the use of asterisks * and underscores _ that seem to be inconsistently used throughout the language. Some examples:
These appear to be functionally equivalent:
NSString* myString;
NSString *myString;
Sometimes new variables get an underscore, while others dont:
NSString _myString;
NSString myString;
And sometimes it gets all crazy:
NSString *myString;
NSString *_myString;
I've also seen variations with asterisks in methods:
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender
willSpeakWord:(NSRange)characterRange
ofString:(NSString *)string;
So what is the functional use of the asterisk or the underscore, when should you use (or not use) either, and when should they be used in combination?
The * indicate a pointer, which all Objective-C objects are. (You pass around pointers to these objects in memory). At a basic level these are normal C pointers. If I remember correctly You could access some data in an Objective-C object by going object->data, just like you do with pointers to C structs.
The _ is a Cocoa (and some other languages) convention, meaning "a piece of data that should be considered private to this object".
Objective-C has a #private declaration, but it's also a relatively new addition to the language - if your code is more than 2 or 3 years old (or targeting much older versions of OS X) it might not use #private
Because of this initial lacking of language infrastructure, the _ is (often) used by the Cocoa community to mark "Hey, you probably shouldn't set or read this directly, please".
So:
When dealing with Objective-C classes you always need the * to follow the class name (like NSString), because they are always pointers. I'm confused about your NSString somestring line in your code - either that'll generate a complier warning or will crash when you try to use it
The _ indicates private data. You would do something like NSString* _name in a #interface section of your Objective-C class. You would use _name by itself if you were calling or operating on that data in a method in your class.
So say you created a Student class:
// Student.h
#interface Student : NSObject {
NSString* _name;
}
- (NSString*) name_as_caps;
#end
// Student.m
#implementation Student
- (NSString*) name_as_caps {
return [_name uppercase];
}
Here we declare a class with a "private" data member: _name. Our Student class needs to return this capitalized for some reason, so we created a method to do that, where we use _name and call the uppercase method on it.
We needed to refer to the type (or class name) or _name a few times here: once, to declare the variable. For name_as_caps we needed to say: this method returns a pointer to an NSString object, thus we used NSString *.
As an addendum to Ryan's answer, when you see something like
-(void)speechSynthesizer:(NSSpeechSynthesizer *)sender willSpeakWord:(NSRange)character
RangeofString:(NSString *)string;
the things like (NSSpeechSynthesizer *)sender just state the type of the argument - in this case NSSpeechSynthesizer*

What class of Objective-C variable is this?

I am working my way through some Objective-C code that I did not write and have found a variable declaration style that I am unfamiliar with. Can anyone tell me the scope of the variable 'myVar' in the class implementation below? Note that this appears in the '.m' file and not the interface declaration.
#implementation MyClass
#synthesize ivar1, ivar2;
NSString* myVar; // <- What is the intent?
- (id)init {
...
#end
To me the intention appears to be similar to that of a member variable. What are the advantages of declaring a variable in this way instead of using an ivar in the #interface declaration?
It's just a plain old global variable. There's only one instance of it, and it can be accessed by any code within the same file translation unit (the final file you get after running the preprocessor). Other translation units (that is, other .m files) can also access that global variable, but in order to do so, they need to use an extern statement:
extern NSString *myVar;
extern says "this is the name of a global variable, but it's defined in a different translation unit". The linker resolves all of the extern declarations at link time.
a poorly named global variable...
I'm not too experienced in ObjC but I'd say that is a global.