Làm thế nào để xử lý chính xác điểm yếu trong khối Swift với các đối số


151

Theo tôi TextViewTableViewCell, tôi có một biến để theo dõi một khối và một phương thức cấu hình trong đó khối được truyền vào và được chỉ định.
Đây là TextViewTableViewCelllớp học của tôi :

//
//  TextViewTableViewCell.swift
//

import UIKit

class TextViewTableViewCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet var textView : UITextView

    var onTextViewEditClosure : ((text : String) -> Void)?

    func configure(#text: String?, onTextEdit : ((text : String) -> Void)) {
        onTextViewEditClosure = onTextEdit
        textView.delegate = self
        textView.text = text
    }

    // #pragma mark - Text View Delegate

    func textViewDidEndEditing(textView: UITextView!) {
        if onTextViewEditClosure {
            onTextViewEditClosure!(text: textView.text)
        }
    }
}

Khi tôi sử dụng phương thức configure trong phương thức của mình cellForRowAtIndexPath, làm thế nào để tôi sử dụng đúng bản thân yếu trong khối mà tôi truyền vào.
Đây là những gì tôi có mà không có bản thân yếu:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {(text: String) in
   // THIS SELF NEEDS TO BE WEAK  
   self.body = text
})
cell = bodyCell

CẬP NHẬT : Tôi có những điều sau đây để làm việc bằng cách sử dụng [weak self]:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {[weak self] (text: String) in
        if let strongSelf = self {
             strongSelf.body = text
        }
})
cell = myCell

Khi tôi làm [unowned self]thay vì [weak self]và đưa ra iftuyên bố, ứng dụng gặp sự cố. Bất kỳ ý tưởng về cách này nên làm việc với [unowned self]?


Bạn có thể chọn một câu trả lời dưới đây là câu trả lời đúng không? Cũng lưu ý rằng với người không có danh tiếng, bạn sẽ không cần phải củng cố bản thân trong phạm vi đóng cửa của mình. Không có tên là tốt hơn yếu ở đây vì vòng đời của tế bào và bộ điều khiển xem của bạn được liên kết.
ikuramedia

1
Tôi nhận ra rằng [không có bản thân] nếu tùy chọn tốt hơn, nhưng ứng dụng của tôi gặp sự cố khi tôi sử dụng nó. Rất thích xem một mẫu mã sử dụng nó để đóng câu trả lời.
NatashaTheRobot

1
Từ các tài liệu: "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ủ đề không giữ vững đối tượng mà nó đề cập đến. Tuy nhiên, không giống như một tài liệu tham khảo yếu được cho là luôn có giá trị." Nếu ứng dụng của bạn gặp sự cố, thì đó là có khả năng bởi vì không có tên đang được áp dụng cho một giá trị không trong thời gian chạy.
Bill Patterson

Có lẽ tốt hơn để quảng cáo một tuyên bố bảo vệ ở đây hơn là nếu để ràng buộc với mạnh mẽ. Chỉ cần nói, đây giống như một ứng cử viên hoàn hảo :-D
Daniel Galasko

@NatashaTheRobot, Cú pháp nào là [bản thân yếu] ?. trông giống như một thông điệp truyền vào mục tiêu C. Bạn có thể vui lòng thêm một chút về cú pháp trong câu hỏi không.
Vignesh

Câu trả lời:


178

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] tôi sẽ đoán rằng bản thân mình không ở một thời điểm nào đó trong lần đóng cửa đó, đó là lý do tại sao bạn phải đi với [bản thân yếu đuối] .

Tôi thực sự thích toàn bộ phần từ hướng dẫn sử dụng mạnh , yếukhông có tên trong các lần đóng cửa:

https://developer.apple.com/l Library / content / document / Swift / Contualual / Swift_Programming_Lingu / AutomaticReferenceCounting.html

Lưu ý: Tôi đã sử dụng thuật ngữ đóng thay vì chặn là thuật ngữ Swift mới hơn:

Sự khác biệt giữa khối (Mục tiêu C) và đóng (Swift) trong ios


7
Apple đã gọi các khối "đóng cửa" trong tài liệu đầu tiên của họ để mở rộng ngôn ngữ C. (Khối hoặc đóng là một phần mở rộng của C ngay từ đầu. Chỉ có MM có liên quan đến Mục tiêu-C.) Ngay cả tôi cũng thích thuật ngữ "đóng", vì "khối" trong C rất thường xuyên liên quan đến câu lệnh ghép là một loại sai trong cả hai ngôn ngữ, bởi vì nó được gọi là bao đóng ngay cả khi nó không đóng trên một đối tượng (biến hoặc hằng).
Amin Negm-Awad

1
đã trả lời rất hay :)
iDevAmit

1
Tôi sẽ đề nghị không bao giờ sử dụng unowned. Nó không đáng để mạo hiểm khiến ứng dụng của bạn bị sập.
Kyle Redfearn

32

Đặt [unowned self]trước (text: String)...trong đóng cửa của bạn. Đây được gọi là danh sách chụp và đặt hướng dẫn sở hữu trên các biểu tượng được chụp trong bao đóng.


2
Cảm ơn vì đã đặt tên cho nó, tôi muốn biết điều đó!
rob5408

3
Tôi không nghĩ câu trả lời này hữu ích. [tự ngã] sẽ sụp đổ nếu bản thân trở thành con số không trong khi thực hiện đóng cửa
Yunus Nedim Mehel

3
hoàn toàn không có lý do để sử dụng không có chủ, trừ (1) trong cực kỳ tình huống bất thường, để thực hiện (điều này là hoàn toàn không thích hợp ở đây và ở 99,999% của chương trình) và (2) như một vấn đề phong cách thực thi. Câu nói "Bạn nên luôn luôn sử dụng yếu, không bao giờ bỏ qua" là rất hợp lý.
Fattie

29

** EDITED cho Swift 4.2:

Như @Koen đã nhận xét, swift 4.2 cho phép:

guard let self = self else {
   return // Could not get a strong reference for self :`(
}

// Now self is a strong reference
self.doSomething()

Tái bút: Vì tôi đang có một số phiếu bầu, tôi muốn khuyên bạn nên đọc về việc thoát khỏi việc đóng cửa .

EDITED: Như @ tim-vermeulen đã nhận xét, Chris Lattner cho biết vào Thứ Sáu ngày 22 tháng 1 19:51:29 CST 2016, thủ thuật này không nên được sử dụng cho bản thân, vì vậy vui lòng không sử dụng nó. Kiểm tra thông tin đóng cửa không thoát và câu trả lời danh sách chụp từ @gbk. **

Đối với những người sử dụng [bản thân yếu] trong danh sách chụp, lưu ý rằng bản thân có thể không, vì vậy điều đầu tiên tôi làm là kiểm tra xem có tuyên bố bảo vệ không

guard let `self` = self else {
   return
}
self.doSomething()

Nếu bạn đang tự hỏi những gì dấu ngoặc kép xung quanh selflà một thủ thuật chuyên nghiệp để sử dụng bản thân bên trong bao đóng mà không cần phải thay đổi tên thành cái này , yếu đuối hoặc bất cứ điều gì.



2
Tôi có xu hướng gọi "bản thân" "mạnh mẽ" cục bộ để đảm bảo rằng nó không bị nhầm lẫn với bản thân mặc định và dễ dàng nhận ra liệu bạn có bảo vệ cho một tham chiếu bản thân mạnh mẽ hay không.
Justin Stanley

1
Điều này không nên được sử dụng, vì nó là một lỗi biên dịch: list.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/ Lỗi
Tim Vermeulen

1
Tôi nghĩ rằng nhận xét của Chris Lattner trong liên kết ở trên chỉ là về việc không đặt tên biến là self(trong backticks). Đặt tên cho nó một cái gì đó khác như nonOptionalSelf và nó sẽ ổn.
OutOnAWeekend

1
Ngày nay (swift 4.2) { [weak self] in guard let self = self else { return }có thể được sử dụng mà không cần backticks và thực sự được hỗ trợ: github.com/apple/swift-evolution/blob/master/proposeals/ Lỗi
Koen.

26

Sử dụng danh sách Capture

Xác định danh sách chụp

Mỗi mục trong danh sách chụp là một cặp từ khóa yếu hoặc không có tên với tham chiếu đến một thể hiện của lớp (chẳng hạn như tự) hoặc một biến được khởi tạo với một số giá trị (chẳng hạn như ủy nhiệm = self.delegate!). Các cặp này được viết trong một cặp dấu ngoặc vuông, cách nhau bằng dấu phẩy.

Đặt danh sách chụp trước danh sách tham số của kiểu đóng và kiểu trả về nếu chúng được cung cấp:

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here 
} 

Nếu một bao đóng không chỉ định một danh sách tham số hoặc kiểu trả về vì chúng có thể được suy ra từ ngữ cảnh, hãy đặt danh sách chụp vào lúc bắt đầu đóng, theo sau là từ khóa in:

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}

giải thích thêm


3
Bạn đã sử dụng chưa được đặt tên cho "bản thân", điều đó có nghĩa là bạn biết chắc chắn "bản thân" sẽ không bị ảnh hưởng khi bạn truy cập nó. Sau đó, bạn đã sử dụng lực lượng unrap trên "self.delegate" (điều đó cũng có nghĩa là bạn biết chắc chắn rằng nó sẽ không còn nữa) để gán nó cho một var yếu. Nếu bạn biết chắc chắn rằng "self.delegate" sẽ không bằng không, tại sao không sử dụng không có tên trên "đại biểu" thay vì yếu?
Roni Leshes

26

EDIT: Tham chiếu đến một giải pháp cập nhật của LightMan

Xem giải pháp của LightMan . Cho đến bây giờ tôi đang sử dụng:

input.action = { [weak self] value in
    guard let this = self else { return }
    this.someCall(value) // 'this' isn't nil
}

Hoặc là:

input.action = { [weak self] value in
    self?.someCall(value) // call is done if self isn't nil
}

Thông thường bạn không cần chỉ định loại tham số nếu nó được suy ra.

Bạn có thể bỏ qua tham số hoàn toàn nếu không có hoặc nếu bạn tham chiếu nó như $0trong bao đóng:

input.action = { [weak self] in
    self?.someCall($0) // call is done if self isn't nil
}

Chỉ cần cho đầy đủ; nếu bạn chuyển qua bao đóng cho một hàm và tham số thì không @escaping, bạn không cần weak self:

[1,2,3,4,5].forEach { self.someCall($0) }

9

Kể từ ngày 4.2, chúng ta có thể làm:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()

Những người khác có giải pháp tương tự, nhưng "cái này" là C ++ IMHO. "StrongSelf" là một quy ước của Apple và bất cứ ai nhìn vào mã của bạn sẽ biết chuyện gì đang xảy ra.
David H

1
@ David H IMO cụm từ strongSelfgiải thích rõ ràng các biến có nghĩa / phụ là tốt nếu mã có tính chất dài hơn. mặc dù đánh giá cao ý kiến ​​của bạn, không biết c ++ đã sử dụng cụm từ như vậy.
eonist

3
Kể từ Swift 4.2, bạn có thể sử dụng guard let self = self else { return }để mở khóa [weak self]: github.com/apple/swift-evolution/blob/master/proposeals/
mẹo

@AmerHukic 👌.
eonist


3

Bạn có thể sử dụng [bản thân yếu] hoặc [bản thân không có chủ đích] trong danh sách chụp trước các thông số của khối. Danh sách chụp là cú pháp tùy chọn.

[unowned self]hoạt động tốt ở đây vì tế bào sẽ không bao giờ là con số không. Nếu không bạn có thể sử dụng[weak self]


1
tế bào không phải là chính mình, anh ta không thuộc lớp tế bào, anh ta có lẽ đang ở trong một trình điều khiển chế độ xem ...
Juan Boero

0

Nếu bạn gặp nạn hơn có lẽ bạn cần [bản thân yếu]

Tôi đoán là khối bạn đang tạo ra bằng cách nào đó vẫn có dây.

Tạo một PrepForReuse và thử xóa khối onTextViewEditClenses bên trong đó.

func prepareForResuse() {
   onTextViewEditClosure = nil
   textView.delegate = nil
}

Xem nếu điều đó ngăn chặn sự cố. (Đó chỉ là một phỏng đoán).


0

Đóng cửa và chu trình tham chiếu mạnh mẽ [Giới thiệu]

Như bạn đã biết đóng cửa của Swift có thể nắm bắt được thể hiện. Nó có nghĩa là bạn có thể sử dụng selfbên trong một đóng cửa. Đặc biệt là escaping closure[Giới thiệu] có thể tạo ra một strong reference cyclecái. Bằng cách này, bạn phải sử dụng rõ ràng selfbên trong escaping closure.

Đóng Swift có Capture Listtính năng cho phép bạn tránh tình huống như vậy và phá vỡ một chu trình tham chiếu vì không có tham chiếu mạnh đến trường hợp bị bắt. Phần tử danh sách Capture là một cặp weak/ unownedvà một tham chiếu đến lớp hoặc biến.

Ví dụ

class A {
    private var completionHandler: (() -> Void)!
    private var completionHandler2: ((String) -> Bool)!

    func nonescapingClosure(completionHandler: () -> Void) {
        print("Hello World")
    }

    func escapingClosure(completionHandler: @escaping () -> Void) {
        self.completionHandler = completionHandler
    }

    func escapingClosureWithPArameter(completionHandler: @escaping (String) -> Bool) {
        self.completionHandler2 = completionHandler
    }
}

class B {
    var variable = "Var"

    func foo() {
        let a = A()

        //nonescapingClosure
        a.nonescapingClosure {
            variable = "nonescapingClosure"
        }

        //escapingClosure
        //strong reference cycle
        a.escapingClosure {
            self.variable = "escapingClosure"
        }

        //Capture List - [weak self]
        a.escapingClosure {[weak self] in
            self?.variable = "escapingClosure"
        }

        //Capture List - [unowned self]
        a.escapingClosure {[unowned self] in
            self.variable = "escapingClosure"
        }

        //escapingClosureWithPArameter
        a.escapingClosureWithPArameter { [weak self] (str) -> Bool in
            self?.variable = "escapingClosureWithPArameter"
            return true
        }
    }
}
  • weak- thích hợp hơn, sử dụng nó khi có thể
  • unowned - sử dụng nó khi bạn chắc chắn rằng thời gian tồn tại của chủ sở hữu cá thể lớn hơn thời gian đóng
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.