iOS Instance Variables - ios

Simple question, I would just like to hear what other people do, and what best practice might be. To clear some things up.
When declaring an instance variable in one of my view controllers. Where is the proper place to declare it? I want the instance variable to available to all methods/function in the view controller
I've seen it done this way, in the .h file in curly braces after the #interface:
#interface ViewController : UIViewController {
NSString *test3;
}
I've seen it done this way, in the .m file in curly braces after the #interface:
#interface ViewController () {
NSString *test1;
}
And I've seen it done this way, in the .m file in curly braces after the #implementation:
#implementation ViewController {
NSString *test2;
}
What is the best practice? Is there any difference? If so what may they be, and what makes one way better than another?
All help will be appreciated,
Thanks.

Any of those will work, but current best practice is to use a property. If you want it to be accessible only inside your class:
#interface ViewController ()
#property (copy, nonatomic) NSString *test1;
#end
An access it like this:
self.test1 = #"Hello, world";
But, if you want it to be accessible to other classes as well, put it in the public interface:
#interface ViewController : UIViewController
#property (copy, nonatomic) NSString *test1;
#end
And then access it like this:
someInstanceOfViewController.test1 = #"Hello, world";
If you need to access the instance variable directly (and this applies only inside the class in most cases), for example if you are making a custom setter, the compiler will auto-synthesize an ivar that is the name of your property prefixed with an underscore:
- (void)setTest1:(NSString *)test1
{
_test1 = [test1 copy];
}
Note: the copy is because you might set test1 to an instance of NSMutableString, and you probably don’t want its contents getting mutated out from under you. When you override the setter, as above, you have to enforce this copy semantic yourself.

Related

Exposing for unit testing in objective c

Let's say I have a class:
#interface MyClass
#end
#implementation MyClass {
myType *_myIvar;
}
#end
And I'd like to expose it for testing. I see two ways to do this:
expose the ivar as a property:
#interface MyClass
// Pragma mark - Expose for testing
#property myIvar;
#end
#implementation MyClass
#end
Use key value coding:
-(void)myTest {
myType *myIvar = [myClass valueForKey:#"_myIvar"];
}
Which method is preferred?
First - you don't test private methods or state of a class for unit-testing, as per TDD best practices.
Having that said, however, sometimes it's the only way to observer possible side-effects. I personally always wrap any ivar of a class with a property. For data which is not supposed to appear in the public interface I put it in the extension inside of the implementation file:
// Implementation file
#interface TDWClass ()
#property (strong, nullable) NSString *tdw_p_message;
#end
P.S. This also helps to maintain certain semantic for the given property (you immediately can see not just storage modifier, but other attributes the property is supposed to follow: like, being read-only, nullability, etc..)
When it comes to testing such a property, this approach helps to conveniently read any "private" property (or accessing a private method) by re-declaring it in a category:
// XCTest file
#interface TDWClass (XCTest)
#property (strong, nullable) NSString *tdw_p_message;
#end

how to pass the variable in segue

i have defined variable in GroupView.h
#interface GroupView()
{
NSMutableArray *chatrooms;
}
#end
#implementation GroupView
Now i want to pass this variable in segue
#interface FriendsViewController ()
#end
#implementation FriendsViewController
else if ([segue.identifier isEqualToString:#"showGroupView"]) {
GroupView *groupView = (GroupView *)segue.destinationViewController;
groupView.chatrooms = [NSMutableArray arrayWithArray:chatrooms];
}
i know that chatrooms has to be property in header file to code this way but it is not
So is there any way to use this variable in segue.
Thanks for help.
chatrooms defined as an ivar like you have done is accessed using -> notation:
groupView->chatrooms = [NSMutableArray arrayWithArray:chatrooms]
This is generally discouraged, though. You should use a property instead:
#interface GroupView
#property (strong) NSMutableArray *chatrooms;
#end
Incidentally, if you're using an NSMutableArray, that indicates that you want to modify the element list of the array directly and not just replace the array wholesale. If you only ever want to replace the array with a whole new array every time, I suggest using NSArray instead.
Another point to make here is that you're attempting to cast the object held at segue.destinationViewController as a GroupView. You have either named a UIViewController subclass in a very misleading way, or you are not accessing the GroupView as a correct member of the UIViewController that is returned to you.
Normally, if you are not building the SDK or something. You don't really have a better reason not to expose it in the header file. However, you can expose the property in the extension and declare a private property in the host class(It's really not able to pass if you just declare a local variable). For example, You have a extension called GroupView+Helper. So, you can pass it into the property exposed in the extension. And then internally translate into the GroupView.
In GroupView.m:
#interface GroupView
#property (strong, nonatomic) NSMutableArray *chatrooms;
#end
In GroupView+Helper.h
#property (strong, nonatomic) NSMutableArray *internalChatrooms;
Also, you need to import the GroupView+Helper in the GroupView.
It will make your chatrooms private and internalChatrooms protected.

Access to a variable from another view controller

I'm working on a project in objective C where i have to modificate some variables which are in a view controller from uiviews.
So i've tried somethings like this :
ViewController.h :
#property (nonatomic) bool Contact;
One of the UIViews :
ViewController * View;
View.Contact = YES;
I've also tried to make a setter method like this in the ViewController :
-(void) SetterContact:(bool)boolean;
And so to modificate from a UIView like this :
[View SetterContact:YES];
But it's looking working.
I've read that i have to init the object in which is containt the variable, but in memory management it's not really good to make some initializations from object who are already actives no ?
So if View is already init, i'm not going to call the init method from another UIView no ?
Thanks for your help !
If you want bool variable to be accessible from other viewController.Then simply wirte it as :-
#property BOOL Contact;
and make an object of ViewController in which you have declared contact variable as BOOL and access this variable using like this:-
OtherViewController *otherViewController=[[OtherViewController alloc] init];
otherViewController.Contact=YES;
As it is a instance variable it has to be accessed using class object.
use #property (nonatomic, assign, getter = isContact) BOOL contact; in your .h file.
Respect naming conventions
#property (nonatomic,retain) UIViewController *myController;
don't forget to synthesize
#synthesize myController = _myController;
If you want to implement your own setter do this: respect the naming convention
-(void)setMyController:(UIViewController*)controller;
or if by any bizarre reason you can't respect naming convention you can point the property to the method you want
#property (nonatomic,retain,setter=myBizarreSetterMethod:) UIViewController *myController;
this can help you out as well question in stackoverflow

Proper way to use instance variables/property/synthetize with ARC [duplicate]

This question already has answers here:
Declaration/definition of variables locations in ObjectiveC?
(4 answers)
Closed 9 years ago.
What is the proper way to work with instance variables (declared on interface), their #property and #synthesize, when working in ARC project? What I now do is following:
SomeClass.h:
#interface SomeClass : NSObject {
NSString *someString;
}
#property(nonatomic, copy) NSString* someString;
and SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
self.someString = #"Foobar";
}
The thing is that there are other approaches that works, like using just the #property:
SomeClass.h:
#interface SomeClass : NSObject
#property(nonatomic, copy) NSString* someString;
Accessing the someString without self:
SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
someString = #"Foobar";
}
etc. I'm new to Objective-c, I'm used to Java. What is the proper way to work with attributes then? I understand that special cases will have special behavior, but what is the best approach in general? (by general I mean I want to access the variable from the class itself and from "outside" and I want ARC to still work correctly, eg. I don't have to worry about memory leaks)
For simple properties, you don't need the instance variable declaration or the #synthesize. The clang compiler will generate both for you by default. So you could write this in the header:
#interface SomeClass : NSObject
#property (nonatomic, copy) NSString *someString;
#end
And the implementation:
#implementation SomeClass
- (void)someMethod {
self.someString = #"Foobar";
}
#end
Avoid direct instance variable access unless you are in the -init method or overriding the setter. Everywhere else you should use the dot syntax (self.someString). If you do need access to the instance variable, the default synthesize will create an underscore-prefixed ivar, e.g. _someString.
Note that for classes with mutable versions like NSString/NSMutableString and NSArray/NSMutableArray the standard practice is to use a copy property. If you use strong on a string or array, the caller might pass in a mutable version and then mutate it from under you, causing hard-to-find bugs.
Check out this SO post for information about ARC.
(Edited) The "strong" attribute tells ARC to keep an object around until the object with the property is deallocated. You do need the "copy" attribute because an NSString property could have been passed in as an NSMutableString. The "copy" guarantees that the original object will be kept around. Again, I apologize for the incorrect/misleading information I originally had here.
The reason you can access the instance variable someString as well as the property self.someString is that the #synthesize someString line creates an instance variable for the property and creates methods for getting and setting the value of it. However, it is recommended that you use the property instead of directly using the instance variable because by using the instance variable, you cannot let the parent object know that you've changed one of its properties.

Objective C - Call method from another class

I have 2 classes geoViewController and geoMainViewController
I have a method in the geoMainViewController called getFoo
It looks like this:
- (NSString *)getFoo
{
NSString* foo = #"This is foo";
return foo;
}
I am trying to call getFoo from the geoViewController class.
I have #import "geoMainViewController.h" in my geoViewController m file.
I am trying instantiate the geoMainViewController class and call the getFoo method from the viewDidLoad in my geoViewController class like this:
- (void)viewDidLoad
{
[super viewDidLoad];
geoMainViewController* mainVC = [[geoMainViewController alloc] init];
NSString* myFoo = [mainVC getFoo];
}
It seems to be instantiating the geoMainViewController class fine but I am getting an error on NSString* myFoo = [mainVC getFoo];
The error is - no visible #interface for 'geoMainViewController' declares the selector 'getFoo'
I am sure I am missing a step because I am very new to Objective C. I am just not sure what I am doing wrong.
Any help on this would be great.
Thanks!
In your geoMainViewController.h you should declare the selector to be visible:
-(NSString *)getFoo;
Did you put - (NSString *)getFoo in your geoMainViewController.h ?
You have to make those methods visible to the outside of your object through the .h file, so other objects know which selectors they respond to. Did the autoComplete fill in the message per chance?
#import <Foundation/Foundation.h>
#interface
{
}
#property (nonatomic,strong) ;
#property (nonatomic,strong) ;
#property (nonatomic, strong) ;
- (NSString *)getFoo
#end
EDIT: (You could also just make Foo a property by the way)
Did you declare it in your header file?
Header file contains all the function declarations in the .h file and you only include the .h file in your class. So it depends on .h file. .h file will have all the functions as the .m file.
Hope it helps you.
You are misunderstanding how to use a view controller. While you can technically create an instance of a view controller in order to call one of its methods, you shouldn't do so. The normal approach is that the view controller is part of the view hierarchy and you can call methods on it when you have access to that instance. You are missing something fundamental here.
Your actual error is a missinh method declaration, I would suspect, but you have bigger problems to solve first.

Resources