Objective-C Concept Notes
In Objective-C, a class is itself an object with an opaque type called Class. Classes can’t have properties defined using the declaration syntax shown earlier for instances, but they can receive messages.
The typical use for a class method is as a factory method, which is an alternative to the object allocation and initialization procedure described in Objects Are Created Dynamically.
Working with Objects
A local variable is allocated on the stack, while objects are allocated on the heap. Memory is allocated dynamically for an Objective-C object. The first step in creating an object is to make sure enough memory is allocated not only for the properties defined by an object’s class, but also the properties defined on each of the superclasses in its inheritance chain.
Because of Objective-C’s dynamic nature, it doesn’t matter what specific class type you use for that pointer — the correct method will always be called on the relevant object when you send it a message.
Because the class of an object is determined at runtime, it makes no difference what type you assign a variable when creating or working with an instance. To use the XYZPerson and XYZShoutingPerson classes described earlier in this chapter, you might use the following code:
Although both firstPerson and secondPerson are statically typed as XYZPerson objects, secondPerson will point, at runtime, to an XYZShoutingPerson object. When the sayHello method is called on each object, the correct implementations will be used; for secondPerson, this means the XYZShoutingPerson version.
When dealing with objects, the == operator is used to test whether two separate pointers are pointing to the same object. If you need to test whether two objects represent the same data, you need to call a method like isEqual.
If you need to compare whether one object represents a greater or lesser value than another object, you can’t use the standard C comparison operators > and <. Instead, the basic Foundation types, like NSNumber, NSString and NSDate, provide a compare: method:
Working with Nil
Compiler will automatically set the variable to nil if you don’t specify any other initial value. A nil value is the safest way to initialize an object pointer if you don’t have another value to use, because it’s perfectly acceptable in Objective-C to send a message to nil. If you do send a message to nil, obviously nothing happens. If you expect a return value from a message sent to nil, the return value will be nil for object return types, 0 for numeric types, and NO for BOOL types. Returned structures have all members initialized to zero.
Values and Collections
Objective-C define additional prime types, like NSInteger and NSUInteger. They are defined differently depending one the target architecture. When building for a 32-bit environment, they are 32-bit.
It’s best practice to use these platform-specific types if you might be passing values across API boundaries (both internal and exported APIs), such as arguments or return values in method or function calls between your application code and a framework. For local variables, such as a counter in a loop, it’s fine to use the basic C types if you know that he value is within the standard limits.
Rather than somehow maintaining a separate copy of each collected object, the collection classes use strong references to keep track of their contents.
It’s possible to truncate the list of items unintentionally if one of the provided values is
nil, like this:
In this case,
someArray will contain only
firstObject, because the
secondObject would be interpreted as the end of the list of items.
NSSet is similar to an array, but maintains an unordered group of distinct objects. Because sets don’t maintain order, they offer a performance improvement over arrays when it comes to testing for membership.
You cannot mutate a collection during fast enumeration, even if the collection is mutable. If you attempt to add or remove a collected object from within the loop, you’ll generate a runtime exception.
The Foundation Collection Classes
Immutable collections are fully thread safe and can be iterated from multiple threads at the same time, without any risk of mutation exceptions. Your API should never expose mutable collections.
Of course there’s a cost when going from immutable and mutable and back — the object has to be copied twice, and all objects within will be retained/released. Sometimes it’s more efficient to hold an internal mutable collection and return a copied, immutable object on access.
Working with Blocks
Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. They also have the ability to capture values from the enclosing scope
Blocks Can Capture Values from the Enclosing Scope. In this example, anInteger is declared outside of the block, but the value is captured when the block is defined. Only the value is captured, unless you specify otherwise. This means that if you change the external value of the variable between the time you define the block and the time it’s invoked, the value captured by the block is unaffected.
Blocks are often used for callbacks, defining the code to be executed when a task complete.
In terms of readability, the block makes it easy to see in one place exactly what will happen before and after the task completes.
Properties Encapsulate an Object’s Values
Objective-C properties offer a way to define the information that a class is intended to encapsulate. object should hide its internal workings behind its public interface, it’s important to access an object’s properties using behavior exposed by the object rather than trying to gain access to the internal values directly.
By default, these accessor methods are synthesized automatically for you by the compiler, so you don’t need to do anything other than declare the property using @property in the class interface.
By default, a readwrite property will be backed by an instance variable, which will again be synthesized automatically by the compiler. Unless you specify otherwise, the synthesized instance variable has the same name as the property, but with an underscore prefix. For a property called firstName, for example, the synthesized instance variable will be called _firstName.
An instance variable is a variable that exists and holds its value for the life of the object. The memory used for instance variables is allocated when the object is first created (through alloc), and freed when the object is deallocated.
If you wish to use a different name for the instance variable, you need to direct the compiler to synthesize the variable using the following syntax in your implementation:
Access Instance Variables Directly from Initializer Methods
You should always access the instance variables directly from within an initialization method because at the time a property is set, the rest of the object may not yet be completely initialized. Even if you don’t provide custom accessor methods or know of any side effects from within your own class, a future subclass may very well override the behavior.
An init method should assign self to the result of calling the superclass’s initialization method before doing its own initialization. A superclass may fail to initialize the object correctly and return nil so you should always check to make sure self is not nil before performing your own initialization.
The Designated Initializer is the Primary Initialization Method
If an object declares one or more initialization methods, you should decide which method is the designated initializer. This is often the method that offers the most options for initialization (such as the method with the most arguments), and is called by other methods you write for convenience. You should also typically override init to call your designated initializer with suitable default values.
Properties Are Atomic by Default
This means that the synthesized accessors ensure that a value is always fully retrieved by the getter method or fully set via the setter method, even if the accessors are called simultaneously from different threads.
Note: Property atomicity is not synonymous with an object’s thread safety.
Manage the Object Graph through Ownership and Responsibility
When one object relies on other objects in this way, effectively taking ownership of those other objects, the first object is said to have strong references to the other objects. In Objective-C, an object is kept alive as long as it has at least one strong reference to it from another object.
By default, both Objective-C properties and variables maintain strong references to their objects. Local variables (and non-property instance variables) also maintain strong references to objects by default.
Use Unsafe Unretained References for Some Classes
There are a few classes in Cocoa and Cocoa Touch that don’t yet support weak references, which means you can’t declare a weak property or weak local variable to keep track of them. These classes include NSTextView, NSFont and NSColorSpace; for the full list, see Transitioning to ARC Release Notes.
An unsafe reference is similar to a weak reference in that it doesn’t keep its related object alive, but it won’t be set to nil if the destination object is deallocated. This means that you’ll be left with a dangling pointer to the memory originally occupied by the now deallocated object, hence the term “unsafe.” Sending a message to a dangling pointer will result in a crash.
Using Strong and Weak Declaration to manage Ownership
To avoid a dangerous dangling pointer to the memory originally occupied by the now deallocated object, a weak reference is automatically set to nil when its object is deallocated.
It’s particularly important to keep this in mind if you need to make sure a weak property is not nil before using it. Because in a multi-threaded application, the property may be deallocated between the test and the method call, rendering the test useless. Instead, you need to declare a strong local variable to cache the value, like this:
Copy Properties Maintain Their Own Copies
The copy attribute means that the property will use a strong reference, because it must hold on to the new object it creates.
If you need to set a copy property’s instance variable directly, for example in an initializer method, don’t forget to set a copy of the original object:
Customizing Existing Classes
If you need to add a method to an existing class, perhaps to add functionality to make it easier to do something in your own application, the easiest way is to use a category.
@interface ClassName (CategoryName)
A category can be declared for any class, even if you don’t have the original implementation source code (such as for standard Cocoa or Cocoa Touch classes). Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.
As well as just adding methods to existing classes, you can also use categories to split the implementation of a complex class across multiple source code files.
Categories can be used to declare either instance methods or class methods but are not usually suitable for declaring additional properties. It’s valid syntax to include a property declaration in a category interface, but it’s not possible to declare an additional instance variable in a category. This means the compiler won’t synthesize any instance variable, nor will it synthesize any property accessor methods. You can write your own accessor methods in the category implementation, but you won’t be able to keep track of a value for that property unless it’s already stored by the original class.
Class Extensions Extend the Internal Implementation
A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the @implementation block for the original class so you can’t, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString.
Class extensions are often used to extend the public interface with additional private methods or properties for use within the implementation of the class itself. It’s common, for example, to define a property as readonly in the interface, but as readwrite in a class extension declared above the implementation, in order that the internal methods of the class can change the property value directly.
Protocols Define Messaging Contracts
Protocols can include declarations for both instance methods and class methods, as well as properties. If a method in a protocol is marked as optional, you must check whether an object implements that method before attempting to call it.