Documente Academic
Documente Profesional
Documente Cultură
Cheat Sheet
This is not meant to be a beginner's guide or a detailed discussion about Objective-C; it is meant to be a quick reference to
common, high level topics.
Read my Swift (https://github.com/iwasrobbed/Swift-CheatSheet) cheatsheet as well (Swift will replace Objective-C for iOS apps).
Support this project via Gratipay tips $0/week (https://gratipay.com/iwasrobbed/)
To download a PDF version of this, use GitPrint.com (https://gitprint.com/iwasrobbed/Objective-C-CheatSheet/blob/master/README.md?
download)
Note: I wrote this over a stretch of 3 days, so it could probably use some editing and clarifications where necessary. Please feel
free to edit this document to update or improve upon it, making sure to keep with the general formatting of the document. The
list of contributors can be found here (https://github.com/iwasrobbed/Objective-C-CheatSheet/graphs/contributors) .
For advanced Objective-C topics, these two sites are updated weekly:
http://www.objc.io (http://www.objc.io)
http://nshipster.com (http://nshipster.com)
If something isn't mentioned here, it's probably covered in detail in one of these:
Swift, Apple's new language to replace Objective-C
(https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language)
NSHipster's "Fake" book aka concise usage examples of common Objective-C tasks (https://gumroad.com/l/the-nshipster-fakebook)
Table of Contents
Commenting
Data Types
Constants
Operators
Declaring Classes
Preprocessor Directives
Compiler Directives
Literals
Methods
Properties and Variables
Naming Conventions
Blocks
Control Statements
Enumeration
Extending Classes
Error Handling
Passing Information
User Defaults
Common Patterns
1 of 30
Commenting
Comments should be used to organize code and to provide extra information for future refactoring or for other developers who
might be reading your code. Comments are ignored by the compiler so they do not increase the compiled program size.
Two ways of commenting:
// This is an inline comment
Data Types
Size
Permissible sizes of data types are determined by how many bytes of memory are allocated for that specific type and whether it's
a 32-bit or 64-bit environment. In a 32-bit environment, long is given 4 bytes, which equates to a total range of 2^(4*8) (with 8
bits in a byte) or 4294967295. In a 64-bit environment, long is given 8 bytes, which equates to 2^(8*8) or 1.84467440737096e19.
For a complete guide to 64-bit changes, please see the transition document
(https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/64bitPorting/transition/transition.html#//apple_ref/doc/uid/TP40001064-CH207TPXREF101) .
C Primitives
Note: Objective-C inherits all of the C language primitive types and then adds a few extras.
Void
void is C's empty data type. It is most commonly used to specify the return type for functions that don't return anything.
Integers
Integers can be signed or unsigned. When signed, they can be either positive or negative and when unsigned, they can only be
positive. Example: When declaring an unsigned int, the range of allowable integer values for a 32-bit compiler will shift from 2147483648 to +2147483647 to instead be 0 to +4294967295.
Integer types with their accompanying byte sizes:
// Char (1 byte for both 32-bit and 64-bit)
u n s i g n e d c h a r anUnsignedChar = 255;
NSLog(@"char size: %zu", s i z e o f (c h a r ));
u n s i g n e d s h o r t anUnsignedShort = 65535;
NSLog(@"short size: %zu", s i z e o f (s h o r t ));
Objective-C Primitives
id : Known as the anonymous or dynamic object type, it can store a reference to any type of object with no need to specify a
pointer symbol.
i d delegate = self.delegate;
3 of 30
Class : Used to denote an object's class and can be used for introspection of objects.
C l a s s aClass = [UIView class];
Method : Used to denote a method and can be used for swizzling methods.
Method aMethod = class_getInstanceMethod(aClass, aSelector);
SEL : Used to specify a selector which is compiler-assigned code that identifies a method name.
S E L someSelector = @ s e l e c t o r (someMethodName);
IMP : Used to point to the memory address of the start of a method. You will probably never need to use this.
I M P theImplementation = [self methodForSelector: someSelector];
BOOL : Used to specify a boolean type where 0 is considered NO (false) and any non-zero value is considered YES (true). Any nil
object is also considered to be NO so there is no need to perform an equality check with nil (e.g. just write if (someObject) not if
(someObject != nil)).
// Boolean
B O O L isBool = YES; // Or NO
nil : Used to specify a null object pointer. When classes are first initialized, all properties of the class are set to 0 which means
they point to nil.
Objective-C also has a number of other types such as NSInteger, NSUInteger, CGRect, CGFloat, CGSize, CGPoint, etc.
4 of 30
// Toggle bit
status ^ = RPOptionTop;
// Set single bit to zero
status & = ! RPOptionBottom;
// Check if it matches a certain bit
i f (status & RPOptionTop) {
[self doSometing];
}
Constants
Using const is usually the better approach since const points to the same memory address for any references to that item in code.
#define defines a macro which replaces all references with the actual constant value contents before compilation starts, instead
of being a memory pointer to that constant value.
Constants can be defined as:
// Format: type const constantName = value;
NSString * c o n s t kRPShortDateFormat = @"MM/dd/yyyy";
Operators
5 of 30
Arithmetic Operators
Operator
Purpose
Addition
Subtraction
Multiplication
Division
Modulo
Purpose
==
Equal to
!=
Not equal to
>
Greater than
<
Less than
>=
<=
Logical Operators
Operator
Purpose
NOT
&&
Logical AND
||
Logical OR
Purpose
+=
Addition
-=
Subtraction
*=
Multiplication
/=
Division
%=
Modulo
&=
Bitwise AND
|=
Bitwise Inclusive OR
^=
Exclusive OR
<<=
Shift Left
>>=
Shift Right
6 of 30
>>=
Shift Right
Operator
Purpose
Purpose
++
Addition
--
Subtraction
*=
Multiplication
/=
Division
%=
Modulo
&=
Bitwise AND
|=
Bitwise Inclusive OR
^=
Exclusive OR
<<=
Shift Left
>>=
Shift Right
Bitwise Operators
Operator
Purpose
&
Bitwise AND
Bitwise Inclusive OR
Exclusive OR
<<
Shift Left
>>
Shift Right
Other operators
Operator
Purpose
()
Cast
?:
Conditional
&
Memory Address
Pointer
Back to top
Declaring Classes
Classes are declared using two files: a header (.h) file and an implementation (.m) file.
The header file should contain (in this order):
Any needed #import statements or forward @class declarations
Any protocol declarations
An @interface declaration specifying which class you're inheriting from
7 of 30
// Used instead of #import to forward declare a class in property return types, etc.
@ c l a s s S o m e O t h e r C l a s s ;
// Place all global constants at the top
e x t e r n NSString * c o n s t kRPErrorDomain;
// Format: YourClassName : ClassThatYouAreInheritingFrom
@ i n t e r f a c e M y C l a s s : S o m e C l a s s
// Public properties first
@ p r o p e r t y (readonly, nonatomic, strong) SomeClass * someProperty;
// Then class methods
+ (i d )s o m e C l a s s M e t h o d ;
// Then instance methods
- (SomeOtherClass * )d o W o r k ;
@end
MyClass.m
#import "MyClass.h"
#import "SomeOtherClass.h"
8 of 30
Preprocessor Directives
This section needs a lot of love, so please feel free to improve upon it!
Directive
#define
Purpose
Used to define constants or macros that are replaced by the compiler at runtime
#elif
#else
#endif
#error
#if
#ifdef
An if conditional statement
An if defined conditional statement
#ifndef
#import
Imports a header file. This directive is identical to #include, except that it won't include the same file more than
once
#include
9 of 30
#pragma
Directive
#undef
#warning
Special operator
The special operator defined is used in #if and #elif expressions to test whether a certain name is defined as a macro.
defined is useful when you wish to test more than one macro for existence at once. For example, #if defined(__IPHONE_8_0) ||
defined(__MAC_10_9) would succeed if either of the names __IPHONE_8_0 or __MAC_10_9 is defined as a macro.
Back to top
Compiler Directives
Also see the literals section.
Classes and Protocols
Directive
Purpose
@class
@interface
@implementation
@protocol
@required
@optional
Declares the methods following the directive as optional. Classes implementing this protocol can decide
whether to implement an optional method or not and should first check if the method is implemented
before sending it a message.
@end
Properties
Directive
@property
@synthesize
@dynamic
Purpose
Declares a property with a backing instance variable
Synthesizes a property and allows the compiler to generate setters/getters for the backing instance variable
Tells the compiler that the setter/getter methods are not implemented by the class itself but somewhere else,
like the superclass
Errors
Directive
@throw
@try
Purpose
Throws an exception
Specifies a block of code to attempt
@catch
@finally
10 of 30
Directive
Directive
@private
@protected
@public
@package
Purpose
Purpose
Limits the scope of instance variables specified below it to the class that declares it
Limits the scope of instance variables specified below it to declaring and inheriting classes
Removes restrictions on the scope of instance variables specified below it
Declares the instance variables following the directive as public inside the framework that defined the class, but
private outside the framework (64-bit only)
Purpose
Returns the compiled selector that identifies a method
Returns the given protocol (an instance of the Protocol class)
Encapsulates code in a mutex lock to ensure that the block of code and the locked object can only be
accessed by one thread at a time
Replaces (and is 6 times faster than) the NSAutoreleasePool class
Yields a character string that encodes the type structure of spec
@compatibility_alias
@defs(classname)
@import
Imports a module and autolinks its framework (currently for Apple frameworks only)
Back to top
Literals
Literals are compiler directives which provide a shorthand notation for creating common objects.
Syntax
@"string"
@28, @3.14, @YES
What it does
Returns an NSString object
Returns an NSNumber object initialized with an appropriate class constructor, depending on the type
@[]
@{}
@()
Dynamically evaluates the boxed expression and returns the appropriate object literal based on its value
11 of 30
Caveats
Similar to NSString literals, collection objects made via literal arrays and dictionaries are immutable. Instead, you will have to
make a mutable copy after making the immutable dictionary or array. Additionally, you cannot have static initializers like you can
with NSString.
Back to top
Methods
Declaration Syntax
For methods without a return type, use void:
// Does not return anything or take any arguments
- (v o i d )s o m e M e t h o d ;
+ precedes declarations of class methods:
// Call on a class (e.g. [MyClass someClassMethod]);
+ (v o i d )s o m e C l a s s M e t h o d ;
- precedes declarations of class instance methods:
// Called on an instance of a class (e.g. [[NSString alloc] init]);
- (v o i d )s o m e C l a s s I n s t a n c e M e t h o d ;
Method arguments are declared after colons : and the method signature should describe the argument type:
// Does something with an NSObject argument
- (v o i d )d o W o r k W i t h O b j e c t : (NSObject * )object;
Argument and return types are declared using type casting syntax:
// Returns an NSString object for the given NSObject arguments
- (NSString * )s t r i n g F r o m O b j e c t : (NSObject * )object a n d S o m e O t h e r O b j e c t : (NSObject * )otherObject;
Calling Methods
Methods are called using bracket syntax: [self someMethod]; or [self sometMethodWithObject:object];
self is a reference to the method's containing class. The self variable is present in all Objective-C methods and it is one of two
hidden arguments passed to code that implements a method, the other being _cmd, which identifies the received message.
At times, it is necessary to call a method in the superclass using [super someMethod];.
Under the hood, methods are implemented via message sending and they are turned into a variation of one of these two C
functions:
i d o b j c _ m s g S e n d (i d self, S E L op, ...);
i d o b j c _ m s g S e n d S u p e r (s t r u c t objc_super * super, S E L op, ...);
Testing Selectors
If you'd like to test if a class responds to a certain selector before you send it (and possibly crash), you can do so with:
i f ([someClassOrInstance respondsToSelector: @ s e l e c t o r (someMethodName)])
{
// Call the selector or do something here
}
This pattern is common when you have a delegate implemented and need to test for methods declared as @optional before calling
them on the delegate object.
Back to top
12 of 30
13 of 30
// Or
@ p r o p e r t y (xxx) SomeClass * someProperty;
where xxx can be a combination of:
Type
What it does
copy
Creates an immutable copy of the object upon assignment and is typically used for creating an
immutable version of a mutable object. Use this if you need the value of the object as it is at this
moment, and you don't want that value to reflect any future changes made by other owners of the
object.
assign
Generates a setter which assigns the value directly to the instance variable, rather than copying or
retaining it. This is typically used for creating properties for primitive types (float, int, BOOL, etc).
weak
Variables that are weak can still point to objects but they do not become owners (or increase the retain
count by 1). If the object's retain count drops to 0, the object will be deallocated from memory and the
weak pointer will be set to nil. It's best practice to create all delegates and IBOutlet's as weak references
since you do not own them.
unsafe_unretained
An unsafe reference is similar to a weak reference in that it doesn't keep its related object alive, but it
wont be set to nil if the object is deallocated. This can lead to crashes due to accessing that deallocated
object and therefore you should use weak unless the OS or class does not support it.
strong
This is the default and is required when the attribute is a pointer to an object. The automatically
generated setter will retain (i.e. increment the retain count of) the object and keep the object alive until
released.
readonly
This only generates a getter method so it won't allow the property to be changed via the setter method.
readwrite
This is the default and generates both a setter and a getter for the property. Often times, a readonly
property will be publicly defined and then a readwrite for the same property name will be privately
redefined to allow mutation of the property value within that class only.
atomic
nonatomic
This is the default and means that any access operation is guaranteed to be uninterrupted by another
thread and is typically slower in performance to use.
This is used to provide quicker (but thus interruptable) access operations.
getter=method
Used to specify a different name for the property's getter method. This is typically done for boolean
properties (e.g. getter=isFinished)
setter=method
Used to specify a different name for the property's setter method. (e.g. setter=setProjectAsFinished)
Accessing Properties
Properties can be accessed using either bracket syntax or dot notation, with dot notation being cleaner to read.
[self myProperty];
// Or
self.myProperty
Local Variables
Local variables exist only within the scope of a method.
- (v o i d )d o W o r k
{
NSString * localStringVariable = @"Some local string variable.";
[self doSomethingWithString: localStringVariable];
}
Back to top
14 of 30
Naming Conventions
The general rule of thumb: Clarity and brevity are both important, but clarity should never be sacrificed for brevity.
Methods and Properties
These both use camelCase where the first letter of the first word is lowercase and the first letter of each additional word is
capitalized.
Class names and Protocols
These both use CapitalCase where the first letter of every word is capitalized.
Methods
These should use verbs if they perform some action (e.g. performInBackground). You should be able to infer what is happening,
what arguments a method takes, or what is being returned just by reading a method signature.
Example:
// Correct
- (UITableViewCell * )t a b l e V i e w : (UITableView * )tableView c e l l F o r R o w A t I n d e x P a t h : (NSIndexPath * )indexPath
{
// Code
}
Blocks
Blocks are essentially anonymous functions that are used to pass arbitrary code between methods or to execute code as a callback
within a method. Since blocks are implemented as closures, the surrounding state is also captured (which can sometimes lead to
retain cycles).
Syntax
// As a local variable
returnType (^ blockName)(parameterTypes) = ^ returnType(parameters) {
// Block code here
};
// As a property
@ p r o p e r t y (nonatomic, copy) returnType (^ blockName)(parameterTypes);
// As a method parameter
15 of 30
- (v o i d )s o m e M e t h o d T h a t T a k e s A B l o c k : (returnType (^ )(parameterTypes))blockName {
// Block code here
};
Control Statements
Objective-C uses all of the same control statements that other languages have:
If-Else If-Else
i f (someTestCondition) {
// Code to execute if the condition is true
} e l s e i f (someOtherTestCondition) {
// Code to execute if the other test condition is true
16 of 30
} e l s e {
// Code to execute if the prior conditions are false
}
Ternary Operators
The shorthand notation for an if-else statement is a ternary operator of the form: someTestCondition ? doIfTrue : doIfFalse;
Example:
- (NSString * )s t r i n g F o r T r u e O r F a l s e : (B O O L )trueOrFalse
{
r e t u r n trueOrFalse ? @"True" : @"False";
}
There is also another lesser known form: A ?: B; which basically returns A if A is YES or non-nil, otherwise it returns B.
For Loops
f o r (i n t i = 0; i < totalCount; i+ + ) {
// Code to execute while i < totalCount
}
Fast Enumeration
f o r (Person * person i n arrayOfPeople) {
// Code to execute each time
}
where arrayOfPeople can be any object that conforms to the NSFastEnumeration protocol. NSArray and NSSet enumerate over their
objects, NSDictionary enumerates over keys, and NSManagedObjectModel enumerates over entities.
While Loop
w h i l e (someTextCondition) {
// Code to execute while the condition is true
}
Do While Loop
d o {
// Code to execute while the condition is true
} w h i l e (someTestCondition);
Switch
Switch statements are often used in place of if statements if there is a need to test if a certain variable matches the value of
another constant or variable. For example, you may want to test if an error code integer you received matches an existing
constant value or if it's a new error code.
s w i t c h (errorStatusCode)
{
c a s e kRPServerErrorCode:
// Code to execute if it matches
b r e a k ;
c a s e kRPNetworkErrorCode:
c a s e kRPWifiErrorCode:
c a s e kRPSystemErrorCode:
// Code to execute if it matches
b r e a k ;
d e f a u l t :
// Code to execute if nothing else matched
b r e a k ;
}
17 of 30
Note: switch statements are fallthrough: when control reaches the matched case (or default block if nothing matches), it
continues execution of the next statements in the source code (including default) until the break statement or the end of switch
is reached. This also allows multiple values to match the same point without any special syntax; they are just listed with empty
bodies.
Exiting Loops
return : Stops execution and returns to the calling function. It can also be used to return a value from a method.
break : Used to stop execution of a loop.
Newer enumeration methods now have special BOOL variables (e.g. BOOL *stop) that are used to stop loop execution. Setting that
variable to YES within the loop is similar to calling break.
Back to top
Enumeration
Fast enumeration was already mentioned in the control statements section, but many collection classes also have their own
block-based methods for enumerating over a collection.
Block-based methods perform almost as well as using fast enumeration, but they just provide extra options for enumerating
over collections. An example of block-based enumeration over an NSArray would be:
NSArray * people = @[ @"Bob", @"Joe", @"Penelope", @"Jane" ];
[people enumerateObjectsUsingBlock: ^ (NSString * nameOfPerson, NSUInteger idx, B O O L * stop) {
NSLog(@"Person's name is: %@", nameOfPerson);
}];
Back to top
Extending Classes
There are a few different ways to extend a class in Objective-C, with some approaches being much easier than others.
Approach
Difficulty
Purpose
Inheritance
Easy
Used if all you want to do is inherit behavior from another class, such as NSObject
Category
Easy
Used if all you need to do is add additional methods to that class. If you also need to add instance
variables to an existing class using a category, you can fake this by using associative references.
Delegation
Easy
Used to allow one class to react to changes in or influence behavior of another class while
minimizing coupling.
Subclass
Can be
difficult
Used if you need to add methods and properties to an existing class or if you want to inherit
behavior from an existing class. Some classes are not designed to be subclassed.
Swizzle
Can be
difficult
Swizzling allows you to replace a method in an existing class with one of your own making. This
approach can lead to a lot of unexpected behavior, so it should be used very sparingly.
Inheritance
Inheritance essentially allows you to create concrete subclasses, which typically have specialized behavior, while inheriting all
methods and properties from a superclass which are specified as @public or @protected within the superclass' header file.
Looking through any framework or open source project, you can see the use of inheritance to not only get behavior for free, but
to also consolidate code and allow it to be easily reused. Examples of this approach can be seen with any of the mutable
framework classes such as NSMutableString which is a subclass of NSString.
In Objective-C, all objects have much of their behavior defined by the NSObject class through the act of class inheritance.
Without inheritance, you would have to implement common methods like object or class equality checks on your own and you'd
end up with a lot of duplicate code across classes.
// MyClass inherits all behavior from the NSObject class
@ i n t e r f a c e M y C l a s s : N S O b j e c t
18 of 30
// Class implementation
@end
Inheritance inherently creates coupling between classes, so be sure to take this into consideration.
Categories
Categories are a very useful and easy way of extending classes, especially when you don't have access to the original source code
(such as for Cocoa Touch frameworks and classes). A category can be declared for any class and 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.
Categories are also useful to:
Declare informal protocols
Group related methods similar to having multiple classes
Break up a large class implementation into multiple categories, which helps with incremental compilation
Easily configure a class differently for different applications
Implementation
Categories are named with the format: ClassYouAreExtending + DescriptorForWhatYouAreAdding
As an example, let's say that we need to add a new method to the UIImage class (and all subclasses) that allows us to easily resize
or crop instances of that class. You'd then need to create a header/implementation file pair named UIImage+ResizeCrop with the
following implementation:
UIImage+ResizeCrop.h
@ i n t e r f a c e U I I m a g e ( R e s i z e C r o p )
- (UIImage * )c r o p p e d I m a g e W i t h S i z e : (CGSize)size;
- (UIImage * )r e s i z e d I m a g e W i t h S i z e : (CGSize)size;
@end
UIImage+ResizeCrop.m
#import "UIImage+ResizeCrop.h"
@ i m p l e m e n t a t i o n U I I m a g e ( R e s i z e C r o p )
- (UIImage * )c r o p p e d I m a g e W i t h S i z e : (CGSize)size
{
// Implementation code here
}
- (UIImage * )r e s i z e d I m a g e W i t h S i z e : (CGSize)size
{
// Implementation code here
}
You could then call these methods on any instances of UIImage or it's subclasses such as:
UIImage * croppedImage = [userPhoto croppedImageWithSize: photoSize];
Associative References
Unless you have access to the source code for a class at compile time, it is not possible to add instance variables and properties to
a class by using a category. Instead, you have to essentially fake this by using a feature of the Objective-C runtime called
associative references.
For example, let's say that we want to add a public property to the UIScrollView class to store a reference to a UIView object, but
we don't have access to UIScrollView's source code. We would have to create a category on UIScrollView and then create a pair of
19 of 30
getter/setter methods for this new property to store a reference like this:
UIScrollView+UIViewAdditions.h
#import <UIKit/UIKit.h>
@ i n t e r f a c e U I S c r o l l V i e w ( U I V i e w A d d i t i o n s )
@ p r o p e r t y (nonatomic, strong) UIView * myCustomView;
@end
UIScrollView+UIViewAdditions.m
#import "UIScrollView+UIViewAdditions.h"
#import <objc/runtime.h>
s t a t i c c h a r UIScrollViewMyCustomView;
@ i m p l e m e n t a t i o n U I S c r o l l V i e w ( U I V i e w A d d i t i o n s )
@ d y n a m i c myCustomView;
- (v o i d )s e t M y C u s t o m V i e w : (UIView * )customView
{
[self willChangeValueForKey: @"myCustomView"];
objc_setAssociatedObject(self, & UIScrollViewMyCustomView, customView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey: @"myCustomView"];
}
- (UIView * )m y C u s t o m V i e w {
r e t u r n objc_getAssociatedObject(self, & UIScrollViewMyCustomView);
}
@end
Let's explain a bit about what's happening here:
We create a static key called UIScrollViewMyCustomView that we can use to get and set the associated object. Declaring it as
static ensures that it is unique since it always points to the same memory address.
Next, we declare the property we are adding as @dynamic which tells the compiler that the getter/setter is not implemented
by the UIScrollView class itself.
Within the setter, we use willChangeValueForKey followed by didChangeValueForKey to ensure that we notify any key-value
observers of a change to this property.
Within the setter, we use objc_setAssociatedObject to store a reference to the object that we really care about, customView
under the static key that we created. & is used to denote that it is a pointer to a pointer to UIScrollViewMyCustomView
Within the getter, we retrieve the object reference using objc_getAssociatedObject and a pointer to the static key
We could then use the property just like we would any other property:
UIScrollView * scrollView = [[UIScrollView alloc] init];
scrollView.myCustomView = [[UIView alloc] init];
NSLog(@"Custom view is %@", scrollView.myCustomView);
// Custom view is <UIView: 0x8e4dfb0; frame = (0 0; 0 0); layer = <CALayer: 0x8e4e010>>
More info from the docs:
The [objc_setAssociatedObject] function takes four parameters: the source object, a key, the value, and an association policy
constant. The key is a void pointer.
The key for each association must be unique. A typical pattern is to use a static variable. The policy specifies whether the
associated object is assigned, retained, or copied, and whether the association is be made atomically or non-atomically. This
pattern is similar to that of the attributes of a declared property
The possible property declaration attributes are:
OBJC_ASSOCIATION_RETAIN_NONATOMIC, OBJC_ASSOCIATION_ASSIGN, OBJC_ASSOCIATION_COPY_NONATOMIC,
20 of 30
OBJC_ASSOCIATION_RETAIN, OBJC_ASSOCIATION_COPY
Class Extension Categories
In the section about declaring classes, it shows how the private instance variables and properties within a class are actually
added to the class by using an anonymous (unnamed) category, also known as a class extension.
// Class extensions for private variables / properties
@ i n t e r f a c e M y C l a s s ()
{
i n t somePrivateInt;
// Re-declare as a private read-write version of the public read-only property
@ p r o p e r t y (readwrite, nonatomic, strong) SomeClass * someProperty;
}
@end
Class extensions are the only way that you can add variables and properties using a category, so you must have access to the
source code at compile time in order to use this method.
Core Data Categories
Categories are very useful when you are working with Core Data models and want to add additional methods to an
NSManagedObject subclass without worrying about Xcode writing over the model class each time you migrate a model.
Model classes should be kept to a minimum, containing only the model properties and Core Data generated accessor methods.
All others, such as transient methods, should be implemented in a category on the model class.
Naming Conflicts
Great advice from Apple's docs
(https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html)
:
Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
If the name of a method declared in a category is the same as a method in the original class, or a method in another category
on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
This is less likely to be an issue if youre using categories with your own classes, but can cause problems when using
categories to add methods to standard Cocoa or Cocoa Touch classes.
Delegation
Delegation is basically a way to allow one class to react to changes made in another class or to influence the behavior of another
class while minimizing coupling between the two.
The most commonly known example of the delegate pattern in iOS is with UITableViewDelegate and UITableViewDataSource.
When you tell the compiler that your class conforms to these protocols, you are essentially agreeing to implement certain
methods within your class that are required for a UITableView to properly function.
Conforming to an Existing Protocol
To conform to an existing protocol, import the header file that contains the protocol declaration (not necessary for framework
classes). Then, insert the protocol name within < > symbols and separate multiple protocols by a comma. Both options shown
below will work just fine, but I prefer to keep them in the header file since it feels cleaner to me.
Option 1: In your .h file:
#import "RPLocationManager.h"
@ i n t e r f a c e M y V i e w C o n t r o l l e r : U I V i e w C o n t r o l l e r < RPLocationManagerDelegate, UITableViewDelegate, UITableViewD
ataSource>
@end
Option 2: In the .m file:
#import "MyViewController.h"
21 of 30
22 of 30
The only difference between @required and @optional methods is that you should always check if the referenced delegate
implemented an optional method before calling it on that class.
Implementing Delegate Methods
To implement a delegate method, just conform to the protocol like was discussed earlier, and then write it as if it was a normal
method:
MyViewController.m
- (v o i d )d i d F i n d L o c a t i o n N a m e : (NSString * )locationName
{
NSLog(@"We found a location with the name %@", locationName);
}
Subclassing
Subclassing is essentially the same as inheritance, but you would typically create the subclass to either
Override a method or property implementation in the superclass; or
Create specialized behavior for the subclass (e.g. Toyota is a subclass of Car... it still has tires, an engine, etc, but it has
additional custom behavior that uniquely makes it a Toyota)
Many design patterns, such as categories and delegation, exist so that you don't have to subclass another class. For example, the
UITableViewDelegate protocol was created to allow you to provide the implementation of methods like
tableView:didSelectRowAtIndexPath: in your own class instead of having to subclass UITableView to override that method.
Other times, classes like NSManagedObject are designed to be easily subclassed. The general rule of thumb is to subclass another
class only if you can satisfy the Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) :
If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the
desirable properties of that program.
Example
Let's assume that we want to model cars. All cars have similar behavior and characteristics, so let's put some of that in a
superclass called Car.
Car.h
#import <Foundation/Foundation.h>
@ i n t e r f a c e C a r : N S O b j e c t
@ p r o p e r t y (nonatomic, strong) NSString * make;
@ p r o p e r t y (nonatomic, strong) NSString * model;
@ p r o p e r t y (nonatomic, assign) NSInteger year;
- (v o i d )s t a r t E n g i n e ;
- (v o i d )p r e s s G a s P e d a l ;
- (v o i d )p r e s s B r a k e P e d a l ;
@end
Car.m
#import "Car.h"
@ i m p l e m e n t a t i o n C a r
- (v o i d )s t a r t E n g i n e
{
NSLog(@"Starting the engine.");
}
- (v o i d )p r e s s G a s P e d a l
{
NSLog(@"Accelerating...");
23 of 30
}
- (v o i d )p r e s s B r a k e P e d a l
{
NSLog(@"Decelerating...");
}
@end
Now when we want to spawn off new car makes and models with unique characteristics, we use the Car superclass as a starting
point and then add custom behavior in the subclass.
Toyota.h
#import "Car.h"
@ i n t e r f a c e T o y o t a : C a r
- (v o i d )p r e v e n t A c c i d e n t ;
@end
Toyota.m
#import "Toyota.h"
@ i m p l e m e n t a t i o n T o y o t a
- (v o i d )s t a r t E n g i n e
{
// Perform custom start sequence, different from the superclass
NSLog(@"Starting the engine.");
}
- (v o i d )p r e v e n t A c c i d e n t
{
[self pressBrakePedal];
[self deployAirbags];
}
- (v o i d )d e p l o y A i r b a g s
{
NSLog(@"Deploying the airbags.");
}
@end
Even though pressBrakePedal is declared in the Car class, that method is still accessible in the Toyota class due to inheritance.
Designated Initializers
Often times, classes implement designated class initializers to allow for easy instantiation. If you override the main designated
initializer for a class or provide a new designated initializer, it's important to ensure that you also override all other designated
initializers so they use your new implementation instead of the superclass version. If you forget to do so and someone calls one
of the secondary designated initializers on your subclass, they will get behavior from the superclass instead.
// The new designated initializer for this class
- (instancetype)i n i t W i t h F u l l N a m e : (NSString * )fullName
{
i f (self = [super init]) {
_fullName = fullName;
[self commonSetup];
}
r e t u r n self;
}
24 of 30
Swizzling
As is often the case, clarity is better than cleverness. As a general rule, it's typically better to work around a bug in a method
implementation than it is to replace the method by using method swizzling. The reason being that other people using your code
might not realize that you replaced the method implementation and then they are stuck wondering why a method isn't
responding with the default behavior.
For this reason, we don't discuss swizzling here but you are welcome to read up on it here (http://www.mikeash.com/pyblog/friday-qa2010-01-29-method-replacement-for-fun-and-profit.html) .
Back to top
Error Handling
Errors are typically handled three different ways: assertions, exceptions, and recoverable errors. In the case of assertions and
exceptions, they should only be used in rare cases since crashing your app is obviously not a great user experience.
Assertions
Assertions are used when you want to ensure that a value is what it is supposed to be. If it's not the correct value, you force exit
the app.
NSAssert(someCondition, @"The condition was false, so we are exiting the app.");
25 of 30
Important: Do not call functions with side effects in the condition parameter of this macro. The condition parameter is not
evaluated when assertions are disabled, so if you call functions with side effects, those functions may never get called when
you build the project in a non-debug configuration.
Exceptions
Exceptions are only used for programming or unexpected runtime errors. Examples: attempting to access the 6th element of an
array with 5 elements (out-of-bounds access), attempting to mutate immutable objects, or sending an invalid message to an
object. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
An example of this might be if you have a library which requires an API key to use.
// Check for an empty API key
- (v o i d )c h e c k F o r A P I K e y
{
i f (! self.apiKey | | ! self.apiKey.length) {
[NSException raise: @"Forecastr" format: @"Your Forecast.io API key must be populated before you can access
the API.", nil];
}
}
Try-Catch
If you're worried that a block of code is going to throw an exception, you can wrap it in a try-catch block but keep in mind that
this has slight performance implications
@ t r y {
// The code to try here
}
@ c a t c h (NSException * exception) {
// Handle the caught exception
}
@ f i n a l l y {
// Execute code here that would run after both the @try and @catch blocks
}
Recoverable Errors
Many times, methods will return an NSError object in a failure block or as a pointer to a pointer (in the case of NSFileManager).
These are typically returned for recoverable errors and offer a much more pleasant user experience since they can clue the user
into what just went wrong.
[forecastr getForecastForLocation: location success: ^ (i d JSON) {
NSLog(@"JSON response was: %@", JSON);
} failure: ^ (NSError * error, i d response) {
NSLog(@"Error while retrieving forecast: %@", error.localizedDescription);
}];
Creating Your Own Errors
It's also possible to create your own NSError objects to return in methods.
// Error domain & enums
NSString * c o n s t kFCErrorDomain = @"com.forecastr.errors";
t y p e d e f N S _ E N U M (NSInteger, ForecastErrorType) {
kFCCachedItemNotFound,
kFCCacheNotEnabled
};
@ i m p l e m e n t a t i o n F o r e c a s t r
- (v o i d )c h e c k F o r e c a s t C a c h e F o r U R L S t r i n g : (NSString * )urlString
s u c c e s s : (v o i d (^ )(i d cachedForecast))success
f a i l u r e : (v o i d (^ )(NSError * error))failure
{
// Check cache for a forecast
26 of 30
Passing Information
We have already discussed many ways of passing information between classes, such as through methods or delegates, but we'll
discuss a few more here and also give one more example of delegation.
27 of 30
Also, notice that we dismiss the modal view controller (AddPersonViewController) in the same class that originally showed it.
This is the recommended approach by Apple.
NSNotificationCenter
Notifications are broadcast messages that are used to decouple classes and establish anonymous communication between
objects at runtime. Notifications may be posted by any number of objects and received by any number of objects thus enabling
one-to-many and many-to-many relationships between objects.
Note: Notifications are sent synchronously so if your observer method takes a long time to return, you are essentially prevently
the notification from being sent to other observing objects.
Registering Observers
You can register to be notified when a certain event has happened, including system notifications, such as a UITextField which
has begun editing.
[[NSNotificationCenter defaultCenter] addObserver: self selector: @ s e l e c t o r (textFieldDidBeginEditing: )
n a m e : UITextFieldTextDidBeginEditingNotification object: self];
When the UITextFieldTextDidBeginEditingNotification notification is broadcast by the OS framework, the
textFieldDidBeginEditing: will be called by NSNotificationCenter and an object will be sent along with the notification that
could contain data.
A possible implementation of the textFieldDidBeginEditing: method could be:
#pragma mark - Text Field Observers
- (v o i d )t e x t F i e l d D i d B e g i n E d i t i n g : (NSNotification * )notification
{
// Optional check to make sure the method was called from the notification
i f ([notification.name isEqualToString: UITextFieldTextDidBeginEditingNotification])
{
// Do something
}
}
Removing Observers
It's important to remove yourself as an observer when the class is deallocated, otherwise NSNotificationCenter will attempt to
call a method on a deallocated class and a crash will ensue.
- (v o i d )d e a l l o c
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
}
Posting Notifications
You can also create and post your own notifications. It's best practice to keep notification names in a constants file so that you
don't accidentally misspell one of the notification names and sit there trying to figure out why the notification wasn't
sent/received.
Naming notifications:
Notifications are identified by NSString objects whose names are composed in this way:
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
Declare a string constant, using the notification name as the string's value:
// Remember to put the extern of this in the header file
NSString * c o n s t kRPAppDidResumeFromBackgroundNotification = @"RPAppDidResumeFromBackgroundNotification";
Post notification:
[[NSNotificationCenter defaultCenter] postNotificationName: kRPAppDidResumeFromBackgroundNotification object: self];
28 of 30
Storyboard Segue
When you are transitioning from one view controller to another in a storyboard, there is an easy way to pass data between the
two by implementing the prepareForSegue:sender: method:
#pragma mark - Segue Handler
- (v o i d )p r e p a r e F o r S e g u e : (UIStoryboardSegue * )segue s e n d e r : (i d )sender
{
i f ([segue.identifier isEqualToString: @"showLocationSearch"] {
[segue.destinationViewController setDelegate: self];
} e l s e i f ([segue.identifier isEqualToString: @"showDetailView"]) {
DetailViewController * detailView = segue.destinationViewController;
detailView.location = self.location;
}
}
User Defaults
User defaults are basically a way of storing simple preference values which can be saved and restored across app launches. It is
not meant to be used as a data storage layer, like Core Data or sqlite.
Storing Values
NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setValue: @"Some value" forKey: @"RPSomeUserPreference"];
[userDefaults synchronize];
Always remember to call synchronize on the defaults instance to ensure they are saved properly.
Retrieving Values
NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
i d someValue = [userDefaults valueForKey: @"RPSomeUserPreference"];
There are also other convenience methods on NSUserDefaults instances such as boolForKey:, stringForKey:, etc.
Back to top
Common Patterns
Singletons
Singleton's are a special kind of class where only one instance of the class exists for the current process. They are a convenient
way to share data between different parts of an app without creating global variables or having to pass the data around manually,
but they should be used sparingly since they often create tighter coupling between classes.
To turn a class into a singleton, you place the following method into the implementation (.m) file, where the method name is
prefixed with shared plus another word which best describes your class. For example, if the class is a network or location
manager, you would name the method sharedManager instead of sharedInstance.
+ (instancetype)s h a r e d I n s t a n c e
{
s t a t i c i d sharedInstance = nil;
29 of 30
s t a t i c d i s p a t c h _ o n c e _ t onceToken;
dispatch_once(& onceToken, ^ {
sharedInstance = [[self alloc] init];
});
r e t u r n sharedInstance;
}
The use of dispatch_once ensures that this method is only ever executed once, even if it's called multiple times across many
classes or different threads.
If the above code were placed within MyClass, then you would get a reference to that singleton class in another class with the
following code:
MyClass * myClass = [MyClass sharedInstance];
[myClass doSomething];
NSLog(@"Property value is %@", myClass.someProperty);
Back to top
30 of 30