Declaring in Objective-C - objective-c

I am very new to Objective-C programming, and I have a question that has always puzzled me: why do you have to declare your variables in the header file, like this?
#interface MyViewController : UIViewController
{
NSString *myString;
}
Why not just declare them here (in the .m file):
#implementation MyViewController
- (void)viewDidLoad
{
NSString *myString;
}

The first declaration is a instance variable available to all instance methods. The second is local to the one method.
However it is possible to declare instance variables in the .m file:
#implementation MyViewController {
NSString *myString;
}
In fact this is the preferred way to declare instance variables that do not need to be exposed. Only declare things in the .h file that need to be available to other classes.

There are two different questions going on here.
To put it simply, the header file (.h) is a public gateway for everything else to see what your class is about without having to know anything about your implementation. Your header file should contain everything that you would want other classes to know about (i.e. public methods, properties).
You could easily declare things in the implementation file but then other classes would not know about them.
Secondly, in the example you provided you have put NSString *myString; in the viewDidLoad method. This means that such a variable would be only available in the scope of that method. Nothing else would be able to access it.

Related

How to add instance variables or properties to an XCTestCase?

I just learned, that I must declare instance variables and properties in a header file in Objective-C. So now i want to add instance variables to my XCTestCase subclass — but it turns out, XCTestCases come without header files.
How do i declare instance variables in my test cases?
You don't have to declare them in the header file at all. Instance variables and properties are commonly added within a private category in the implementation file:
#interface MyClass () {
BOOL _someVar;
}
#property NSString *someOtherVar;
- (void)_aPrivateMethod:(id)something;
#end
#implementation MyClass
...
#end

Instantiating multiple objects of the same class in Interface Builder results in shared property

I am trying to use NSPopUpButtons in my OSX program. In order to use KVO for its string and its index, I wrote a custom class (DLPopUpButtonManager).
#interface DLPopUpButtonManager : NSObject
#property NSArray *contentArray;
#property NSString *selectionString;
#property NSNumber *selectionIndex;
#end
That class works fine, when used only once in the program. But…
When I use more than one instance their contentArray is shared, meaning the two contentArrays point to the same instance. Huh?? That totally confuses me.
(Encapsulation, etc.)
I have two NSPopUpButtons that each are connected to an objects of class DLPopUpButtonManager. Those two classes are instantiated in Interface Builder though two objects. And in my AppDelegate I initialize them.
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet DLPopUpButtonManager *pUBM_1;
#property (weak) IBOutlet DLPopUpButtonManager *pUBM_2;
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self.pUBM_1 setContentArray:[NSArray arrayWithObjects:#"Female", #"Male", nil]];
[self.pUBM_2 setContentArray:[NSArray arrayWithObjects:#"Tall", #"Short", nil]];
[self showDetails:nil];
}
I find that both instances (confusingly and unwanted) use the same contentArray.
I investigated using breakpoints and saw that I have indeed two separate instances of DLPopUpButtonManager, but their contentArrays have the same pointer value.
Printing description of $20: <DLPopUpButtonManager: 0x6080000100b0>
Printing description of $23: <DLPopUpButtonManager: 0x6080000100c0>
Printing description of $25: <__NSArrayI 0x600000223ba0>
(
Tall,
Short
)
Printing description of $24: <__NSArrayI 0x600000223ba0>
(
Tall,
Short
)
(lldb)
I can’t find anything like that through Google or here on SO. Who can tell me, what I am doing wrong here?
I uploaded a little sample program to GitHub (https://github.com/donnerluetjen/PopUpButtonEtude).
Thanks for any input on that issue.
Try moving the underlying ivars for your your array and selection index properties into a private extension in the .m file, to ensure that they are not in fact static variables.
#interface DLPopUpButtonManager (){
NSArray *_contentArray;
NSUInteger _selectionIndex;
}
Thanks to tjboneman I could solve my problem, and I read some more about instance variables and static instance variables. Here is what I found after some serious searching:
From Apple's docs, The Objective-C Language | Defining a Class:
Class Interface
...
Note: Historically, the interface required declarations of a class’s instance variables, the data structures that are part of each instance of the class. These were declared in braces after the #interface declaration and before method declarations:
#interface ClassName : ItsSuperclass
{
// Instance variable declarations.
}
// Method and property declarations.
#end
Instance variables represent an implementation detail, and should typically not be accessed outside of the class itself. Moreover, you can declare them in the implementation block or synthesize them using declared properties. Typically you should not, therefore, declare instance variables in the public interface and so you should omit the braces.
...
Class Implementation
The definition of a class is structured very much like its declaration. It begins with an #implementation directive and ends with the #end directive. In addition, the class may declare instance variables in braces after the #implementation directive:
#implementation ClassName
{
// Instance variable declarations.
}
// Method definitions.
#end
Thanks again, tjboneman for pointing me in the right direction.

Objective-C propreties and private variable

I'm a debutant to objective-c and I don't understand something.
I practiced c++ so some practices are not instinctif ..
1)
#implementation Car {
// Private instance variables
double _odometer;
}
Why a private attribute is in the .m file ?
2)
#interface Car : NSObject {
// Protected instance variables (not recommended)
}
#property (copy) NSString *model;
-(void)drive
a)It seems model is declared like an attribute, but why it's not recommended to do it in the #interface ?
b) why the drive method is not in the interface ?
3)
What if i'm not use function allocation for exemple for a NString and initialise it directly with #"..." ?
4)
I don't understand the difference between + and - before method declaration too..
Thanks in advance
1.>Why a private attribute is in the .m file
Why not? Everything that is declared inside *.m is private because *.m file could not be imported (included) somewhere. In objective C you can declare instance variables in both files - *.h and *.m
2.a Recommended way is to use accessors (#properties in Objective C). For each property setter, getter and instance variable are generated. Default names for
#property (copy)NSString *str;
are:
- (void)setStr:(NSString*)obj; // setter
- (NSString*)str; //getter
NSString *_str; //instance variable
You can modify accessors names
#property (copy, setter = NewSetterName, getter = NewGetterName )NSString *str;
and instance variable name (should be done #implementation section)
#synthesize str = _newStingInstanceVariable;
Ofcource you can rewrite setters and getters that are generated by default.
2.b Interface is everything between #implementation and #end. {} area after #interface - is a place were you can declare instance variables.
3.It is ok for NSStrings
NSString *str = #"some text";
but for most classes it will not work as you expect:
NSMutableString *str = #"some text";
// You will receive warning here! And str will be NSString, not NSMutableString
4."+" - class methods (static in C++), "-" - instance methods.
There's a general principle in programming that you should make the smallest possible amount of information available to the outside. Anything that's in the .h file, anyone in the world can see, access, and mess up. If there's something wrong with an _odometer and it is in the header file, you have to go through all of your source code to find if something is using it wrong. If it's only in the .m file, you know that if there's a problem, it is in the .m file.
The other reason to not make things public is that you are free to change them if nobody knows about them. For some reason, you decide next month that having _odometer wasn't a good idea in the first place. By now three programmers have changed a dozen files to use _odometer because it was there. So removing that _odometer is now a lot, lot of work. If it was never in the .h file, nobody is using it so you only need to change your own .m file.

How Exactly To Use a Global Variable?

I'm a beginner with Objective-C, and am trying to use a global variable. I know that this question has been asked a hundred times, but none of the answers have worked for me. I'm trying to declare a BOOL variable in one class, and check its value in another. This is what I'm working with:
SController.h:
#interface SController : UIViewController {
BOOL leftSide;
BOOL rightSide;
}
SController.m:
- (void)viewDidLoad {
leftSide = YES;
rightSide = YES;
}
Now, for the class I'm trying to access the value of the BOOLs in:
#import "SController.h"
#interface VViewController : UIViewController
{
}
And VViewController's .m:
- (void)viewDidLoad {
// See what the BOOL values from SController are.
}
What I've tried:
Going off of the previous related questions on here, I've tried putting "extern" in front of the BOOLs declaration in SController.h, but that did not work. I tried simply importing the SControllers header file into VViewController, and that did not work either. I'm very new to Objective-C and programming in general, so I'm having a tough time wrapping my head around basic concepts like this. I understand the potential issues surrounding using a global variable, but this program is very small and for personal use. If anyone can show me what to change to make this happen, that would be great.
Like the others said, don't use a global variable for that (and most other) purpose.
You created iVars and in order to access them, you need to expose them to other objects.
You generally do that by defining #properties in your SControllers header file. When doing that, you don't need to create iVars yourself, they are created implicitly. And methods to access the iVars are also automagically created (getters and setters).
Your SControllers header could look something like this:
#interface SController: UIViewController
//no need to declare the iVars here, they are created by the #property definitions
#property (nonatomic, assign) BOOL leftSide;
#property (nonatomic, assign) BOOL rightSide;
#end
In your other viewController you need a reference to the instance of SController you previously created and want to "talk" to (it is important you understand this), then you could access the instance variable through the generated getter/setter methods like so:
//this is "dot notation", the first line would be equivalent
//to writing: [sControllerInstance setLeftSide: YES]
sControllerInstance.leftSide = YES;
BOOL valueRightSide = sControllerInstance.rightSide;
Please read up on: objective-c properties, getters/setters and dot notation.
You will find plenty of information on google and SO
I know this is not the answer you're looking for, but try rethinking your app. Global variables is not the best way to go for Object oriented programming.
Create GlobalVariable.h header class file and defined following externs as follows
extern NSString * googleURL;
And then in your implementation GlobalVariable.m file
#import "GlobalVariable.h"
NSString * googleURL = #"www.google.co.uk";
And then import the class wherever you want to use it across.
By default the variables (as defined in your code) are protected. You can add the #public keyword before the 2 variables to make them public but it's not recommended. Generally you want to expose those as properties using the #property keyword
Example:
#interface SController : UIViewController {
#public
BOOL leftSide;
BOOL rightSide;
#protected
//other protected variables here
}

Where to put iVars in "modern" Objective-C?

The book "iOS6 by Tutorials" by Ray Wenderlich has a very nice chapter about writing more "modern" Objective-C code. In one section the books describes how to move iVars from the header of the class into the implementation file.
Since all iVars should be private this seems to be the right thing to do.
But so far I found 3 ways of doing so. Everyone is doing it differently.
1.) Put iVars under #implementantion inside a block of curly braces (This is how it is done in the book).
2.) Put iVars under #implementantion without block of curly braces
3.) Put iVars inside private Interface above the #implementantion (a class extension)
All these solutions seems to work fine and so far I haven't noticed any difference in the behavior of my application.
I guess there is no "right" way of doing it but I need to write some tutorials and I want to choose only one way for my code.
Which way should I go?
Edit: I am only talking about iVars here. Not properties. Only additional variables the object needs only for itself and that should not be exposed to the outside.
Code Samples
1)
#import "Person.h"
#implementation Person
{
int age;
NSString *name;
}
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = #"Holli";
}
return self;
}
#end
2)
#import "Person.h"
#implementation Person
int age;
NSString *name;
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = #"Holli";
}
return self;
}
#end
3)
#import "Person.h"
#interface Person()
{
int age;
NSString *name;
}
#end
#implementation Person
- (id)init
{
self = [super init];
if (self)
{
age = 40;
name = #"Holli";
}
return self;
}
#end
The ability to put instance variables in the #implementation block, or in a class extension, is a feature of the “modern Objective-C runtime”, which is used by every version of iOS, and by 64-bit Mac OS X programs.
If you want to write 32-bit Mac OS X apps, you must put your instance variables in the #interface declaration. Chances are you don't need to support a 32-bit version of your app, though. OS X has supported 64-bit apps since version 10.5 (Leopard), which was released over five years ago.
So, let's assume you are only writing apps that will use the modern runtime. Where should you put your ivars?
Option 0: In the #interface (Don't Do It)
First, let's go over why we don't want to put instance variables in an #interface declaration.
Putting instance variables in an #interface exposes details of the implementation to users of the class. This may lead those users (even yourself when using your own classes!) to rely on implementation details that they should not. (This is independent of whether we declare the ivars #private.)
Putting instance variables in an #interface makes compiling take longer, because any time we add, change, or remove an ivar declaration, we have to recompile every .m file that imports the interface.
So we don't want to put instance variables in the #interface. Where should we put them?
Option 2: In the #implementation without braces (Don't Do It)
Next, let's discuss your option 2, “Put iVars under #implementantion without block of curly braces”. This does not declare instance variables! You are talking about this:
#implementation Person
int age;
NSString *name;
...
That code defines two global variables. It does not declare any instance variables.
It's fine to define global variables in your .m file, even in your #implementation, if you need global variables - for example, because you want all of your instances to share some state, like a cache. But you can't use this option to declare ivars, because it doesn't declare ivars. (Also, global variables private to your implementation should usually be declared static to avoid polluting the global namespace and risking link-time errors.)
That leaves your options 1 and 3.
Option 1: In the #implementation with braces (Do It)
Usually we want to use option 1: put them in your main #implementation block, in braces, like this:
#implementation Person {
int age;
NSString *name;
}
We put them here because it keeps their existence private, preventing the problems I described earlier, and because there's usually no reason to put them in a class extension.
So when do we want to use your option 3, putting them in a class extension?
Option 3: In a class extension (Do It Only When Necessary)
There's almost never a reason to put them in a class extension in the same file as the class's #implementation. We might as well just put them in the #implementation in that case.
But occasionally we might write a class that's big enough that we want to divide up its source code into multiple files. We can do that using categories. For example, if we were implementing UICollectionView (a rather big class), we might decide that we want to put the code that manages the queues of reusable views (cells and supplementary views) in a separate source file. We could do that by separating out those messages into a category:
// UICollectionView.h
#interface UICollectionView : UIScrollView
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
#property (nonatomic, retain) UICollectionView *collectionViewLayout;
// etc.
#end
#interface UICollectionView (ReusableViews)
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
#end
OK, now we can implement the main UICollectionView methods in UICollectionView.m and we can implement the methods that manage reusable views in UICollectionView+ReusableViews.m, which makes our source code a little more manageable.
But our reusable view management code needs some instance variables. Those variables have to be exposed to the main class #implementation in UICollectionView.m, so the compiler will emit them in the .o file. And we also need to expose those instance variables to the code in UICollectionView+ReusableViews.m, so those methods can use the ivars.
This is where we need a class extension. We can put the reusable-view-management ivars in a class extension in a private header file:
// UICollectionView_ReusableViewsSupport.h
#interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
#end
We won't ship this header file to users of our library. We'll just import it in UICollectionView.m and in UICollectionView+ReusableViews.m, so that everything that needs to see these ivars can see them. We've also thrown in a method that we want the main init method to call to initialize the reusable-view-management code. We'll call that method from -[UICollectionView initWithFrame:collectionViewLayout:] in UICollectionView.m, and we'll implement it in UICollectionView+ReusableViews.m.
Option 2 is flat out wrong. Those are global variables, not instance variables.
Options 1 and 3 are essentially identical. It makes absolutely no difference.
The choice is whether to put instance variables in the header file or the implementation file. The advantage of using the header file is that you have a quick and easy keyboard shortcut (Command + Control + Up in Xcode) to view and edit your instance variables and interface declaration.
The disadvantage is that you expose the private details of your class in a public header. That's not desirable is some cases, particularly if you're writing code for others to use. Another potential problem is that if you're using Objective-C++, it's good to avoid putting any C++ data types in your header file.
Implementation instance variables are great option for certain situations, but for most of my code I still put the instance variables in the header simply because it's more convenient for me as a coder working in Xcode. My advice is to do whatever you feel is more convenient for you.
Largely it has to do with the visibility of the ivar to subclasses. Subclasses will not be able to access instance variables defined in the #implementation block.
For reusable code that I plan to distribute (e.g. library or framework code) where I prefer not expose instance variables for public inspection, then I'm inclined to place the ivars in the implementation block (your option 1).
You should put instance variables in a private interface above the implementation. Option 3.
The documentation to read on this is the Programming in Objective-C guide.
From the documentation:
You Can Define Instance Variables without Properties
It’s best practice to use a property on an object any time you need to keep track of a value or another object.
If you do need to define your own instance variables without declaring a property, you can add them inside braces at the top of the class interface or implementation, like this:
Public ivars should really be declared properties in the #interface (likely what you're thinking of in 1). Private ivars, if you're running the latest Xcode and using the modern runtime (64-bit OS X or iOS), can be declared in the #implementation (2), rather than in a class extension, which is likely what you're thinking of in 3.