Let's say I have a simple app that is loading data into a table view. It then allows you to view details (etc).
My table view controller on first load looks something like this below.
Notice I am not using an "property" declarations for these variables. Is this OK? Are there any disadvantages regarding the way memory is then handled?
#interface TblVC ()
{
MBProgressHUD *hudLoad; // new up loading while I go get data
NSMutableArray *results; // set to results after loading data
CLLocationManager *locManager; // get location in view load
}
#end
#implementation TblVC
{
}
- (void)viewDidLoad
{
// spin up the above variables here which can then be used in other methods inside view controller
}
Just use properties. There is absolutely no reason to use the old-style instance variables anymore.
Apple's documentation on properties goes into detail about the benefits. https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
An instance variable is unique to a class. By default, only the class and subclasses can access it. Therefore, as a fundamental principal of object-oriented programming, instance variables (ivars) are private—they are encapsulated by the class.
By contrast, a property is a public value that may or may not correspond to an instance variable. If you want to make an ivar public, you'd probably make a corresponding property. But at the same time, instance variables that you wish to keep private do not have corresponding properties, and so they cannot be accessed from outside of the class. You can also have a calculated property that does not correspond to an ivar…
Without a property, ivars can be kept hidden. In fact, unless an ivar is declared in a public header it is difficult to even determine that such an ivar exists.
A simple analogy would be a shrink-wrapped book. A property might be the title, author or hardcover vs. softcover. The "ivars" would be the actual contents of the book. You don't have access to the actual text until you own the book; you don't have access to the ivars unless you own the class.
Related
Description + sample + explanation: (You can skip to the question section)
I'd like to make an object instance, which can be implemented by different implementations, depend on a condition (the internet status).
Simple declaration
#interface LoginController : NSObject
/** The currently logged-in User. Nil if not logged-in yet. */
#property (strong, nonatomic) User *currentUser;
// Singleton object
+ (instancetype)shareInstance;
/** Abstract methods, will do nothing if call directly. Use inheritance implements (Online/Offline) instead. */
- (User *)loginByEmail:(NSString *)email password:(NSString *)pwd;
#end
#interface LoginControllerOnline : LoginController
// Login will call request to server.
#end
#interface LoginControllerOffline : LoginController
// Login will check data in coredata.
#end
The LoginController's login method actually do nothing (return nil). Instead, the inherited class (Online/Offline) overwrite the parent login's method, with different implementations (as in comments)
And then, I have a manager to define which class should be in use:
#implement InternetManager
+ (LoginController *)loginController
{
return [self hasInternet] ? [LoginControllerOnline shareInstance] : [LoginControllerOffline shareInstance];
}
+ (BOOL)hasInternet
{
// Check with Reachability.
}
#end
This work. But it's not the mechanism I'd like to achieve.
This mean I have 2 instances of inherited LoginController instead of 1.
When internetStatus change from offline to online, I'd like to re-login online (to get session/oauthToken...). But, I'll have to do many things (copy user, change instance, check retained...) before I can actually call from login online
QUESTION:
Is there a way for me to create only one instance of LoginController, which hold the same properties (User), but can has different (dynamic) implementations (Online/Offline)?
Update question:
Quote from Apple's Dynamic typing:
The isa Pointer:
Every object has an isa instance variable that
identifies the object's class. The runtime uses this pointer to
determine the actual class of the object when it needs to.
So, is there a way for me to change this isa pointer of an object instance?
It sounds like the real problem is that you've given these things direct primary ownership of state that you actually don't want them to own — factor it out. There's no copying, just give each an instance of the thing that marshals sate at -init and allow them to talk to it.
Then just do the normal programming thing when you want to do either one thing or another based on a condition: use an if statement.
So, I don't think use of the dynamic runtime is appropriate. However, academically, supposing an interest:
If you really must, use object_setClass, which "[s]ets the class of an object", answering your actual question. Obviously you need the storage to be compatible, so probably your subclasses shouldn't declare any properties or instance variables.
A commonly-discussed alternative for this general area is not changing the class of an existing instance but changing the methods that are a member of the class. So you'd have two alternative implementations of -loginByEmail:password: and set which was the one that actually responded to that selector dynamically. But there's really no advantage over just using an if if you have access to the source code and a bunch of disadvantages around its generally indirect, opaque nature. The whole thing is usually known as swizzling. class_replaceMethod is the key component but just search for swizzling.
// apply borders to the buttons
-(void)viewDidLoad
{
[super viewDidLoad];
[self.hostGameButton MH_applySnapStyle];
[self.joinGameButton MH_applySnapStyle];
[self.singlePlayerGameButton MH_applySnapStyle];
}
I am confused about this piece of code. If self is just referring to the instance of that object and when we use #property it creates an instance called _hostGameButton ... etc; why can't we just use this code instead?
[_hostGameButton MH_applySnapStyle];
You can, it's just not considered good practice or style. It breaks the encapsulation created by using properties, as well as the (usually desirable) KVO notifications the accessors generate. Modern Objective-C practice is to use properties for everything and only access the synthesized ivar in the init, dealloc (if necessary), and lazy accessor methods, if necessary.
Instances are the heart of the action in an Objective-C program. Most of the methods you’ll define when creating your own classes will be instance methods; most of the messages you’ll send in your code will call instance methods.
So when you create instance of something(like uibutton,uitext field) as property you can access
it anywhere in your.m file by using self. But if you create instance of it in a class you can access it only in that class but no where outside.
As per Apple Documentation, if you’re accessing an object’s properties from within its own implementation, in this case you should use self.
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.
I got a app that uses navigation controller and tableViews and I want to create a class to do some simple storage of information that stays persistent while navigating through the different views without saving to disk.
I can either create an singleton with only class methods, but in this case I´d need to create
the collection class holding the data as an instance variable (as #properties don´t work with class methods). I only ever see objects declared in properties in iOS, so is this frowned upon?
The class would look something like this
header:
+ (BOOL) addObject: (id) object;
+ (BOOL) removeObject: (id) object;
+ (NSInteger) count;
and privately I´ll have an NSArray for storage
NSArray *cache;
But is this a good way of achieving the task? or would it be possible to have a non-singelton class with instance methods and use that same instance of the class in the different table views? if so, how would I do that?
First, ALL readwrite properties auto-synthesize instance variables (unless you implement BOTH setter AND getter).
Second, if that information is global to the entire (or most of the) App, a singleton is just what you need. You don't need to keep it as a property (or an ivar). It's a singleton, it keeps its own pointer.
If you still want to go with a property, you will have to pass it some how to every VC in your App (prepareForSegue:sender: probably if you're using storyboards).
First figure out what global information you need. Then figure out what objects you already have that have a lifetime consistent with that global information, and which are logically associated with the info. Eg, if you need an array of info to "back up" a UITableView, store the pointer to that array in the table view data source instance.
It is rarely necessary to create a "singleton", and having lots of singletons is usually a sign of poor programming.
I used this 'tutorial' to bind my array called 'collection' to a NSTableview on my interface:
http://www.cocoadev.com/index.pl?NSArrayController
The interfacebuilder stuff isn't that hard. It becomes difficult when I try to actually show the data in my array into the view.
in my .h file:
#interface MyDocument : NSDocument
{
NSMutableArray *collection;
//other variables
}
and in my .m file:
#implementation MyDocument
#synthesize collection;
//quite some functions
inside one function (that works):
[collection addObject:fileName];
//some other functions
inside the init function:
collection = [[NSMutableArray alloc] init];
Now I guess the array is bound well to the interface and the tableview inside it, but ofcourse the tableview and its columns need to be filled in a specific way. Right now nothing shows after adding an item. with collection addObject:fileName function
Should I create a sub-Array as one item, filled with fields? And how should I bind these values/fields to the specific columns. (the fields are 'artist', 'title', etc)
I have already bound every column in Interface Builder to Array Controller with Controller key 'arrangedObjects' and Model Key Path 'artist','title',etc.
Please keep the explanation simple since I'm slowly starting to think I will never get this Array Controller thing... Objective-C doesn't seem that hard, but the binding which it needs is what I just don't get. Apple's examples are not sufficient to newbies
Typically to populate your data you'd use a dictionary (the key would be the keyPath, and object the data) for each row, or even better, create a class to represent the data and create a new instance for each row. Bindings can be a little tricky at first (if you're new to Cocoa get used to the data source methods first), but have a look at this tutorial and the examples here. Both contain samples you can download and examine exactly how the bindings is set up in Interface Builder.
Just mutating the array doesn't tell anything that the array has changed. You need to send KVO notifications for the mutation.
The right way to do this is to implement accessor methods for the property, then call your own accessors. In this case, you'll want to implement insertObjectInCollection:atIndex: and pass the length of the array as the index ([self insertObjectIntoCollection:fileName atIndex:[self countOfCollection], after also implementing countOfCollection).
When you implement accessors, then when an object binds to the property, Cocoa will wrap the accessors in KVO magic that will send the appropriate notifications for the mutation.