picture

Blog

Khai báo biến trong extension của Swift



Một trong những hạn chế của Swift Extension là không thể khai báo biến trong nó. Trong bài viết này, chúng ta sẽ tìm hiểu về cách để tạo biến trong extension.

Tổng quan

Swift Extension cho phép chúng ta thêm các hàm cho một class, struct, enum hoặc protocol có sẵn. Extension thường được sử dụng nhiều trong project. Và đôi khi, ta cần phải giữ reference đến một object nào đó trong những extension này. Không may là, Swift không cung cấp một phương pháp cụ thể nào cho việc này cả. Tuy nhiên, Swift API hiện tại có cách hỗ trợ cho việc này. Bắt đầu nào 😉

Cách thông thường

Nếu bạn chưa bao giờ thử khai báo biến trong extension, thì việc khai báo sẽ đại khái như thế này.

Ví dụ chúng ta có 1 protocol tên là ToggleProtocol, chứa 1 phương thức toggle. Sau đó, ta cho UIButton implement protocol này để thay đổi trạng thái của button. Khá đơn giản phải không? 😉

protocol ToggleProtocol {
    func toggle()
}
enum ToggleState {
    case on
    case off
}
extension UIButton: ToggleProtocol {
    private(set) var toggleState = ToggleState.off
    func toggle() {
        toggleState = toggleState == .on ? .off : .on
        if toggleState == .on {
            // Shows background for status on
        } else {
            // Shows background for status off
        }
    }
}

Khoan, có lỗi compile rồi! Lỗi xảy ra ở dòng

private(set) var toggleState = ToggleState.off:

error: extensions may not contain stored properties.

Cú lừa chăng? Không đâu bạn ơi, đó là do Swift không support việc khai báo biến trong extension 😔😔. Vì thế, bạn không thể dùng thuộc tính toggleState được. Giờ ta phải thêm 1 chút ma thuật để đưa mọi thứ về với đúng quỹ đạo của nó.

Cách ma thuật

protocol ToggleProtocol {
    func toggle()
}
enum ToggleState {
    case on
    case off
}
extension UIButton: ToggleProtocol, PropertyStoring {
    typealias T = ToggleState
    private struct CustomProperties {
        static var toggleState = ToggleState.off
    }
    var toggleState: ToggleState {
        get {
            return getAssociatedObject(&CustomProperties.toggleState, defaultValue: CustomProperties.toggleState)
        }
        set {
            return objc_setAssociatedObject(self, &CustomProperties.toggleState, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }
    func toggle() {
        toggleState = toggleState == .on ? .off : .on
        if toggleState == .on {
            // Shows background for status on
        } else {
            // Shows background for status off
        }
    }
}

Ở đây ta có 2 keyword mới đó là objc_getAssociatedObjectobjc_setAssociatedObject. Hãy cùng tìm hiểu rõ hơn nào 😋😋.

objc_getAssociatedObject trả về một giá trị của object tương ứng với key được truyền vào. Phương thức này yêu cầu 2 tham số.

  1. object: Any!: source object. Ta có thể truyền self trong trường hợp này.
  2. key: UnsafeRawPointer!: con trỏ nối giữa key và object.

objc_setAssociatedObject set giá trị object cho key. Phương thức này yêu cầu 4 tham số.

  1. object: Any!: source object. Ta có thể truyền self trong trường hợp này.
  2. key: UnsafeRawPointer!: con trỏ nối giữa key và object.
  3. value: Any!: object truyền vào.
  4. policy: objc_AssociationPolicy: Kiểu lưu object. Có thể là 1 trong các kiểu sau:
    • OBJC_ASSOCIATION_ASSIGN: Lưu object với weak reference.
    • OBJC_ASSOCIATION_RETAIN_NONATOMIC: Lưu strong object theo cách nonatanomic.
    • OBJC_ASSOCIATION_COPY_NONATOMIC: Khởi tạo object kiểu non-atomic và tạo 1 bopy cho object đó.
    • OBJC_ASSOCIATION_RETAIN: Lưu strong object theo cách atanomic.
    • OBJC_ASSOCIATION_COPY: Khởi tạo object kiểu atomic và tạo 1 bopy cho object đó.

Property Storing là 1 kiểu Generic cho phép ta khởi tạo object ngắn hơn

protocol PropertyStoring {
    associatedtype T
    func getAssociatedObject(_ key: UnsafeRawPointer!, defaultValue: T) -> T
}
extension PropertyStoring {
    func getAssociatedObject(_ key: UnsafeRawPointer!, defaultValue: T) -> T {
        guard let value = objc_getAssociatedObject(self, key) as? T else {
            return defaultValue
        }
        return value
    }
}

 

Và đây là cách để tạo biến trong Swift Extension. ✌️✌️✌️

Nguồn

  1. Source
  2. objc_setassociatedobject
  3. objc_getassociatedobject

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *


« »

Keep Better In The Life


WP Facebook Auto Publish Powered By : XYZScripts.com