Làm cách nào tôi có thể tạo tham chiếu giao thức yếu trong Swift 'thuần' (không có @objc)


561

weakcác tham chiếu dường như không hoạt động trong Swift trừ khi protocolđược khai báo là @objc, điều mà tôi không muốn trong một ứng dụng Swift thuần túy.

Mã này đưa ra lỗi biên dịch ( weakkhông thể áp dụng cho loại không phải lớp MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Tôi cần tiền tố giao thức với @objc, sau đó nó hoạt động.

Câu hỏi: Cách Swift "thuần túy" để hoàn thành a là weak delegategì?


Câu trả lời:


1038

Bạn cần khai báo loại giao thức là AnyObject.

protocol ProtocolNameDelegate: AnyObject {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Sử dụng AnyObjectbạn nói rằng chỉ các lớp có thể phù hợp với giao thức này, trong khi các cấu trúc hoặc enum không thể.


25
Vấn đề của tôi với các giải pháp này là việc gọi đại biểu gây ra sự cố - EXC_BAD_ACCESS (như được lưu ý bởi những người khác ở nơi khác). Đây có vẻ là lỗi. Giải pháp duy nhất tôi tìm thấy là sử dụng @objc và loại bỏ tất cả các loại dữ liệu Swift khỏi giao thức.
Jim T

12
Cách chính xác để làm các đại biểu yếu trong Swift bây giờ là gì? Tài liệu của Apple không hiển thị hoặc tuyên bố đại biểu là yếu trong mã ví dụ của họ: developer.apple.com/l
Library / ios / document / swift / conceptionual / trên

2
Điều này không phải lúc nào cũng an toàn - hãy nhớ rằng bạn chỉ cần làm cho đại biểu yếu đi nếu nó cũng giữ một tham chiếu đến đại biểu & bạn cần phá vỡ chu trình tham chiếu mạnh mẽ đó. Nếu đại biểu không có tham chiếu đến đại biểu, đại biểu có thể ra khỏi phạm vi (vì nó yếu) và bạn sẽ gặp sự cố và các vấn đề khác: / cần lưu ý điều gì đó.
Trev14

5
BTW: Tôi nghĩ rằng "phong cách mới" (Swift 5) là để làm protocol ProtocolNameDelegate: AnyObject, nhưng không thành vấn đề.
hnh

1
Nó sẽ bị AnyObjecttừ classchối vào một lúc nào đó.
Jose

283

Bổ sung trả lời

Tôi luôn bối rối về việc liệu các đại biểu có nên yếu đi hay không. Gần đây tôi đã tìm hiểu thêm về các đại biểu và khi nào nên sử dụng các tài liệu tham khảo yếu, vì vậy hãy để tôi thêm một số điểm bổ sung ở đây vì lợi ích của người xem trong tương lai.

  • Mục đích của việc sử dụng weaktừ khóa là để tránh các chu kỳ tham chiếu mạnh (giữ lại các chu kỳ). Chu kỳ tham chiếu mạnh xảy ra khi hai thể hiện lớp có tham chiếu mạnh với nhau. Số tham chiếu của họ không bao giờ giảm về 0 nên họ không bao giờ bị xử lý.

  • Bạn chỉ cần sử dụng weaknếu đại biểu là một lớp. Các cấu trúc và enum Swift là các loại giá trị (giá trị của chúng được sao chép khi tạo một thể hiện mới), không phải các loại tham chiếu, vì vậy chúng không tạo ra các chu kỳ tham chiếu mạnh .

  • weaktài liệu tham khảo luôn là tùy chọn (nếu không bạn sẽ sử dụng unowned) và luôn luôn sử dụng var(không let) để tùy chọn có thể được đặt thành nilkhi nó được giải phóng.

  • Một lớp cha nên tự nhiên có một tham chiếu mạnh đến các lớp con của nó và do đó không sử dụng weaktừ khóa. Tuy nhiên, khi một đứa trẻ muốn tham chiếu đến cha mẹ của nó, nó sẽ làm cho nó trở thành một tài liệu tham khảo yếu bằng cách sử dụng weaktừ khóa.

  • weaknên được sử dụng khi bạn muốn tham chiếu đến một lớp học mà bạn không sở hữu, không chỉ cho một đứa trẻ tham khảo cha mẹ của nó. Khi hai lớp không phân cấp cần tham chiếu lẫn nhau, chọn một lớp yếu. Người bạn chọn phụ thuộc vào tình huống. Xem câu trả lời cho câu hỏi này để biết thêm về điều này.

  • Theo nguyên tắc chung, các đại biểu nên được đánh dấu làweak vì hầu hết các đại biểu đang tham chiếu các lớp mà họ không sở hữu. Điều này hoàn toàn đúng khi một đứa trẻ đang sử dụng một đại biểu để giao tiếp với cha mẹ. Sử dụng một tài liệu tham khảo yếu cho đại biểu là những gì tài liệu đề xuất. (Nhưng cũng thấy điều này .)

  • Các giao thức có thể được sử dụng cho cả loại tham chiếu (lớp) và loại giá trị (structs, enums). Vì vậy, trong trường hợp có khả năng bạn cần làm cho một đại biểu yếu đi, bạn phải biến nó thành một giao thức chỉ dành cho đối tượng. Cách để làm điều đó là thêm AnyObjectvào danh sách kế thừa của giao thức. (Trước đây bạn đã làm điều này bằng cách sử dụng classtừ khóa, nhưng AnyObjectbây giờ được ưa thích hơn .)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

Học cao hơn

Đọc các bài viết sau đây là những gì giúp tôi hiểu điều này tốt hơn nhiều. Họ cũng thảo luận về các vấn đề liên quan như unownedtừ khóa và các chu kỳ tham chiếu mạnh mẽ xảy ra với việc đóng cửa.

Liên quan


5
Đây là tất cả tốt đẹp và thú vị, nhưng không thực sự liên quan đến câu hỏi ban đầu của tôi - đó không phải là về bản thân yếu / ARC cũng như lý do tại sao các đại biểu thường yếu. Chúng tôi đã biết về tất cả những điều đó và chỉ tự hỏi làm thế nào bạn có thể khai báo một tham chiếu giao thức yếu (được trả lời hoàn toàn tốt bởi @flainez).
hnh

30
Bạn đúng. Tôi thực sự đã có cùng một câu hỏi như bạn trước đó, nhưng tôi đã thiếu rất nhiều thông tin cơ bản này. Tôi đã đọc ở trên và thực hiện các ghi chú bổ sung để giúp bản thân hiểu tất cả các vấn đề liên quan đến câu hỏi của bạn. Bây giờ tôi nghĩ rằng tôi có thể áp dụng câu trả lời được chấp nhận của bạn và biết lý do tại sao tôi làm điều đó. Tôi hy vọng có thể nó sẽ giúp người xem trong tương lai là tốt.
Suragch

5
Nhưng tôi có thể có một giao thức yếu KHÔNG phụ thuộc vào loại không? Một giao thức tự nó không quan tâm đối tượng nào phù hợp với chính nó. Vì vậy, cả một lớp hoặc một cấu trúc có thể phù hợp với nó. Có thể vẫn có lợi ích của cả hai việc có thể tuân thủ nó, nhưng chỉ có các loại lớp phù hợp là yếu?
FlowUI. SimpleUITesting.com 6/07/2016

> bởi vì hầu hết các đại biểu đang tham chiếu các lớp mà họ không sở hữu, tôi sẽ viết lại đây là: hầu hết các đại biểu. Nếu không, đối tượng không thuộc sở hữu sẽ trở thành chủ sở hữu
Victor Jalencas

36

AnyObject là cách chính thức để sử dụng tham chiếu yếu trong Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

Từ Apple:

Để ngăn chặn chu kỳ tham chiếu mạnh, các đại biểu nên được khai báo là tham chiếu yếu. Để biết thêm thông tin về các tài liệu tham khảo yếu, hãy xem Chu kỳ tham chiếu mạnh giữa các trường hợp lớp. Đánh dấu giao thức là chỉ lớp sau này sẽ cho phép bạn tuyên bố rằng đại biểu phải sử dụng tham chiếu yếu. Bạn đánh dấu một giao thức là chỉ dành cho lớp bằng cách kế thừa từ AnyObject , như được thảo luận trong Giao thức chỉ dành cho lớp.

https://developer.apple.com/l Library / content / document / Swift / Conception


7
Hấp dẫn. Có classbị phản đối trong Swift 4.1 không?
hnh

@hnh Bạn vẫn có thể tạo "giao thức giả" bằng cách biến nó thành một lớp, nhưng giao thức: AnyObject thực hiện chính xác những gì OP yêu cầu với ít tác dụng phụ hơn là biến nó thành một lớp. (bạn vẫn không thể sử dụng một giao thức như vậy với các loại giá trị, nhưng khai báo nó là một lớp sẽ không giải quyết được điều đó)
Arru

8

Cập nhật: Có vẻ như hướng dẫn đã được cập nhật và ví dụ tôi đang đề cập đã bị xóa. Xem chỉnh sửa câu trả lời của @ flainez ở trên.

Bản gốc: Sử dụng @objc là cách phù hợp để thực hiện ngay cả khi bạn không tương tác với Obj-C. Nó đảm bảo rằng giao thức của bạn đang được áp dụng cho một lớp chứ không phải enum hay struct. Xem "Kiểm tra sự phù hợp giao thức" trong hướng dẫn.


Như đã đề cập đây là IMO không phải là một câu trả lời cho câu hỏi. Một chương trình Swift đơn giản sẽ có thể tự đứng vững, không bị ràng buộc với NS'ism (điều này có thể ngụ ý không sử dụng nữa của đại biểu mà là một số cấu trúc thiết kế khác). Swift MyClass thuần túy của tôi thực sự không quan tâm liệu đích đến là cấu trúc hay đối tượng, tôi cũng không cần tùy chọn. Có lẽ họ sẽ sửa nó sau, đó là một ngôn ngữ mới. Có thể một cái gì đó như 'giao thức lớp XYZ' nếu cần ngữ nghĩa tham chiếu?
hnh

4
Tôi nghĩ cũng đáng lưu ý rằng \ @objc có thêm tác dụng phụ - đề xuất NSObjectProtocol của @eXhausty tốt hơn một chút. Với \ @objc - nếu đại biểu lớp nhận một đối số đối tượng, như 'handleResult (r: MySwiftResultClass)', MySwiftResultClass bây giờ cần kế thừa từ NSObject! Và có lẽ nó không còn là không gian tên nữa, v.v. Tóm lại: \ @objc là một tính năng bắc cầu, không phải là ngôn ngữ.
hnh

Tôi nghĩ rằng họ đã giải quyết điều này. Bây giờ bạn viết: giao thức MyClassDelegate: class {}
user3675131

Tài liệu về vấn đề này ở đâu? Tôi bị mù hoặc làm điều gì đó sai, vì tôi không thể tìm thấy bất kỳ thông tin nào về việc này ... O_O
BastiBen

Tôi không chắc liệu nó có trả lời câu hỏi của OP hay không, nhưng điều này rất hữu ích đặc biệt nếu bạn đang tương tác với Objc-C;)
Dan Rosenstark

-1

giao thức phải là lớp con của AnyObject, lớp

ví dụ dưới đây

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

-9

Apple sử dụng "NSObjectProtocol" thay vì "class".

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

Điều này cũng làm việc cho tôi và loại bỏ các lỗi tôi đã thấy khi cố gắng thực hiện mẫu đại biểu của riêng tôi.


5
Không liên quan đến câu hỏi, câu hỏi này là về việc xây dựng một lớp Swift thuần túy (cụ thể là không có NSObject) hỗ trợ một đối tượng ủy nhiệm. Đây không phải là về việc thực hiện các giao thức Objective-C, đó là những gì bạn đang làm. Cái sau đòi hỏi @objc aka NSObjectProtocol.
hnh

OK, nhưng không được khuyến khích.
DawnSong
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.