Làm cách nào tôi có thể đối phó với khấu hao suy luận @objc với #selector () trong Swift 4?


131

Tôi đang cố gắng chuyển đổi mã nguồn dự án của mình từ Swift 3 sang Swift 4. Một cảnh báo Xcode đang đưa ra cho tôi là về các công cụ chọn của tôi.

Chẳng hạn, tôi thêm mục tiêu vào một nút bằng cách sử dụng bộ chọn thông thường như thế này:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

Đây là cảnh báo mà nó cho thấy:

Đối số của '#selector' đề cập đến phương thức cá thể 'myAction ()' trong 'ViewContoder' phụ thuộc vào suy luận thuộc tính '@objc' trong Swift 4

Thêm '@objc' để hiển thị phương thức cá thể này vào Objective-C

Bây giờ, nhấn Fixvào thông báo lỗi thực hiện chức năng này:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

Tôi thực sự không muốn đổi tên tất cả các chức năng của mình để bao gồm @objcnhãn hiệu và tôi cho rằng điều đó là không cần thiết.

Làm thế nào để tôi viết lại bộ chọn để đối phó với sự phản đối?


Câu hỏi liên quan:


3
Không, đánh dấu chúng @objc cần thiết để đưa chúng ra Obj-C, và do đó sử dụng với bộ chọn.
Hamish

3
Vì vậy, phần không dùng là suy ra các chức năng truy cập công cộng là @objc? Điều đó hơi khó chịu, nhưng tôi thường làm cho các chức năng này ở chế độ riêng tư, yêu cầu tôi đánh dấu nó là @objcdù sao đi nữa.
Connor

2
Xem SE-0160 để biết thêm thông tin về sự thay đổi. Một cách khác là đánh dấu lớp đã cho của bạn là @objcMembersđể hiển thị tất cả các thành viên tương thích Obj-C với Obj-C, nhưng tôi không khuyên bạn trừ khi bạn thực sự cần phải tiếp xúc với toàn bộ lớp.
Hamish

3
@LinusGeffarth Như đã nói trong đề xuất, có thể việc tăng kích thước của liên kết nhị phân và động của bạn sẽ mất nhiều thời gian hơn. Tôi thực sự không nghĩ rằng nó quá rắc rối cho sự rõ ràng thêm mà bạn đặc biệt có ý nghĩa cho một thứ cụ thể được sử dụng từ Obj-C.
Hamish

1
Đã thử, không làm việc.
LinusGeffarth

Câu trả lời:


156

Cách khắc phục là chính xác - không có gì về bộ chọn mà bạn có thể thay đổi để làm cho phương thức mà nó đề cập đến tiếp xúc với Objective-C.

Toàn bộ lý do cho cảnh báo này ở nơi đầu tiên là kết quả của SE-0160 . Trước Swift 4, internalhoặc các thành viên tương thích Objective-C cao hơn củaNSObject các lớp kế thừa đã được suy ra @objcvà do đó được tiếp xúc với Objective-C, do đó cho phép chúng được gọi bằng cách sử dụng các bộ chọn (vì thời gian chạy Obj-C là bắt buộc để tra cứu phương thức thực hiện cho một bộ chọn nhất định).

Tuy nhiên trong Swift 4, đây không còn là trường hợp nữa. Hiện tại, chỉ có các khai báo rất cụ thể được suy ra là @objcghi đè các @objcphương thức, triển khai các @objcyêu cầu giao thức và khai báo với các thuộc tính ngụ ý @objc, chẳng hạn như@IBOutlet .

Động lực đằng sau điều này, như chi tiết trong đề xuất được liên kết ở trên , trước hết là để ngăn chặn quá tải phương thức trongNSObject kế thừa các lớp va chạm với nhau do có các bộ chọn giống hệt nhau. Thứ hai, nó giúp giảm kích thước nhị phân bằng cách không phải tạo thun cho các thành viên không cần phải tiếp xúc với Obj-C và thứ ba cải thiện tốc độ liên kết động.

Nếu bạn muốn giới thiệu một thành viên với Obj-C, bạn cần đánh dấu nó là @objc, ví dụ:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(trình di chuyển sẽ tự động làm điều này cho bạn với các bộ chọn khi chạy với tùy chọn "thu nhỏ suy luận" được chọn)

Để giới thiệu một nhóm thành viên với Obj-C, bạn có thể sử dụng @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

Điều này sẽ phơi bày tất cả các thành viên được xác định trong Obj-C và đưa ra lỗi đối với bất kỳ thành viên nào không thể tiếp xúc với Obj-C (trừ khi được đánh dấu rõ ràng là @nonobjc ).

Nếu bạn có một lớp mà bạn cần tất cả các thành viên tương thích Obj-C được tiếp xúc với Obj-C, bạn có thể đánh dấu lớp đó là @objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

Bây giờ, tất cả các thành viên có thể được suy luận @objcsẽ được. Tuy nhiên, tôi không khuyên bạn nên làm điều này trừ khi bạn thực sự cần tất cả các thành viên tiếp xúc với Obj-C, với những nhược điểm đã đề cập ở trên về việc các thành viên tiếp xúc không cần thiết.


2
Tại sao Xcode không tự động làm điều đó khi chuyển đổi mã thành cú pháp mới nhất?
LinusGeffarth

1
Tôi tự hỏi, @IBActioncũng tự động @objc? đó không phải là trường hợp cho đến bây giờ nhưng nó sẽ hợp lý. EDIT: nvm, đề xuất nêu rõ @IBActionlà đủ cho một phương thức @objc.
Sulthan

8
Tôi không hiểu gì về điều này. Tôi không có mã Objective-C gì bao giờ. Không có cách Swift thuần túy để thêm mục tiêu vào một nút? Hoặc sử dụng bộ chọn? Tôi không muốn mã của mình bị đánh đố với thuộc tính này. Có phải vì một UIButton có nguồn gốc từ một số NSObject / Obj-c-thing?
Sti

11
@Sti Vì vậy, để trả lời trực tiếp " Không có cách đơn giản nào để thêm mục tiêu vào nút? Hay sử dụng bộ chọn? " - không, không có cách sử dụng bộ chọn "thuần túy" để gửi đến các phương thức. Họ dựa vào thời gian chạy Obj-C để tra cứu việc thực hiện phương thức để gọi cho một bộ chọn cụ thể - thời gian chạy Swift không có khả năng đó.
Hamish

12
Người chọn là một mớ hỗn độn. Có vẻ như mọi cập nhật nhanh chóng khác mà họ đang làm hỏng nó. Tại sao chúng ta không thể có bộ chọn tự động hoàn thành nhanh chóng cho các phương thức. Làm cho mã trông rất xấu xí.
John Riselvato

16

Như Tài liệu chính thức của Apple . bạn cần sử dụng @objc để gọi Phương thức chọn của bạn.

Trong Objective-C, bộ chọn là một loại đề cập đến tên của phương thức Objective-C. Trong Swift, bộ chọn Objective-C được biểu diễn bằng Selectorcấu trúc và có thể được xây dựng bằng #selector biểu thức. Để tạo bộ chọn cho một phương thức có thể được gọi từ Objective-C, hãy truyền tên của phương thức, chẳng hạn như #selector(MyViewController.tappedButton(sender:)). Để xây dựng bộ chọn cho phương thức getter hoặc setter Objective-C của thuộc tính, hãy chuyển tên thuộc tính được tiền tố bởi getter:hoặc setter:nhãn, chẳng hạn như #selector(getter: MyViewController.myButton).


tất cả những gì tôi hiểu từ điều này là trong một số trường hợp tôi cần thêm @objc vào đầu func cho dù tôi có gọi nó từ Objective-C hay không. Tôi mới học Swift đã sử dụng Obj-C trong nhiều năm
SundialSoft

10

Theo tôi, Swift 4.2, tất cả những gì bạn cần làm là gán @IBAction cho phương thức của bạn và bạn có thể tránh chú thích @objc ngớ ngẩn này

`` `

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

1
Điều này cũng sẽ cho người xây dựng giao diện biết rằng chức năng này có thể được kết nối, có thể không phải là điều bạn muốn. Nó cũng đang làm điều tương tự như @objc.
Josh Paradroid

2

Như đã đề cập trong các câu trả lời khác, không có cách nào để tránh @objc chú thích cho các bộ chọn.

Nhưng cảnh báo được đề cập trong OP có thể bị tắt tiếng bằng cách thực hiện các bước sau:

  1. Chuyển đến Cài đặt bản dựng
  2. Tìm kiếm từ khóa @objc
  3. Đặt giá trị của giao diện Swift 3 @objc thànhOff

dưới đây là ảnh chụp màn hình minh họa các bước được đề cập ở trên:

Im lặng cảnh báo "Giao diện Swift 3 @objc"

Hi vọng điêu nay co ich


Làm thế nào điều này ảnh hưởng đến dự án của bạn trong bức tranh lớn hơn?
Zonily Jame

1
Làm thế nào về kích thước * .ipa hoặc * .xarchive và thời gian xây dựng?
Zonily Jame

@ZonilyJame tôi không nhận thấy thời gian xây dựng, nhưng không có ảnh hưởng đến kích thước IPA
WARLOR S1LENT

Đây không phải là giá trị được đề xuất mà bạn có thể sử dụng nếu bạn sử dụng phiên bản swift 4.0+ theo hướng dẫn của Apple. Kiểm tra trợ giúp này.apple.com/xcode/mac/c Hiện / # / deve838b19a1
Bhavin_m

1
Bạn có thể cập nhật câu trả lời này bằng cách đặt giá trị thành Mặc định trong Xcode 11. Điều quan trọng là phải đặt giá trị trên cả dự án và mục tiêu của bạn để loại bỏ hoàn toàn cảnh báo.
Tommie C.

2

Nếu bạn cần các thành viên c khách quan trong trình điều khiển chế độ xem của mình, chỉ cần thêm @objcMembers ở đầu trình điều khiển chế độ xem. Và bạn có thể tránh điều này bằng cách thêm IBAction vào mã của bạn.

@IBAction func buttonAction() {

}

Hãy chắc chắn để kết nối ổ cắm này trong bảng phân cảnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.