INSTANCE VARIABLES VS PROPERTIES
An instance variable is a variable that exists and holds its value for the life of the object. Instance variables (or ivars, as they're commonly referred to) are available to the entire class that they're declared in - this is in contrast to local variables, which are available only to the method they are declared in. Generally, it is bad practice to directly set the value of an ivar - instead, we abstract this process by using setter and getter methods. The concept of instance variables (and properties) has a lot to do with data encapsulation, which we'll cover towards the end of this post.
Properties are simply variables that are backed by their ivars - by declaring a property, we create an instance variable and automatically generate setter and getter methods. Additionally, properties come with a list of attributes that we can use to help further shape and define our variables - readonly, readwrite, atomic, nonatomic, strong and copy.
A readonly property is when the backing ivars setter method is never synthesized.
A readwrite property generates both getters and setters.
An atomic property guarantees that a fully initialized object is always returned - the object is locked when being set or get so that it is not being simultaneously accessed from another thread. You will never see a "partial write" for an atomic property. Atomicity doesn't imply thread-safety! Consider an example where you have a first and last name, both atomic, both being accessed and set from two threads (one thread sets the initial first and last names, the other changes them). While you can always guarantee that you will be returned a full string for the first name and a full string for the last name (no partial writes), you cannot guarantee which thread is returning the value. You may get the original first name (from the first thread) and the changed last name (from the second thread), which is not the combination of values you perhaps expected.
A nonatomic property makes no guarantees regarding object initialization.
A strong property creates an owning relationship between the property and the assigned value. This is the default for properties. If a Car object has a @property (strong) Person *driver, and in our implementation file we write Car * Honda = [Car new]; and we have Person *John = [Person new]; and Honda.driver = John, then Honda has strong ownership of John. As long as Honda needs its driver, John will exist.
A weak property creates a non-owning relationship between the property and the assigned value. This can be used to prevent retain cycles. In our previous example, if Person has a property called @property (strong) Car *car, and we have John and say John.car = Honda, we have a problem. Honda has strong ownership of its driver, John, but John also has strong ownership of his car, Honda. These objects, because of the strong relationship, will ALWAYS be owned by another object. If they’re no longer needed, they still can’t be destroyed - this is a retain cycle. A retain cycle is when two objects have a strong relationship to one another and thus can’t be destroyed, and no other objects hold a reference to either of them. The way around this is to use the weak property attribute - if John has weak ownership of his Honda, then we can avoid this retain cycle. If Honda is deallocated, because John has a weak relationship to it, that won’t be a problem - however, John.car will, by default, be set to nil.
Keep in mind that if we want to use Key Value Observing (KVO), we must user properties in our classes. This is a means of notifying a current class when a property value has been accessed in a different class. It provides a means of communicating between different classes. Additionally, properties don’t HAVE to be global - if we declare them as class extensions (declared in our implementation file) as (readwrite), we can use them as we like in the class file. In the interface file, we can declare those same properties as (readonly) - other classes will not be able to write values to that property, essentially encapsulating that data.
Data encapsulation encompasses the idea that data should be hidden and confined to a single area of software as much as possible, unless it is necessary that it be available to other classes. There are many, many ways to go about doing this, and it's up to the developer to decide how they want to go about it. You can hide ivars and properties in a class extension, making them available only to that class. You can create a category, use public variables, and import that category into your class for use.
*As a side note, I almost always use properties instead of explicitly declaring ivars.
If data needs to be publicly available, it is bad practice to make it possible for other classes to change that variables value - in this case, you can declare a readonly property in a class interface file, and redeclare it as readwrite in the implementation file. This makes it so that you can internally change the value of that variable, but no other classes can write new values to it. To make properties "private" (although there is no such thing as a truly private variable in objective-c due to its dynamic runtime) you can simply put them in a class extension - this is my typical encapsulation workflow. dditionally, if you will be using publicly available variables from one class, but you are only using them internally, you can write your import statement for that class ONLY in your implementation file.
To recap -
- Try and declare ivars or properties in categories as much as possible - this limits their exposure to the class they are declared in.
- If you need to expose a variable to other classes, try and make them readonly properties in your interface file and make them readwrite in your implementation file.
- All property variable are backed by ivars - they simply generate setters and getters automatically and come with several attributes you can utilize that deal with atomicity, encapsulation (readonly) and memory management.
- NOTE - If you don't want to put your variables in an extension, you can just put them in curly braces right after your @implementation "class name" in your .m file. This also encapsulates your data - I prefer using extensions because I think it makes my code more readable.
Last but not least, if you want other classes to be able to change the values of a variable you have, you can keep the variable private and still accomplish this - create a public method, declared in your interface file, that internally changes the value of the private variable (see below). This is often used for further encapsulation, as it is bad practice in a large code base to allow other classes to directly change the value of an object. Once again, the way you choose to encapsulate your data depends entirely on your preferences and the data you have at hand - different pieces of software will always have different encapsulation requirements.