optional

Kiểu Optional là gì ?

  • Kiểu Optional : cho phép gán nil
  • Không phải kiểu Optional : không cho phép gán nil

Phương pháp khai báo kiểu Optional

var a: Int? // Kiểu Optional
var b: Int  // Không phải kiểu Optional

var c: String? // Kiểu Optional
var d: String  // Không phải kiểu Optional

T? là syntactic sugar của Optional<T>

var a: Int?
var b: Optional<Int> // tương đương với Int?

Kiểu Optional cho phép gán nil

var a: Int? // Kiểu Optional
a = nil     // Không phát sinh compile error

var b: Int // Không phải kiểu optional
b = nil    // Compile error : Could not find an overload for '__conversion' that accepts the supplied arguments

Giá trị khởi tạo của kiểu Optional là nil

Giá trị khởi tạo của kiểu Optional là nil.

var a: Int? // Kiểu Optional
println(a)  // -> nil

Nếu không phải là kiểu Optional thì không có gì trong giá trị khởi tạo (không có cả nil)

var b: Int // Không phải kiểu Optional
println(b) // Compile error: Variable 'b' used before being initialized

Kiểu Optional không thể xử lý giống kiểu không phải Optional

var a: Int = 1
println(a + 2) // -> 3

var b: Int? = 1
println(b + 2) // Compile error: Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

Theo như trên, thì Int? là kiểu khác với Int nên không thể thao tác giống vây được. để có thể xử lý giống với Int thì cần phải unwrap.(mô tả bên dưới).

Wrap và unwrap

【Đang được wrap】là gì?

Biến số có kiểu Optional (kiểu Optional<T>) gọi là『đang được wrap』.

【Unwrap】?

Việc lấy ra biến kiểu T từ kiểu Optional(kiểu Optional<T>)được gọi là 『Unwrap』.

Phương pháp unwrap biến kiểu Optional

Có những phương pháo dưới dùng để unwrap biến kiểu Optional

  1. Forced Unwrapping
  2. Optional Chaining
  3. Optional Binding
  4. Implicitly Unwrapped Optional Type

1. Forced Unwrapping

Sử dụng ! như dưới đây để có thể unwrap biến kiểu Optional.

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = Dog() // Kiểu Optional

// Không thể call method bark nếu vẫn là trạng thái đã wrap
println(d.bark()) // Compile error: 'Dog?' does not have a member named 'bark'

// Có thể unwrap bằng cách thêm 「!」 vào sau biến d (Forced Unwrapping)
println(d!.bark()) // -> Wan!

// Nếu biến d là nil thì sẽ phát sinh runtime error khi unwrap bằng 「!」
d = nil
println(d!.bark()) // Runtime error: fatal error: Can't unwrap Optional.None

2. Optional Chaining

Sử dụng ? chứ không phải ! như bên dưới đây để unwrap.

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = Dog() // Kiểu Optional

// Nếu thêm 「?」 vào phía sau biến d thì sẽ unwrap (Optional Chaining)
println(d?.bark()) // -> Wan!

// Nếu biến d là nil thì unwrap sẽ trả lại nil
d = nil
println(d?.bark()) // -> nil

Khác với Forced Unwrapping, cho dù unwrap cho nil thì cũng không phát sinh runtime error, mà thay vào đó sẽ trả về nil.

Chú ý: về println

Kết quả output của println(d?.bark()) sẽ khác với xcode version mới nhất nên phải chú ý.

  • Nếu là Xcode 6 Beta(version đã sử dụng để viết trong bài này) println(d?.bark()) // -> Wan!
  • Nếu là Xcode 6.1 GM seed 2 println(d?.bark()) // -> Optional(Wan!)

Khi output variable kiểu optional bằng println thì chuỗi Optional() sẽ được thêm vào.

3. Optional Binding

Biến được gán bằng kiểu Optional mà được khai báo trong biểu thức điều kiện IF hay WHILE thì trở thành kiểu không phải là Optional.

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var wrappedDog: Dog? = Dog() // Kiểu Optional

// Biến được khai báo trong câu IF sẽ trở thành không phải kiểu Optional(Optional Binding)
if var unwrappedDog1 = wrappedDog {
    // Không cần phải thêm「!」
    println(unwrappedDog1.bark()) // -> Wan!
}

// Khai báo「var」と「let」đều được
if let unwrappedDog2 = wrappedDog {
    println(unwrappedDog2.bark()) // -> Wan!
}

// Có thể sử dung Optional Binding trong câu WHILE
while var unwrappedDog3 = wrappedDog {
    println(unwrappedDog3.bark()) // -> Wan!
    break
}

///////////////
// Bổ sung 1 //
///////////////

//
// Các biến「unwrappedDog1」「unwrappedDog2」「unwrapedDog3」 bên trên chỉ hữu hiệu trong block
//
println(unwrappedDog1.bark()) // Compile error: Use of unresolved identifier 'unwrappedDog1'
println(unwrappedDog2.bark()) // Compile error: Use of unresolved identifier 'unwrappedDog2'
println(unwrappedDog3.bark()) // Compile error: Use of unresolved identifier 'unwrappedDog3'

///////////////
// Bổ sung 2 //
///////////////

//
// Trong trường hợp wrappedDog là nil, kết quả của điều kiện câu IF là false nên ko thực hiện xử lý trong block
//
wrappedDog = nil
if var unwrappedDog4 = wrappedDog {     // false
    println("This is not printed out.") // -> không output gì
}

4. Kiểu Implicitly Unwrapped Optional

Nếu sử dụng kiểu Implicitly Unwrapped Optional chứ không phải kiểu Optional(Optional<T>) thì có thể unwrap ngầm. Khi khai báo biến kiểu Implicitly Unwrapped Optional thì sử dụng ! chứ không phải ?.

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

// Khai báo bằng 「!」chứ ko phải bằng「?」
var d: Dog! = Dog() // Kiểu Implicitly Unwrapped Optional

// Không cần thực hiện unwrap(đã unwrap ngầm)
println(d.bark()) // -> Wan!

// Nếu biến d là nil, khi unwrap ngầm sẽ bị runtime error
d = nil
println(d.bark()) // Runtime error: fatal error: Can't unwrap Optional.None

T! là syntactic sugar của ImplicitlyUnwrappedOptional<T>

var a: Int! // Kiểu Implicitly Unwrapped Optional
var b: ImplicitlyUnwrappedOptional<Int> // Tương đương với Int!

Điểm khác nhau giữa kiểu Optional & kiểu Implicitly Unwrapped Optional

Type Khai báo Entity of type Thao tác khi unwrap
Optional var a: T? Optional<T> Cần thao tác rõ ràng
Implicitly Unwrapped Optional var a: T! ImplicitlyUnwrappedOptional<T> Không cần (được unwrap ngầm)

Cách xử lý trong trường hợp đã unwrap cho nil

Trường hợp là kiểu Optional (Optional<T>)

Phương pháp unwrap Kết quả
Trường hợp là Forced Unwrapping (phương pháp sử dụng !) Phát sinh runtime error
Trường hợp là Optional Chaining (phương pháp sử dụng ?) Trả về nil
class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = nil // Kiểu Optional

// Trường hợp Forced Unwrapping (sử dụng !)
println(d!.bark()) // Runtime error: fatal error: Can't unwrap Optional.None

// Trường hợp Optional Chaining (Sử dụng ?)
println(d?.bark()) // -> nil

Trường hợp là kiểu Implicitly Unwrapped Optional (ImplicitlyUnwrappedOptional<T>)

Phương pháp unwrap Kết quả
Unwrap ngầm Phát sinh runtime error
class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog! = nil // Kiểu Implicitly Unwrapped Optional
println(d.bark()) // Runtime error: fatal error: Can't unwrap Optional.None

Develop enviroment

Xcode 6 Beta

Mac OS X 10.9.3

Run enviroment

iOS Simulator (iPhone5s / iOS 8.0)

Source: http://qiita.com/cotrpepe/items/e30c7442733b93adf46a