2015-06-16 15 views
11

Tôi tự hỏi tương đương Swift là gì khi gọi phương thức trên id trong đó tính sẵn có của phương pháp được xác định khi chạy. Cụ thể là tôi đang tìm cách để làm mô hình này trong Swift:Tương đương nhanh như thế nào để thiết lập các thuộc tính trên `id`?

-(IBAction) handleEvent:(id) sender { 
    BOOL didDisable = NO; 
    if([sender respondsToSelector:@selector(setEnabled:)]) { 
     [sender setEnabled:NO]; 
     didDisable = YES; 
    } 
    [self doSomethingAsyncWithCompletionHandler:^{ 
     if(didDisable) { 
      [sender setEnabled:YES]; 
     } 
    }]; 
} 

Vấn đề lớn nhất là setEnabled: được nhập Swift như một thuộc tính (ví dụ UIBarItem) và không ai trong số các cấu trúc sau biên dịch

func handleEvent(sender: AnyObject) { 
    // Error: AnyObject does not have a member named "enabled" 
    sender.enabled? = false 

    // Error: (BooleanLiteralCompatible) -> _ is not identical to Bool 
    sender.setEnabled?(false) 
} 
+1

Nếu có thể, hãy thực hiện chức năng này bằng cách sử dụng giao thức. Sau đó bạn có thể thay đổi kiểm tra 'respondsToSelector' bằng một' if-let' như 'if let toggable = sender as? Toggable' (giả định một 'giao thức Toggable' với thuộc tính' enabled'). – dcestari

+0

Không chắc chắn, nhưng có lẽ AnyObject? – kostek

+0

http://roadfiresoftware.com/2014/07/swifts-var-is-not-objective-cs-id/ –

Trả lời

6

Trong Swift 2.0 beta 4, lời cầu nguyện của bạn được trả lời; mã này trở thành quy phạm pháp luật:

@IBAction 
func handleEvent(sender: AnyObject) { 
    if sender.respondsToSelector("setHidden:") { 
     sender.performSelector("setHidden:", withObject: true) 
    } 
} 
+0

Thật kỳ lạ, tôi không thể làm cho nó thành _do_ bất cứ điều gì cho 'setEnabled:' (nó biên dịch nhưng nút không vô hiệu hóa). Tuy nhiên, đó có thể là một lỗi tạm thời; kể từ khi nó hoạt động cho 'ẩn' Tôi nghĩ rằng chúng ta nên giả sử nó phải làm việc cho' enabled'. – matt

+1

Tôi đã gửi một lỗi về vấn đề 'performSelector (" setEnabled: ")'. Nó không sụp đổ nhưng nó cũng không làm gì cả. – matt

+0

Tôi đã có thể làm cho nó hoạt động với 'setEnabled:', nhưng theo một cách khá kỳ lạ ... '.performSelector (" setEnabled: ")' luôn đặt giá trị thành ** true ** và '.performSelector (" setEnabled: ", withObject: nil)' luôn được đặt thành ** false **, bất kể giá trị của 'withObject:'. Tôi nghĩ rằng xứng đáng nộp một lỗi thực sự ... –

24

Bạn có thể thực sự làm điều đó giống y như trước đây: bằng cách gọi respondsToSelector:. Trên thực tế, đó là chính xác những gì biểu hiện đề xuất của bạn không:

sender.setEnabled?(false) 

Cái vẻ thực sự là một cách viết tắt - nó gọi respondsToSelector: đầu tiên, và sau đó gọi setEnabled: chỉ khi kiểm tra respondsToSelector: đi. Thật không may, như bạn nói, bạn không thể lấy mã đó để biên dịch. Tuy nhiên, điều đó chỉ đơn thuần là một sự lừa gạt của các phương thức có sẵn của Swift. Thực tế là, mặc dù nó là một chút khó khăn để có được nó để biên dịch, nó có thể được thực hiện - và một khi bạn nhận được nó để biên dịch, nó hoạt động giống như bạn mong đợi.

Tuy nhiên, tôi sẽ không giải thích cách để biên dịch, bởi vì tôi không muốn khuyến khích loại mẹo này. Loại nhắn tin động này không được khuyến khích trong Swift. Nói chung, các thủ thuật nhắn tin động như mã hóa khóa-giá trị, nội tâm, và vân vân không cần thiết trong Swift và không phụ thuộc vào cách gõ mạnh mẽ của Swift. Nó sẽ là tốt hơn để làm những điều theo cách Swift, bằng cách đúc tùy chọn để một cái gì đó mà bạn có lý do để tin rằng điều này có thể được và có một tài sản enabled. Ví dụ:

@IBAction func doButton(sender: AnyObject) { 
    switch sender { 
    case let c as UIControl: c.enabled = false 
    case let b as UIBarItem: b.enabled = false 
    default:break 
    } 
} 

Hoặc:

@IBAction func doButton(sender: AnyObject) { 
    (sender as? UIControl)?.enabled = false 
    (sender as? UIBarItem)?.enabled = false 
} 
+2

ngón tay cái lên cho không khuyến khích mã mùi –

+0

Vì vậy, bạn đang nói với tôi instanceof là ít mùi? –

+0

"... nhắn tin động không được khuyến khích trong Swift ..." trong khi phần còn lại của Cocoa sử dụng nó rất nhiều. Tôi sẽ dính vào Objective-C nếu đây là trường hợp. – adib

2

Nếu bạn muốn tránh sử dụng phương pháp respondsToSelector: bạn có thể định nghĩa một giao thức để thay thế. Sau đó, mở rộng các lớp bạn muốn sử dụng đã phù hợp với định nghĩa của giao thức này (bật) và xác định hàm có biến chung phù hợp với giao thức của bạn.

protocol Enablable{ 
    var enabled:Bool { get set } 
} 

extension UIButton  : Enablable {} 
extension UIBarButtonItem : Enablable {} 

//.... 

func handleEvent<T:Enablable>(var sender: T) { 
    sender.enabled = false 
} 

Nếu bạn cần sử dụng phương pháp IBAction thì cần phải có một chút công việc xung quanh vì bạn không thể sử dụng Generics trực tiếp trên chúng.

@IBAction func handleEventPressed(sender:AnyObject){ 
    handleEvent(sender); 
} 

Chúng tôi cũng cần có chức năng chung phù hợp mà không có sự phù hợp của Enablable để chúng tôi có thể gọi handleEvent mà không biết có hay không người gửi là Enablable. May mắn là trình biên dịch đủ thông minh để tìm ra hai hàm chung nào để sử dụng.

func handleEvent<T>(var sender: T) { 
    //Do Nothing case if T does not conform to Enablable 
} 
1

Là một workaround/thay thế, bạn có thể sử dụng Key-Value Mã hóa:

@IBAction func handler(sender: AnyObject) { 

    if sender.respondsToSelector("setEnabled:") { 
     sender.setValue(false, forKey:"enabled") 
    } 
} 

này làm việc với cả hai Swift 1.2 (Xcode 6.4) và Swift 2.0 (Xcode 7 beta).

Các vấn đề liên quan