IOS Adopting Modern Objective-C

Source : Adopting Modern Objective-C

Over the years, the Objective-C language has grown and evolved. Although the core concepts and practices remain the same, parts of the language have been through significant changes and improvements. These modernizations improve type safety, memory management, performance, and other aspects of Objective-C, making it easier for you to write correct code. It’s important to adopt these changes in your existing and future code to help it become more consistent, readable, and resilient.

多年來,Objective-C逐漸發展及演變。雖然核心概念和做法保持一致,部分的語言已經通過顯著的變化和改進。這些現代化(modernizations)改進了類型安全,記憶體管理,性能和Objective-C的其他方面,使您更輕鬆地編寫出正確的代碼。採用這些變化到你既有或未來的程式碼是非常重要的,讓它變得更加一致,可讀性,彈性。

Instancetype

Use the instancetype keyword as the return type of methods that return an instance of the class they are called on (or a subclass of that class). These methods include alloc, init, and class factory methods.

Instancetype
1
2
3
4
5
6
7

@interface MyObject

- (instancetype)myFactoryMethod;

@end

個人理解是alloc, init, and class factory methods 都從回傳 id 改成回傳 instancetype

To make sure instancetype factory methods have the right subclassing behavior, be sure to use [self class] when allocating the class rather than referring directly to the class name.

為了確保instancetype工廠方法有正確的子類的行為,一定要使用[self class] when allocating the class rather than referring directly to the class name

+ (instancetype)factoryMethodA { return [[[self class] alloc] init]; }

Properties

Using properties instead of instance variables in as many places as possible provides many benefits:

  • Autosynthesized getters and setters. When you declare a property, by default getter and setter methods are created for you.
  • Better declaration of intent of a set of methods. Because of accessor method naming conventions, it’s clear exactly what the getter and setter are doing.
  • Property keywords that express information about behavior. Properties provide the potential for declaration of attributes like assign (vs copy), weak, atomic (vs nonatomic), and so on.

使用property而不是實體變數,在許多方面可以提供很多好處

  1. 自動合成getter & setter
  2. 更明確的方法意圖:因為存取方法已有它的命名慣例,因此它可以很明確的說明getter & setter 在幹嘛 XD
  3. property的關鍵字表達了額外的行為資訊:如copy,assign,weak等等

The naming convention for Boolean properties is to declare them with a named getter starting with the word “is”:

Boolean properties的命名慣例是在getter前加入"is"

Boolean properties
1
2
3

@property (readonly, getter=isBlue) BOOL blue;

As a result, all of the following work:

Boolean properties getter
1
2
3
4
5

if (color.blue) { }
if (color.isBlue) { }
if ([color isBlue]) { }

When deciding what can be a property, keep in mind that the following are not properties:

  • init method
  • copy method, mutableCopy method
  • A class factory method
  • A method that initiates an action and returns a BOOL result
  • A method that explicitly changes internal state as a side effect of a getter

請注意下列幾項並不是property:

  1. init 方法
  2. copy, mutableCopy 方法
  3. 類別工廠方法
  4. 發起行動後並回傳BOOL結果的方法
  5. 明確地改變內部狀態(或者說是具有副作用)的getter

Additionally, consider the following set of rules when identifying potential properties in your code:

  • A read/write property has two accessor methods. The setter takes one argument and returns nothing, and the getter takes no arguments and returns one value. If you convert this set of methods into a property, tag it with the readwrite keyword.
  • A read-only property has a single accessor method, the getter, which takes no arguments and returns one value. If you convert this method into a property, tag it with the readonly keyword.
  • The getter should be idempotent (if a getter is called twice, the second call results in the same result as the first). However, it is acceptable for a getter to compute the result each time it’s called.
  1. read/write property有兩個存取方法。setter接收一個參數並且不回傳值,getter不接收參數且回傳一個值,假使你轉換這組方法到property的話,把它標上readwrite關鍵字。
  2. getteridempotent(當getter被呼叫兩次這裡指連續呼叫,兩次的結果應該是要相同的)。然而,getter被呼叫的時候才去計算他的結果也是可以被接受的。

Enumeration Macros

The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.

Old Enum
1
2
3
4
5
6
7
8
9

enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

New Enum
1
2
3
4
5
6
7
8

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};

Old Enum Option
1
2
3
4
5
6
7
8
9
10
11
12

enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

New Enum Option
1
2
3
4
5
6
7
8
9
10
11

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

Object Initialization

In Objective-C, object initialization is based on the notion of a designated initializer, an initializer method that is responsible for calling one of its superclass’s initializers and then initializing its own instance variables. Initializers that are not designated initializers are known as convenience initializers. Convenience initializers typically delegate to another initializer—eventually terminating the chain at a designated initializer—rather than performing initialization themselves.

The designated initializer pattern helps ensure that inherited initializers properly initialize all instance variables. A subclass that needs to perform nontrivial initialization should override all of its superclass’s designated initializers, but it does not need to override the convenience initializers. For more information about initializers, see Object Initialization.

To clarify the distinction between designated and designated initializers clear, you can add the NS_DESIGNATED_INITIALIZER macro to any method in the init family, denoting it a designated initializer. Using this macro introduces a few restrictions:

  • The implementation of a designated initializer must chain to a superclass init method (with [super init…]) that is a designated initializer for the superclass.
  • The implementation of a convenience initializer (an initializer not marked as a designated initializer within a class that has at least one initializer marked as a designated initializer) must delegate to another initializer (with [self init…]).
  • If a class provides one or more designated initializers, it must implement all of the designated initializers of its superclass.

If any of these restrictions are violated, you receive warnings from the compiler.

If you use the NS_DESIGNATED_INITIALIZER macro in your class, you need to mark all of your designated initializers with this macro. All other initializers will be considered to be convenience initializers.

Automatic Reference Counting (ARC)

Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Instead of your having to remember when to use retain, release, and autorelease, ARC evaluates the lifetime requirements of your objects and automatically inserts appropriate memory management calls for you at compile time. The compiler also generates appropriate dealloc methods for you.

ARC