Lỗi trình biên dịch: Phương thức với bộ chọn Objective-C xung đột với khai báo trước đó với cùng bộ chọn Objective-C


209

Tôi đang bắt đầu học Swift, và đã theo dõi các bài giảng video rất hay của Đại học Stanford trên YouTube. Đây là một liên kết nếu bạn quan tâm hoặc nó giúp (mặc dù không bắt buộc phải hiểu vấn đề của tôi):

Phát triển ứng dụng iOS 8 với Swift - 2. Thêm Xcode và Swift, MVC

Trong khi theo các bài giảng, tôi đã đến một điểm mà (theo như tôi có thể nói) mã của tôi giống hệt với mã trong video nhưng trên hệ thống của tôi, tôi đã gặp lỗi trình biên dịch. Sau rất nhiều thử nghiệm và lỗi, tôi đã cố gắng giảm mã của mình xuống còn hai ví dụ, một trong số đó tạo ra lỗi, cái kia hoặc không, nhưng tôi không biết điều gì thực sự gây ra lỗi hoặc cách khắc phục.

Mã tạo ra lỗi là:

import UIKit

class BugViewController: UIViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

Điều này tạo ra lỗi trình biên dịch sau:

Phương thức 'thực hiện' với bộ chọn Objective-C 'thực hiện:' xung đột với khai báo trước đó với cùng bộ chọn Objective-C

Bằng cách đơn giản loại bỏ phân lớp phụ của UIViewControll, mã biên dịch:

import UIKit

class BugViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

Một số thông tin khác có thể có hoặc không có liên quan:

  • Gần đây tôi đã nâng cấp lên Yosemite.
  • Khi tôi cài đặt Xcode, tôi đã kết thúc phiên bản Beta (Phiên bản 6.3 (6D543q)) bởi vì (nếu tôi nhớ chính xác) đây là phiên bản tôi cần để chạy trên phiên bản OS X của mình.

Tôi một nửa hy vọng đây là một lỗi trong trình biên dịch bởi vì nếu không thì điều này không có ý nghĩa gì với tôi. Bất kỳ trợ giúp rất biết ơn nhận được!


3
Bạn có thể chạy Xcode 6.2 trên Yosemite. Bạn có thể tải xuống từ cửa hàng ứng dụng và nó có thể sống trên hệ thống của bạn với phiên bản Beta. Tôi không khuyên bạn nên sử dụng Xcode 6.3 cho lớp Stanford vào thời điểm này vì nó là phiên bản beta và bao gồm Swift 1.2, khác với phiên bản trước đó của Swift được sử dụng trong video.
vacawama

2
Câu trả lời (hiện được chấp nhận) từ người dùng (feb) từ ngày 5 tháng 4 không còn là câu trả lời hay nhất nữa. Thay vào đó, câu trả lời từ (James Zhang) từ ngày 16 tháng 4 là cụ thể và chính xác hơn.
phlebotinum

Câu trả lời:


144

Objective-C không hỗ trợ nạp chồng phương thức, bạn phải sử dụng tên phương thức khác. Khi bạn kế thừa UIViewContoder, bạn đã kế thừa NSObject và làm cho lớp có thể tương tác với Obj-C. Mặt khác, Swift hỗ trợ quá tải, đó là lý do tại sao nó hoạt động khi bạn loại bỏ quyền thừa kế.


2
Phương pháp SUPPORTS của Objective-C ghi đè (với một trình biên dịch (có thể triệt tiêu) cảnh báo cho bạn về việc quá tải một cái gì đó đã được triển khai), Apple chỉ không muốn bạn làm như vậy để khung của họ không bị quá tải. Tôi đang sử dụng quá tải như vậy UIFontmỗi ngày.
Michi

Câu trả lời của @ Polarwar dưới đây là câu trả lời hay nhất cho Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

237

Bản thân tôi cũng đang tham gia khóa học Standford và tôi cũng bị kẹt ở đây trong một thời gian dài, nhưng sau khi tìm kiếm, tôi đã tìm thấy một cái gì đó từ đây: Ghi chú phát hành Xcode và nó đã đề cập đến một cái gì đó bên dưới:

Swift 1.2 rất nghiêm ngặt trong việc kiểm tra quá tải dựa trên kiểu của các phương thức và trình khởi tạo @objc, một thứ không được Objective-C hỗ trợ.

// Has the Objective-C selector "performOperation:".
func performOperation(op: NSOperation) { /* do something */ }
// Also has the selector "performOperation:".
func performOperation(fn: () -> Void) {
    self.performOperation(NSBlockOperation(block: fn))
}

Mã này sẽ hoạt động khi được gọi từ Swift, nhưng có thể dễ dàng bị sập nếu được gọi từ Objective-C. Để giải quyết vấn đề này, hãy sử dụng một loại không được Objective-C hỗ trợ để ngăn trình biên dịch Swift hiển thị thành viên trong thời gian chạy Objective-C:

  • Nếu nó hợp lý, đánh dấu thành viên là riêng tư để vô hiệu hóa suy luận của @objc.
  • Mặt khác, sử dụng tham số giả với giá trị mặc định, ví dụ: _ nonobjc: () = (). (19826275)

Việc ghi đè các phương thức tiếp xúc với Objective-C trong các lớp con riêng không được suy ra là @objc, khiến trình biên dịch Swift bị sập. Hoàn toàn thêm thuộc tính @objc vào bất kỳ phương thức ghi đè nào như vậy. (19935352)

Các biểu tượng từ SDK không khả dụng khi sử dụng Mở nhanh trong dự án hoặc không gian làm việc sử dụng Swift. (20349540)

những gì tôi đã làm chỉ là thêm "riêng tư" vào trước phương thức ghi đè như thế này:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}

3
Giải pháp này là khả thi nhất mà tôi thấy imho, vì điều này hoàn toàn hợp lý khi đặt phương thức này ở chế độ riêng tư
mất trí nhớ

38
Xin lưu ý rằng giờ đây cũng có một thuộc tính @nonobjc, có thể được sử dụng để loại trừ một phương thức khỏi thời gian chạy Objective-C.
Erik J

2
Tôi thứ hai bình luận của @ ErikJ và câu trả lời của Polarwar bên dưới. Đây dường như là câu trả lời hay nhất về phía trước với Swift 2 và xcode 7. Nếu bạn chưa cập nhật, tôi thực sự khuyên bạn nên dùng nó.
Austin A

Câu trả lời của @ Polarwar dưới đây là câu trả lời hay nhất cho Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

111

Như đã được trả lời, ObjC không hỗ trợ nạp chồng phương thức (hai phương thức có cùng tên) và Trong swift 2 theo Xcode 7, có hai tùy chọn để giải quyết loại vấn đề này. Một tùy chọn là đổi tên phương thức bằng thuộc tính:@objc(newNameMethod:)

func methodOne(par1, par2) {...}

@objc(methodTwo:)
func methodOne(par1) {...}

một tùy chọn khác để giải quyết vấn đề này trong Xcode 7+ là bằng cách áp dụng @nonobjcthuộc tính cho bất kỳ phương thức, đăng ký hoặc trình khởi tạo nào

func methodOne() {...}

@nonobjc
func methodOne() {...}

6
điều này giải quyết vấn đề cho swift 2 (trở lên). nên được cập nhật như câu trả lời đúng nhất. ty.
Maxim Veksler

2
Đối với bất kỳ ai sử dụng Swift 2 và Xcode 7 + thì đây là câu trả lời đúng mà tôi đồng ý với Polarwar
TerNovi

17

Vấn đề UIViewControllerlà một @objclớp học. Khi kế thừa từ UIViewController, BugViewControllercũng là một @objclớp.

Điều này có nghĩa là nó phải tuân theo các quy tắc của bộ chọn Objective-C (tên của một phương thức). Các phương thức func perform(operation: (Double) -> Double)func perform(operation: (Double, Double) -> Double)cả hai đều có cùng bộ chọn @selector(perform:). Điều này không được phép.

Để giải quyết điều này, sử dụng các tên khác nhau: thích func perform1(operation: (Double) -> Double)func perform2(operation: (Double, Double) -> Double).


Tôi nghĩ cách tốt nhất để xử lý việc này là cung cấp cho các perform()phương thức của bạn các tên mô tả nhiều hơn. Những phương pháp này làm gì? Làm thế nào để họ thay đổi trạng thái của bộ điều khiển xem? Nhìn vào các UIViewControllerphương thức khác để cảm nhận về phong cách đặt tên phương thức hoặc đọc Tên phương thức nên biểu cảm và duy nhất trong một lớp


Cảm ơn - câu trả lời này hoàn hảo cho câu hỏi của tôi và vì bạn là người đầu tiên tôi sẽ đánh dấu câu này là chính xác.
Kiết tường

Có nói rằng tôi vẫn không hiểu tại sao mã trên bài giảng hoạt động vì tôi khá chắc chắn rằng nó đã làm những gì mà mã không biên dịch của tôi đã làm! Hey ho - tôi sẽ quay lại và kiểm tra lại Phải có một cái gì đó khác biệt.
Kiết tường

2
@Auspice Nó có thể không tạo ra lỗi với phiên bản Xcode mà họ đang sử dụng cho video nhưng nó vẫn là một vấn đề. Mãi đến Xcode 6.3, trình biên dịch mới có thể phát hiện ra điều này cảnh báo bạn.
Mick MacCallum

3
Paul Hegarty muốn thể hiện chức năng 'quá tải' ở đây (2 hàm có cùng tên, nhưng tập hợp các đối số khác nhau), vì vậy anh ta sử dụng cùng một tên phương thức! Quá tải chỉ được phép trong Swift, không phải trong Objective-C. Đó là lý do tại sao giải pháp là loại bỏ biểu mẫu thừa kế UIViewControll (là lớp Objective-C) hoặc khai báo phương thức riêng tư. Cả hai giải pháp được giải thích chi tiết trong các câu trả lời khác ở đây.
Ronny Webers

Trên thực tế tôi đã sử dụng từ khóa riêng trước chức năng. like, private func PerformanceOperation (oper: Double -> Double) {} và private func PerformanceOperation (oper: (Double, Double) -> Double) {} Ở đây tôi đã đạt được phương thức nạp chồng với sự trợ giúp của PRIVATE. bởi vì tôi đã sử dụng cả hai trong số đó trong ViewControll.Swift. Tại sao trình biên dịch không nói bất kỳ Lỗi nào?
itag

5

Từ https://developer.apple.com/l Library / ios

Swift bây giờ phát hiện sự khác biệt giữa quá tải và ghi đè trong hệ thống loại Swift và hành vi hiệu quả được thấy qua thời gian chạy Objective-C.


2

Tôi đã gặp cùng một lỗi do có hai phương thức có cùng chữ ký Obj-C:

static func prepareForUpSyncing(obj : NSManagedObject!) -> Bool
static func prepareForUpSyncing(objs : [NSManagedObject]!) -> Bool

Tôi không muốn đánh dấu một trong số họ là @nonobjc do khả năng giải quyết hậu quả trong thời gian chạy. (Ai đó có thể sửa tôi nếu không có khả năng)

Đã giải quyết nó bằng cách sử dụng tính năng tên tham số bên ngoài của Swift (tôi đã đặt tên bên ngoài giống như tên cục bộ) thành phương thức thứ hai, điều này thay đổi hiệu quả chữ ký phương thức Obj-c:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
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.