Scope NSArray in Objective-C - ios

In GLViewController.m file
At the very top of Implementation
NSArray* imageArray ;
Then in GLViewController.m inside GLViewController
- (id)initWithCoder:(NSCoder*)coder
{
imageArray = #[ #"baban.jpg",#"cete.jpg",#"cipan.jpg",#"kuc.jpg"];
}
Inside drawView in GLViewController.m
NSLog(imageArray[0]); //Fails
I've got it declared in the GLViewController.h file too:
NSArray* imageArray;
I am including GLViewController.h in the GLViewController.m

Your question is not clear. E.g. have you declared imageArray as an instance variable, a global variable, or possibly both – "At the very top of Implementation" could be read as "top of file", "top of implementation just below #implementation in braces" etc.
So we're guessing, let's hope it helps.
First, from what you report; that the init runs but in and instance method accessing the instance variables "fails" in some non-specified way; you may have two different variables declared in different scopes. Without a minimal reproducible example we can't be more specific. What you can try is to insert:
NSLog(#"Loc A imageArray #%p = %p", &imageArray, imageArray);
in various locations, where you change the "A" (to "B", "C"...) for each one, where you are accessing imageArray. This will print the address of the variable itself, from which you can determine whether you are always referring the same variable, and the variable's contents as an address, from which you can determine if the value is changing i.e. whether it's nil or the array it references has changed.
Second, you write "I've got it declared in the GLViewController.h file too". You normally do not declared instance variables in the .h file, though you can, and you do not re-declare global variables in a .h and doing so should produce a compiler error – you don't report such an error which leads to the possibility covered above that you may have two variables in different scopes.
If you wish to declare a global variable in file.m and have it accessible to other code in a different file then in file.h you include an extern declaration for it:
extern NSArray* imageArray;
The extern states that this line is not declaring the variable itself but that the variable is declared elsewhere and accessible. (You might wonder why you don't add extern to functions/methods, in those cases the extern is implicit.)
Hope that helps in some way. If this doesn't help at all I suggest you ask a completely new question providing more detail, the minimum reproducible example, etc. (If you edit this question now people may miss the edits thinking they've seen the question.) You can always delete this question to reduce clutter.

Related

Should Objective-C constants be wrapped by the implementation block?

I have an iOS application I'm writing. I've moved away from #define to create my constant values. I have a few questions regarding the use of these style declarations: NSString *const segueToMainMenu
If I'm using these internally, I'm placing them inside the .m file. However, should I be placing these wrapped around the #implementation block or outside of it or does it matter? I'm thinking inside, because they are specific to the implementation and not global, but I'm not sure. Any details are appreciated.
If I'm creating a more global scope using the extern keyword and I'm using a Constants file pair (Constants.h/Constants.m) do I need to place those in the #interface section and then define them in the #implementation section? What is the benefit of that vs the old way of just using a Constants.h file and including it with other headers? Why do I now need two files?
Is the standard practice still to name the constants with a "k" prefix (e.g. kAnimationDuration) or should I now be doing something like MainMenuViewControllerAnimationDuration? I'm imagining yes and if so, does it matter for the constants from number 1 (i.e. not extern) how I name them? In other words, are those visible outside of my implementation?
Clarification is much appreciated.
Doesn't matter whether you place them inside the implementation block or not—only methods are part of the class implementation, so the scope of constants won't change regardless.
The k prefix is a bit dated now. The usual way is to name constants as <prefix><name>, such as "MDSomeConstant".

Is it okay to create class local references without using #property in iOS?

In my ViewController I have created some objects that I need to use throughout various places in the controller itself, but nowhere outside of it. I have done it like this:
#implementation MyController1
NSString *myString;
- (void)myFirstMethod {
myString = #"hello world";
}
...
#end
I haven't put them in the header file nor have I defined them with #property in an interface declaration that would look like this:
#interface MyController1 ()
// could define myString with #property here
#end
I'm not having any problems with my code, but I wanted to make sure that I'm not violating safe practices. I know I could put them in the header or implementation file and use #private, but for the sake of code brevity I haven't. Is this okay to do? Does no longer having to use #synthesizehave any impact on this?
Thanks,
It's perfectly fine, just as long as you are aware of the difference between an instance variable (a.k.a. "member variable" or "ivar") and a static variable. For example, there will only be one string object associated with your myString variable in your example here (#"hello world"), no matter how many MyController1 objects you create, not one per instance of the class MyController1. So in this way, myString is not behaving like a property nor an instance variable.
Furthermore, the static variable's life cycle is longer -- it will outlive all instances of your MyController1 objects, only being deallocated when the program exits or if you explicitly do so, say if you allocated it on the heap to begin with (which you are not doing in the case of #"hello world", but of course could potentially do with other static variables).
There are pros and cons to both types/approaches. For example, ivars can keep track of object state, but this means each instance of your objects are bigger because they must each have memory allocated for that state. So if memory performance matters in your app, ivars shouldn't be used unless you need them. On the other hand, static variables are good for "one offs" -- things that are not associated with the object state, but often have to be protected if they can be written to by more than one object on more than one thread. These are just some contrasts... there are many others depending on what you're trying to do.
Re your final question about #synthesize, not using it just means that there won't be any auto-generated accessors for the variable, which is fine because the variable isn't an ivar and not associated with instances of the MyController1 object anyway.

iOS: understanding global variables

In my first ViewController ViewControllerTest1 I've got a global variable called counter.
counter is supposed to be increased every now and then in my app. Everything works fine:
#implementation ViewControllerTest1{
int counter = 0;
-(void)viewDidLoad
{...}
-(void)method {...}
}
Now if I declare another global variable called counter in my second ViewController ViewControllerTest2 XCode gives me an error.
I know I can just give it a different name, but why does that happen? Can I make sure only the globals of the certain ViewController that is active are in my memory?
Or am I doing something like a no go right now with globals like counter?
Is there something better?
If you want a symbol to be specific to a file, use the static keyword when declaring it.
Your declaration should look like
static int counter = 0;
At link time (after all the files were compiled), the global symbols are mixed up in the same file, and therefore, if two share the same name, an error is fired by the linker.
If you define a variable at file scope (which includes locations within a class definition but outside the ivar area or a method body), it will have extern linkage by default, which requires unique symbol names.
Make it a static variable (static int ...) and the problem will be resolved, because static symbol names only need to be unique within the file in which they are declared.
If you are accessing this variable outside this file intentionally, and so need to maintain extern linkage, you will need to name the other variable something else to distinguish the two.

getting value of global variable from .m to another .m

I have tried getting value of global variable from .m to another .m in 2 ways :
Firstly, I tried to import the viewController.m class in another .m class and tried to retrieve the value but I received a linker error.
Secondly, I simply created extern with the string name and then it started working perfectly.
My question is Why import gave me the error and how extern variable recognizes the string name from that particular class ?
( ie : What if I had 2 or 3 variables with the same name in different classes and I type extern and string name which value would I get since I am not importing any class.)
And, Add-on question : When DO you really import the class ?
You really import a class when you need access to it's properties and methods. That might have sounded too obvious, but that's what it really is. Classes are OOP's encapsulation concept personified.
Getting a variable from one point to another can be done in many ways, one could use the userdefaults facility, you could have the variable within the app delegate and then access the value in different classes or you could expose a property on your destination view controller and have the source view controller set that property before transitioning to the destination..to name a few. There are many more methods that are right and wrong depending on the scenario.
Importing a view controller A into another view controller B, simply to have access to a value in A seems like overkill (if not flat out an inappropriate use of the importing mechanism).
This question explains externs further, I have no exposure to them,
Objective-C static, extern, public variables
Hope this helps.
Firstly, I tried to import the viewController.m class in another .m
class and tried to retrieve the value but I received a linker error.
You NEVER import a .m (implementation) file. You only ever import .h (header) files.
My question is Why import gave me the error
In C, there is a rule that any (non-static) global function or variable with a certain name must be defined only once. For a variable, writing its type and name defines it. For a function, the implementation defines it. Thus, having global functions or variables of the same name in two different code files will fail. They will conflict.
Importing a file means take the entire source of the file and insert it there. When you "import" A.m into B.m, the entire source of A.m is inserted into B.m. That means every declaration in A.m now shows up in two code files in your program -- A.m and B.m, hence the error.
You can declare (but not define) a global variable or function more than once. You can declare a variable by using extern without defining it. Declaring it with extern allows you to use a variable even though it is not actually declared in the same code file.
and how extern variable
recognizes the string name from that particular class ? ( ie : What if
I had 2 or 3 variables with the same name in different classes and I
type extern and string name which value would I get since I am not
importing any class.)
As explained above, you can only have one non-static global variable with a given name in your program. It would be an error to have variables with the same name in different files. It is possible for non-static global variables and functions to be accessible from any other part of the program, by its name (and it works through the magic of linking).
Generally, the best practice for a global variable that will be used in many files, is to define it in a .m file (as you're doing), and the declare it extern in the .h file corresponding to that .m file. Then other files that import that header will automatically have access to that variable.
I know this has already been answered in detail, but I just want to offer some clarification.
Declare data that you want to keep private in your .m file. For data that you want to make accessible to other objects, declare in your header .h file. Use properties for all your declarations:
//myViewController.h
#interface myViewController: UIViewController
#property (assign) int myInt;
#property (strong) NSString* someString;
#end
You can access these properties from another object by passing a message to myViewController:
[myViewController setMyInt:20]; //setter
[myViewController someString]; //getter
Or you can use the equivalent dot notation
myViewController.myInt = 20;
myViewController.someString;
(you need to #include "myViewController.h" in any object that wants access to these)
Inside myViewController you access them using 'self':
self.myInt;
self.someString;
This is mediated access via the setter/getter. The backing variable can be accessed directly from inside myViewController (but not from any other object) as _myInt, _someString. This is not advised outside of init and dealloc methods.
A property encapsulates declaration of instance variables and creation of default getters and setters, so you do not need to make a separate variable declaration, and you only need to make your own accessors if you need to change the default behaviour.
Please also refer to my answer here:
Should I declare variables in interface or using property in objective-c arc?

Why must I define variables twice in the Header file?

Why must I define variables twice in the header file? What differences are there between these variables?
The first definition is here:
#interface MyController: UIViewController
{
NSInteger selectedIndex;
}
The second definition is here:
#property (nonatomic) NSInteger selectedIndex;
What you're seeing was required in earlier versions of Objective-C,
but isn't any more.
In the first versions of Objective-C used by NeXT up until the new
runtime was introduced (with Objective-C 2.0 on Mac OS X), all
instance variables had to be declared as part of the class's structure
in its #interface. The reason was that if you subclassed a class,
the compiler needed to know the instance variable layout of the class
so it could see at what offset to put the subclass's instance
variables.
When properties were introduced, synthesized properties had to be
"backed" by an instance variable in the class's structure. Therefore
you had to declare both an instance variable and the property.
All of the above is no longer true. Newer Objective-C is less fragile
in the way it looks up instance variable offsets, which has meant a
few changes:
not all instance variables need to be in the #interface. They can now be defined in the #implementation: though not in categories due
to the possibilities of clashing and other issues.
instance variables for synthesized properties can be inferred and created based on the property definition.
you can programmatically add instance variables to classes you're creating at runtime (only before you've registered the class as
available to the system).
So, to reiterate, you only needed to declare both the instance
variable and a synthesized property in older versions of the
Objective-C language. What you're seeing is redundant and should not
be considered a "best practice".
[Source]
As others have pointed out, it is no longer necessary to declare a backing instance variable for a synthesized property in the header.
To make this a bit clearer though: What you're seeing are not two declarations of the same variable, it is one declaration of the variable and one declaration of the property.
A property is basically a set of methods (in this case selectedIndex and setSelectedIndex:) that are typically used to access instance variables. There is a difference between a property and an instance variable. The property's setter/getter could do more than just set the variable, in your example, it could also e.g. update the UI to reflect the change of the selected index or the getter could infer the index from some other variable (in this case, there might be a selection index path), etc.
Synthesizing a property simply frees you of implementing those methods yourself and provides you with default implementations that simply set the variable, but you could also implement selectedIndex and setSelectedIndex: yourself just like any other method in which case you might either need the instance variable itself or omit it altogether (in case of an inferred property).
In modern runtime you do NOT need to declare them twice. Just use:
In you .h
#property (nonatomic) NSInteger selectedIndex;
The part between the {} is the declaration of the iVar. And with your #property you declare getter and setters. In modern runtime if you just use the code above you say basicly the same (your iVar is now _selectedIndex).

Resources