Sự khác biệt giữa một tài liệu tham khảo yếu và một tài liệu tham khảo không có chủ đề là gì?


240

Swift có:

  • Tài liệu tham khảo mạnh mẽ
  • Tài liệu tham khảo yếu
  • Tài liệu tham khảo

Làm thế nào là một tài liệu tham khảo không có tên khác với một tài liệu tham khảo yếu?

Khi nào an toàn để sử dụng một tài liệu tham khảo không có tên?

Các tài liệu tham khảo không có tên có nguy cơ bảo mật như con trỏ lơ lửng trong C / C ++ không?


3
Bài viết rất hay trên andrewcbancroft.com/2015/05/08/ Ấn
Zeeshan

Kinh nghiệm của tôi là sử dụng unownedcho các lớp chúng tôi kiểm soát, cho các lớp của Apple, sử dụng weakvì chúng tôi không thể đảm bảo chắc chắn những gì nó làm
onmyway133

@NoorAli, hoặc "ownBy" là tài liệu tham khảo "không có chủ đích" thường chỉ đến chủ sở hữu.
Ian Ringrose

Câu trả lời:


361

Cả hai weakvà các unownedtham chiếu đều không tạo ra sự stronggiữ đối tượng được giới thiệu (hay còn gọi là chúng không tăng số lần giữ lại để ngăn ARC giải phóng đối tượng được giới thiệu).

Nhưng tại sao hai từ khóa? Sự khác biệt này có liên quan đến thực tế là Optionalcác loại được tích hợp sẵn trong ngôn ngữ Swift. Câu chuyện dài về chúng: các loại tùy chọn cung cấp sự an toàn cho bộ nhớ (điều này hoạt động rất tốt với các quy tắc xây dựng của Swift - vốn rất nghiêm ngặt để cung cấp lợi ích này).

Một weaktài liệu tham khảo cho phép các khả năng của nó để trở thành nil(điều này xảy ra tự động khi đối tượng tham chiếu được deallocated), do đó các loại tài sản của bạn phải là tùy chọn - vì vậy bạn, như một lập trình viên, có nghĩa vụ để kiểm tra nó trước khi bạn sử dụng nó (về cơ bản trình biên dịch buộc bạn, càng nhiều càng tốt, để viết mã an toàn).

Một unownedtài liệu tham khảo cho rằng nó sẽ không bao giờ trở thành niltrong suốt cuộc đời của nó. Một tham chiếu không được đặt tên phải được đặt trong quá trình khởi tạo - điều này có nghĩa là tham chiếu đó sẽ được xác định là loại không tùy chọn có thể được sử dụng một cách an toàn mà không cần kiểm tra. Nếu bằng cách nào đó, đối tượng được đề cập bị hủy bỏ, thì ứng dụng sẽ bị sập khi tham chiếu không có tên sẽ được sử dụng.

Từ các tài liệu của Apple :

Sử dụng một tham chiếu yếu bất cứ khi nào nó có giá trị để tham chiếu đó trở thành con số không tại một thời điểm nào đó trong suốt vòng đời của nó. Ngược lại, sử dụng một tham chiếu chưa được đặt tên khi bạn biết rằng tham chiếu đó sẽ không bao giờ là không khi nó đã được đặt trong quá trình khởi tạo.

Trong các tài liệu, có một số ví dụ thảo luận về các chu kỳ giữ lại và cách phá vỡ chúng. Tất cả những ví dụ này được trích xuất từ các tài liệu .

Ví dụ về weaktừ khóa:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

Và bây giờ, đối với một số nghệ thuật ASCII (bạn nên đi xem các tài liệu - chúng có sơ đồ đẹp):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

Các PersonApartmentví dụ chương trình một tình huống mà hai tài sản, cả hai đều được cho phép trở thành con số không, có khả năng gây ra một chu kỳ tài liệu tham khảo mạnh mẽ. Kịch bản này được giải quyết tốt nhất với một tài liệu tham khảo yếu. Cả hai thực thể có thể tồn tại mà không có sự phụ thuộc chặt chẽ vào nhau.

Ví dụ về unownedtừ khóa:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

Trong ví dụ này, a Customercó thể có hoặc không có a CreditCard, nhưng a CreditCard sẽ luôn được liên kết với a Customer. Để thể hiện điều này, Customerlớp có một thuộc tính tùy chọn card, nhưng CreditCardlớp có thuộc tính không tùy chọn (và không có tên) customer.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

Các CustomerCreditCardví dụ chương trình một tình huống mà một tài sản được phép trở thành con số không và một tài sản đó không thể bằng không có khả năng gây ra một chu kỳ tài liệu tham khảo mạnh mẽ. Kịch bản này được giải quyết tốt nhất với một tài liệu tham khảo chưa được đăng.

Lưu ý từ Apple:

Tham chiếu yếu phải được khai báo là biến, để chỉ ra rằng giá trị của chúng có thể thay đổi khi chạy. Một tham chiếu yếu không thể được khai báo là hằng số.

Ngoài ra còn có một kịch bản thứ ba khi cả hai thuộc tính phải luôn có một giá trị và không thuộc tính nào sẽ không có giá trị nào khi quá trình khởi tạo hoàn tất.

Và cũng có những kịch bản giữ chu kỳ cổ điển cần tránh khi làm việc với các bao đóng.

Đối với điều này, tôi khuyến khích bạn truy cập tài liệu của Apple hoặc đọc sách .


3
Điều này hơi tầm thường nhưng tôi thấy ví dụ về Căn hộ và Người hơi khó hiểu cũng là một giải pháp bổ sung để phá vỡ chu trình tham chiếu mạnh mẽ. Căn hộ của một người là tùy chọn và do đó có thể là con số không cũng như người thuê căn hộ là tùy chọn và do đó có thể là con số không nên cả hai thuộc tính đều có thể được xác định là yếu. `` `
Justin Levi Mùa đông

class Person {let name: String init (name: String) {self.name = name} yếu var căn hộ: Căn hộ? } class Căn hộ {let number: Int init (number: Int) {self.number = number} người thuê var yếu: Người? }
Justin Levi Mùa đông

3
Sự khác biệt giữa weak var Person?so với var Person??
Trưởng khoa

4
@JustinLevi, Nếu bạn khai báo cả hai thuộc tính là yếu, có khả năng chúng sẽ bị xử lý. Người giữ một tham chiếu mạnh mẽ đến Căn hộ để Căn hộ sẽ không bị hủy bỏ. Nếu căn hộ có cùng tham chiếu mạnh mẽ với Người, họ sẽ tạo ra một chu trình giữ lại - có thể bị lập trình viên phá vỡ khi chạy nếu anh ta biết về nó, nhưng nếu không thì đó chỉ là rò rỉ bộ nhớ. Đây là tất cả những ồn ào về mạnh mẽ, yếu đuối và vô danh: quản lý bộ nhớ ở cấp độ cao hơn, bởi vì ARC làm tất cả những thứ bẩn thỉu cho chúng ta. Tránh chu kỳ giữ lại là công việc của chúng tôi.
Ilea Cristian

1
Có phải lợi ích duy nhất của việc không có người yếu là bạn không cần phải mở khóa và có thể sử dụng hằng số? Có trường hợp nào bạn không thể sử dụng yếu và chỉ có thể sử dụng không có tên?
Alan

29

Q1. Làm thế nào là một tài liệu tham khảo không được đề cập khác biệt với một người khác

Tham khảo yếu:

Tham chiếu yếu là một tham chiếu không giữ vững đối tượng mà nó đề cập đến và do đó không ngăn ARC xử lý đối tượng được tham chiếu. Bởi vì các tham chiếu yếu được phép có không có giá trị, bạn phải khai báo mọi tham chiếu yếu là có một loại tùy chọn. (Tài liệu Apple)

Tài liệu tham khảo chưa được đăng:

Giống như các tài liệu tham khảo yếu, một tài liệu tham khảo không có chủ quyền không giữ vững mạnh trong trường hợp mà nó đề cập. Tuy nhiên, không giống như một tham chiếu yếu, một tham chiếu không được đặt tên được giả định là luôn có giá trị. Bởi vì điều này, một tham chiếu không có tên luôn được định nghĩa là một loại không tùy chọn. (Tài liệu Apple)

Khi nào nên sử dụng mỗi:

Sử dụng một tham chiếu yếu bất cứ khi nào nó hợp lệ để tham chiếu đó trở thành con số không tại một thời điểm nào đó trong suốt vòng đời của nó. Ngược lại, sử dụng một tham chiếu chưa được đặt tên khi bạn biết rằng tham chiếu đó sẽ không bao giờ là không khi nó đã được đặt trong quá trình khởi tạo. (Tài liệu Apple)


Quý 2 Khi nào thì an toàn khi sử dụng một tài liệu tham khảo chưa được đăng ký của người Viking?

Như đã trích dẫn ở trên, một tham chiếu không có tên được giả định là luôn có giá trị. Vì vậy, bạn chỉ nên sử dụng nó khi bạn chắc chắn rằng tài liệu tham khảo sẽ không bao giờ là con số không. Apple Docs minh họa một trường hợp sử dụng cho các tài liệu tham khảo không có tên thông qua ví dụ sau.

Giả sử chúng ta có hai lớp CustomerCreditCard . Một khách hàng có thể tồn tại mà không cần thẻ tín dụng, nhưng thẻ tín dụng sẽ không tồn tại nếu không có khách hàng, tức là có thể giả định rằng thẻ tín dụng sẽ luôn có khách hàng. Vì vậy, họ nên có mối quan hệ sau:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

H3 Là những người tham gia không được đề cập đến Tham khảo các mối nguy hiểm về bảo mật như con trỏ dangling hung dữ trong C / C ++

Tôi không nghĩ vậy.

Vì các tham chiếu không có tên chỉ là các tham chiếu yếu được đảm bảo có giá trị, nên không có rủi ro bảo mật theo bất kỳ cách nào. Tuy nhiên, nếu bạn cố gắng truy cập một tham chiếu chưa được đặt tên sau khi đối tượng mà tham chiếu của nó bị hủy, bạn sẽ gây ra lỗi thời gian chạy và ứng dụng sẽ bị sập.

Đó là rủi ro duy nhất tôi thấy với nó.

Liên kết với Apple Docs


Chương trình ví dụ Q2 của bạn đơn giản để hiểu về unown..cảm ơn .. bạn có thể thêm cùng loại ví dụ cho yếu & mạnh không ..
Ranjith Kumar

Thông minh. Cảm ơn bạn.
Swifty McSwifterton

Bạn có thể bao gồm một ví dụ phổ biến cho người không có danh tiếng hoặc yếu?
Mật ong

Hãy xem xét các đối tượng cha mẹ và con cái, nếu con không thể tồn tại mà không có cha mẹ thì sử dụng unownedcho tài sản của cha mẹ trong lớp con. yếu là ngược lại. Giải thích tốt đẹp @myxtic! unownedtài liệu tham khảo chỉ là weaktài liệu tham khảo được đảm bảo có giá trị!
Saif

26

Nếu bản thân có thể là con số không, hãy sử dụng [bản thân yếu] .

Nếu bản thân sẽ không bao giờ là con số không trong việc đóng cửa, hãy sử dụng [bản thân không có chủ đích] .

Nếu nó bị sập khi bạn sử dụng [bản thân không có chủ đích] thì bản thân có lẽ sẽ không ở một thời điểm nào đó trong lần đóng cửa đó và có lẽ bạn cần phải sử dụng [bản thân yếu] .

Kiểm tra các ví dụ về việc sử dụng mạnh , yếukhông có dấu trong các bao đóng:

https://developer.apple.com/l Library / ios / document / swift / conithual / swift_programming_lingu / AnatomaticReferenceCounting.html


7
Tại sao không chỉ sử dụng yếu ngay cả khi bản thân không bao giờ có thể là con số không, không có hại gì phải không?
Boon

4
chào @Boon - đó thực sự là câu hỏi quan trọng.
Fattie

[tự yếu] => Nếu tôi sử dụng bao đóng trong viewDidLoad (), làm thế nào có thể selfđược không?
Hassan Tareq

@HassanTareq, tôi nghĩ rằng một vài ví dụ hay được đề cập trong bài báo, được đề cập ở trên. Kiểm tra phần "Giải quyết chu kỳ tham chiếu mạnh cho đóng cửa", đặc biệt. Trích dẫn: "Swift yêu cầu bạn viết self.someProperty hoặc self.someMethod () (thay vì chỉ một sốProperty hoặc someMethod ()) bất cứ khi nào bạn đề cập đến một thành viên của chính mình trong một bao đóng. Điều này giúp bạn nhớ rằng có thể tự chụp Tai nạn." Trích từ: Apple Inc., Ngôn ngữ lập trình Swift (Swift 4). iBooks. itunes.apple.com/de/book/the-swift-programming-lingu-swift-4/ mẹo "
Nick Entin

1
@Boon Nếu bạn luôn sử dụng yếu, trình biên dịch sẽ buộc phải kiểm tra tùy chọn trước khi sử dụng. Trong trường hợp bạn không đặt kiểm tra đó, nó sẽ đưa ra lỗi thời gian biên dịch. Không có tác hại nào khác.
Vikas Mishra

5

Chiết xuất từ liên kết

Ít điểm kết luận

  • Để xác định xem bạn thậm chí có cần phải lo lắng về việc mạnh, yếu hay không được đặt tên hay không, hãy hỏi, tôi đang xử lý các loại tham chiếu. Nếu bạn đang làm việc với Structs hoặc Enums, ARC không quản lý bộ nhớ cho các Loại đó và bạn thậm chí không cần phải lo lắng về việc chỉ định yếu hoặc không được đặt tên cho các hằng hoặc biến đó.
  • Tài liệu tham khảo mạnh mẽ là tốt trong các mối quan hệ phân cấp trong đó cha mẹ tham khảo đứa trẻ, nhưng không phải ngược lại. Trong thực tế, các tài liệu tham khảo mạnh mẽ là loại tài liệu tham khảo phù hợp nhất hầu hết thời gian.
  • Khi hai trường hợp có liên quan tùy ý với nhau, hãy đảm bảo rằng một trong các trường hợp đó có tham chiếu yếu đến trường hợp kia.
  • Khi hai trường hợp có liên quan theo cách mà một trong các thể hiện không thể tồn tại mà không có trường hợp khác, thì thể hiện với sự phụ thuộc bắt buộc cần phải giữ một tham chiếu chưa được đặt tên cho trường hợp khác.

1

Cả hai weakunownedtham chiếu sẽ không ảnh hưởng đến số tham chiếu của đối tượng. Nhưng tham chiếu yếu sẽ luôn là tùy chọn, tức là có thể không, trong khiunowned tham chiếu không bao giờ là không nên chúng sẽ không bao giờ là tùy chọn. Khi sử dụng một tham chiếu tùy chọn, bạn sẽ luôn phải xử lý khả năng đối tượng không. Trong trường hợp một tài liệu tham khảo không có chủ đề, bạn sẽ phải đảm bảo rằng đối tượng không bao giờ là con số không. Sử dụng một tham chiếu không được đặt tên cho một đối tượng con số không sẽ tương tự như việc hủy bỏ một tùy chọn không có giá trị.

Điều đó nói rằng an toàn khi sử dụng một tham chiếu không có tên trong đó bạn chắc chắn rằng thời gian tồn tại của đối tượng nhiều hơn tham chiếu. Nếu đó không phải là trường hợp tốt hơn là sử dụng một tài liệu tham khảo yếu thay thế.

Đối với phần thứ ba của câu hỏi, tôi không nghĩ rằng tài liệu tham khảo không có tên tương tự như một con trỏ lơ lửng. Khi chúng ta nói về số tham chiếu, chúng ta thường đề cập đến số tham chiếu mạnh của đối tượng. Tương tự swift duy trì số tham chiếu chưa được đặt tên và số tham chiếu yếu cho đối tượng (điểm tham chiếu yếu đến một thứ gọi là "bảng phụ" chứ không phải chính đối tượng). Khi số tham chiếu mạnh đạt đến 0, đối tượng sẽ được khử cấp, nhưng nó không thể được giải phóng nếu số tham chiếu không có giá trị lớn hơn 0.

Bây giờ một con trỏ lơ lửng là một cái gì đó trỏ đến một vị trí bộ nhớ đã được giải phóng. Nhưng trong swift vì bộ nhớ chỉ có thể được giải phóng miễn là có một tham chiếu không xác định đến đối tượng, nó không thể gây ra một con trỏ lơ lửng.

Có rất nhiều bài viết thảo luận về quản lý bộ nhớ nhanh chi tiết hơn. Đây là một.


0

Tham chiếu không có tên là một loại tham chiếu yếu được sử dụng trong trường hợp có mối quan hệ trọn đời giữa hai đối tượng, khi một đối tượng chỉ nên được sở hữu bởi một đối tượng khác. Đó là một cách để tạo ra một liên kết bất biến giữa một đối tượng và một trong các thuộc tính của nó.

Trong ví dụ được đưa ra trong video WWDC swift trung gian, một người sở hữu thẻ tín dụng và thẻ tín dụng chỉ có thể có một chủ sở hữu. Trên thẻ tín dụng, người đó không nên là một tài sản tùy chọn, vì bạn không muốn có một thẻ tín dụng nổi xung quanh chỉ có một chủ sở hữu. Bạn có thể phá vỡ chu trình này bằng cách làm cho tài sản chủ sở hữu trên tín dụng trở thành một tham chiếu yếu, nhưng điều đó cũng đòi hỏi bạn phải làm cho nó tùy chọn cũng như biến số (trái ngược với hằng số). Tham chiếu không có tên trong trường hợp này có nghĩa là mặc dù CreditCard không có cổ phần sở hữu trong một Người, cuộc sống của nó phụ thuộc vào nó.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}

liên kết đến video wwdc hoặc tiêu đề?
Osa

-2

Sử dụng unownedkhi bạn chắc chắn selfkhông bao giờ có thể nilở điểm bạn truy cập selftại thời điểm đó.

Ví dụ (tất nhiên bạn có thể thêm mục tiêu trực tiếp từ MyViewController, nhưng một lần nữa, đó là một ví dụ đơn giản).:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Sử dụng weakkhi có khả năng selfcó thể là niltại điểm bạn truy cập self.

Thí dụ:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Nhược điểm của unowned:

  • Hiệu quả hơn yếu
  • Bạn có thể (tốt, bạn bị buộc) đánh dấu trường hợp là bất biến (không còn nữa kể từ Swift 5.0).
  • Chỉ ra cho người đọc mã của bạn: Trường hợp này có mối quan hệ với X và nó không thể sống mà không có nó, nhưng nếu X biến mất, tôi cũng sẽ biến mất.

Nhược điểm của weak:

  • An toàn hơn so với không có tên (vì nó không thể sụp đổ).
  • Có thể tạo mối quan hệ với X đi theo cả hai cách, nhưng cả hai có thể sống mà không cần nhau.

Nếu bạn không chắc chắn, hãy sử dụng weak. Đợi đã , ý tôi là hỏi ở đây trên StackOverflow bạn nên làm gì trong trường hợp của bạn! Sử dụng yếu mọi lúc khi bạn không nên chỉ gây nhầm lẫn cho bạn và người đọc mã của bạn.

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.