2016-07-05 18 views
5

Tôi muốn lên lịch một cuộc gọi chức năng trong tương lai. Tôi đang sử dụng Swift.Swift - scheduleTimerWithTimeInterval - NSInvocation

Tôi muốn gọi lại một phương pháp đó là tư nhân và trả về một Promise (từ PromiseKit)

Tất cả các ví dụ tôi đã nhìn thấy sử dụng

NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, target: AnyObject, selector: Selector, userInfo: AnyObject?, repeats: Bool) 

Fine. Tôi đã thử

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "connect", userInfo: nil, repeats: false) 

Điều đó không thành công với No method declared with Objective-C selector 'connect'.

Objective-C đang làm gì ở đây ??

Dù sao, đề nghị tôi thêm @objc trước phương thức connect. Khỏe. Vâng, tôi có thể không phải vì vẻ Method cannot be marked @objc because its result type cannot be represented in Objective-C

Nếu tôi muốn sử dụng Mục tiêu-C tôi không được viết Swift ...

Có một scheduledTimerWithTimeInterval đó là

NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, invocation: NSInvocation, repeats: Bool) 

Nhưng từ những gì tôi đã đọc NSInvocation không phải là một điều Swift ...

Vì vậy, tôi đã kết thúc việc tạo ra một trình bao bọc mà không có gì khác hơn là gọi connect và trả lại Void rằng Mục tiêu C có thể understan d. Nó hoạt động nhưng nó cảm thấy rất ngu ngốc. Có cách nào tốt hơn Swift không?

Phần thưởng: tại sao javascript có thể làm điều đó đơn giản như setTimeout(this.connect, 1) và Swift không có cách nào tôi có thể tìm thấy?

Trả lời

4

Bắt đầu với iOS 10 và Swift 3, chúng ta có thể sử dụng (NS) Hẹn giờ với một đóng khối và do đó tránh Objective-C selector gọi khi cháy timer:

if #available(iOS 10.0, *) { 
     Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { (Timer) in 
      self.connect() // per the OP's example 
     }) 
    } 

Ngoài tránh @objc trang trí, sử dụng kỹ thuật này cho phép bạn gọi các phương pháp đó bao gồm các loại thông số không Objective-C-tương thích như enums và optionals.

Re: setTimeout(this.connect, 1) từ Javascript, nếu bạn không cần phải hủy bỏ nó, một loại suy trực tiếp hơn trong Swift 3 có thể là:

DispatchQueue.Main.asyncAfter(deadline: .now() + 1.0, execute { self.connect() }) 

Đó là darn khá chặt chẽ cho rằng bạn thực sự có một sự lựa chọn Trong đó thread cần chạy trên ;-)

+0

Tuyệt vời, cảm ơn! – Guig

0

Hãy nhớ Swift 2.2/Xcode 7.3 có một cách mới để sử dụng selector: Selector("funcName") đã được đổi thành #selector(ClassName.funcName)

Bạn nên sử dụng #selector:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(YourClass.connect), userInfo: nil, repeats: false) 

HOẶC Selector("connect"), tuy nhiên hãy nhớ rằng bạn sẽ nhận được cảnh báo:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("connect"), userInfo: nil, repeats: false) 

Ngoài ra, hãy xem this để biết cách sử dụng Selector().

Thông tin khác Tham chiếu bộ chọn Mục tiêu-C của phương thức here.

+0

Có cần cả bộ chọn để dễ hiểu bởi Objective-C, đó không phải là trường hợp của tôi vì nó trả về một Promise (từ PromiseKit) – Guig

+0

@Guig Có lẽ cung cấp mã tiếp theo? – fuzz

+0

:) Chắc chắn rồi. Tôi đã đề cập đến nó ở trên mặc dù: "Vâng tôi không thể vì rõ ràng' Phương pháp không thể được đánh dấu @objc vì loại kết quả của nó không thể được đại diện trong Objective-C' "nhưng tôi đồng ý mã là dễ dàng hơn để đọc – Guig

0

Có 2 cách bạn có thể gọi một timer:

// Specify the selector name as String 
// Swift will give you a warning but this is handy if you have the function's name stored 
// as a variable or need some dynamism 
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timerFired"), userInfo: nil, repeats: false) 

// The recommended way since Swift 2.2/2.3 (can't remeber exactly) 
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(MyClass.timerFired), userInfo: nil, repeats: false) 

Và cả hai giả sử bạn có một chức năng như thế này:

func timerFired() { 
    print("Hello world") 
} 
0

là Objective-C làm gì ở đây ??

Lý do cho sự cần thiết của Objective-C là nó tự động liên kết "cuộc gọi" (không có cuộc gọi trong mục tiêu-C) khi chạy, trong khi Swift không thể thực hiện việc này. Swift không thể có mã trong lớp timer mà "gọi" một "hàm" không được biết tại thời điểm biên dịch là NSTimer.

BTW: NSTimer sử dụng NSInvocation (hoặc một cái gì đó tương tự không công nghệ tương tự) để thực hiện "cuộc gọi". Do đó việc sử dụng NSTimer không phải là nhiều hơn, nhưng sự cần thiết cho việc ràng buộc muộn hơn sẽ làm cho các nhà phát triển Swift cảm thấy tốt hơn.

Nếu tôi muốn sử dụng Mục tiêu-C tôi không được viết Swift ...

Ngay cả mã của bạn là hoàn toàn viết bằng Swift, phải mất khối lượng của lợi ích từ cuối ràng buộc Objective-C . Nhiều kỹ thuật cốt lõi của Cocoa không thể viết trong Swift, bao gồm chuỗi trả lời, trình quản lý hoàn tác, Dữ liệu cốt lõi, hoạt ảnh… (Mặt khác, bạn có thể xác định toán tử, tiến bộ lớn trong kỹ nghệ phần mềm và mô tả toàn bộ câu chuyện.)

0

Gọi lại cho NSTimer cần phải truy cập được đối tượng-C, ngay cả khi bạn đang sử dụng nhanh chóng. Trong trường hợp của tôi, điều đó có nghĩa là hai điều:

  • trang trí phương thức riêng với @objc do các phương thức riêng tư mặc định không thể truy cập được đối tượng-C từ lớp Swift.
  • bọc phương thức của tôi theo phương thức gọi và không trả lại gì, để trả về cuộc gọi lại. Điều này là cần thiết vì Objective-C không biết cách xử lý loại Promise.

Vì vậy, cuối cùng nó trông giống như:

import PromiseKit 

class bla : NSObject { 
    private func myCallback() -> Promise<Void> { 
    return Promise { fullfill, reject in 
     // ... 
    } 
    } 

    @objc private func myCallbackWrap -> Void { 
    myCallback() 
    } 

    func startTimeout() -> Void { 
     NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: #selector(myCallbackWrap), userInfo: nil, repeats: false) 
    } 
} 
Các vấn đề liên quan