인수를 사용하여 스위프트 블록에서 약한 자체를 올바르게 처리하는 방법


151

내에서는 TextViewTableViewCell블록을 추적하는 변수와 블록이 전달되고 할당되는 구성 방법이 있습니다.
여기 내 TextViewTableViewCell수업이 있습니다.

//
//  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)
        }
    }
}

내 메소드에서 configure 메소드를 사용할 때 cellForRowAtIndexPath전달하는 블록에서 weak self를 올바르게 사용하는 방법은
다음과 같습니다.

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

업데이트 : 사용하여 다음을 얻었습니다 [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

내가 할 때 [unowned self]대신 [weak self]하고 꺼내 if문을 응용 프로그램이 충돌합니다. 이것이 어떻게 작동하는지에 대한 아이디어가 [unowned self]있습니까?


그렇다면 정답으로 아래 답변을 선택할 수 있습니까? 또한 소유하지 않은 경우 폐쇄 내에서 자기를 강화할 필요가 없습니다. 셀과 뷰 컨트롤러의 수명주기가 연결되어 있기 때문에 소유하지 않은 것이 약한 것보다 낫습니다.
ikuramedia

1
더 나은 옵션이라면 [소유하지 않은 자아]라는 것을 알지만 그것을 사용할 때 앱이 충돌합니다. 답변을 마무리하기 위해 코드 샘플을 사용하고 싶습니다.
NatashaTheRobot

1
문서에서 : "약한 참조와 마찬가지로 소유되지 않은 참조는 참조하는 인스턴스를 강력하게 유지하지 않습니다. 그러나 약한 참조와 달리 소유되지 않은 참조는 항상 가치가있는 것으로 간주됩니다."앱이 충돌하면 unowned가 런타임에 0이 아닌 값에 적용되기 때문일 수 있습니다.
빌 패터슨

strongSelf에 바인딩하는 것보다 여기에 보호문을 알리는 것이 좋습니다. 그냥 말하면, 이것은 완벽한 후보와 같습니다 :-D
Daniel Galasko

@NatashaTheRobot, [약한 자기] 구문은 무엇입니까? 객관적인 C에 전달되는 메시지처럼 보입니다. 질문의 구문에 대해 조금 더 추가해 주시겠습니까?
비네 쉬

답변:


178

클로저에 self 가 없을 경우 [weak self]를 사용하십시오 .

클로저에서 self 가 절대로없는 경우 [owned self]를 사용하십시오 .

[소유하지 않은 자아] 를 사용할 때 충돌이 발생하면 해당 폐쇄의 어느 시점에서 자아가 없는 것으로 추측 할 수 있으므로 대신 [약한 자아] 를 사용해야 합니다.

나는 강한 사용에 대한 매뉴얼의 전체 섹션을 정말로 좋아했습니다. , 약한 , 그리고 소유되지 않은를 폐쇄에 :

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

참고 : 폐쇄 라는 용어를 사용했습니다. 새로운 스위프트 용어 인 블록 대신 .

iOS에서 블록 (목표 C)과 클로저 (Swift)의 차이점


7
애플은 C 언어 확장을위한 첫 번째 문서에서 "클로저"를 막았다. (블록 또는 클로저는 맨 처음 C의 확장입니다. MM 만 Objective-C와 관련이 있습니다.) C의 "블록"은 복합 문장과 매우 ​​관련이 있기 때문에 "클로저"라는 용어를 선호합니다. 객체 (변수 또는 상수)를 닫지 않아도 클로저라고하기 때문에 두 언어 모두에서 일종의 잘못입니다.
Amin Negm-Awad

1
매우 정중하게 대답 :)
iDevAmit

1
절대 사용하지 않는 것이 좋습니다 unowned. 앱이 중단 될 위험이 없습니다.
Kyle Redfearn

32

넣어 [unowned self]전에 (text: String)...귀하의 폐쇄에. 이것을 캡처 목록 이라고하며 클로저에서 캡처 된 심볼에 소유권 명령을 배치합니다.


2
명명 해 주셔서 감사합니다. 알고 싶습니다!
rob5408

3
이 답변이 유용하다고 생각하지 않습니다. 자기 폐쇄를 실행하는 동안 전무를하게되면 [소유되지 않은 자기] 충돌합니다
유누스 네딤 Mehel

3
이없는 이유 절대적으로 (1) 이외의 무소속 사용하려면, 매우 특이한 상황이 스타일 집행 문제로 (2) (이 여기 99.999 프로그래밍 %에 완전히 무관하다)의 성능을 위해. "당신은 항상 약한, 결코 소유하지 않아야한다"는 말은 매우 합리적입니다.
Fattie

29

** Swift 4.2 용으로 편집 :

@ Koen이 언급했듯이 swift 4.2는 다음을 허용합니다.

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

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

추신 : 나는 투표를하기 때문에 클로저 탈출 에 관한 독서를 추천하고 싶습니다. .

편집 : @ tim-vermeulen이 언급했듯이 Chris Lattner는 2016 년 1 월 22 일 금요일 19:51:29 CST에 말했다.이 트릭은 자체적으로 사용해서는 안되므로 사용하지 마십시오. 비 탈출 탈출 정보와 @gbk의 캡처 목록 답변을 확인하십시오. **

캡처 목록에서 [약한 자아]를 사용하는 사람들에게는 자아가 전혀 없을 수 있으므로 가장 먼저 가드 문으로 확인하십시오.

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

인용 부호가 무엇인지 궁금한 경우 self이름을 this , weakSelf 또는 무엇이든 변경할 필요없이 클로저 내부에서 자체를 사용하는 것이 좋습니다.



2
나는 로컬 "self" "strongSelf"를 호출하여 기본 self와 혼동되지 않고 강력한 self reference를 지키고 있는지를 쉽게 확인할 수 있습니다.
Justin Stanley

1
컴파일러 버그이므로 이것을 사용해서는 안됩니다 : list.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…
Tim Vermeulen

1
위의 링크에서 Chris Lattner의 의견은 변수의 이름을 self(백틱으로) 지정하지 않는 것 입니다. nonOptionalSelf와 같은 다른 이름을 지정하면 좋습니다.
OutOnAWeekend

1
요즘 (swift 4.2) { [weak self] in guard let self = self else { return }은 백틱없이 사용할 수 있으며 실제로 지원됩니다. github.com/apple/swift-evolution/blob/master/proposals/…
Koen.

26

캡처 목록 사용

캡처 목록 정의

캡처 목록의 각 항목은 클래스 인스턴스 (예 : self) 또는 일부 값으로 초기화 된 변수 (delegate = self.delegate!)에 대한 약하거나 소유되지 않은 키워드의 쌍입니다. 이 쌍은 쉼표로 구분 된 한 쌍의 대괄호 안에 작성됩니다.

캡처 목록을 클로저의 매개 변수 목록 앞에 놓고 제공된 경우 반환 유형 :

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

클로저가 컨텍스트에서 유추 될 수 있으므로 매개 변수 목록 또는 리턴 유형을 지정하지 않은 경우 캡처 목록을 클로저의 맨 처음에 배치 한 후 in 키워드를 배치하십시오.

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

추가 설명


3
"self"에 대해 비 소유를 사용했기 때문에 액세스 할 때 "self"가 전혀 없다는 것을 의미합니다. 그런 다음 "self.delegate"에서 force unwrap을 사용하여 약한 var에 할당합니다. "self.delegate"가 0이 아닌 것을 확실히 알고 있다면, "delegate"에 소유되지 않은 것을 약한 대신 사용하지 않겠습니까?
Roni Leshes 2016 년

26

편집 : LightMan의 업데이트 된 솔루션에 대한 참조

LightMan의 솔루션을 참조하십시오 . 지금까지 나는 사용하고 있었다 :

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

또는:

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

일반적으로 유추 된 경우 매개 변수 유형을 지정할 필요가 없습니다.

매개 변수가 없거나 $0클로저에서 참조하는 경우 매개 변수를 모두 생략 할 수 있습니다 .

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

완전성을 위해; 클로저를 함수에 전달하고 매개 변수가 아닌 @escaping경우 다음이 필요하지 않습니다 weak self.

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

9

신속한 4.2 🔸 기준으로 다음을 수행 할 수 있습니다.

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

다른 솔루션은 비슷한 솔루션을 가지고 있지만 "this"는 C ++ IMHO입니다. "strongSelf"는 Apple 컨벤션이며 코드를 살펴 보는 사람은 진행 상황을 알 수 있습니다.
David H

1
@ David H IMO이 문구 strongSelf는 변수의 의미 / 부작용을 명시 적으로 설명합니다. 그래도 귀하의 의견에 감사드립니다.
eonist

3
Swift 4.2부터 다음과 같이 guard let self = self else { return }풀 수 있습니다 [weak self]: github.com/apple/swift-evolution/blob/master/proposals/…
Amer Hukic

@AmerHukic 👌.
eonist


3

블록의 매개 변수보다 먼저 캡처 목록에서 [약한 자기] 또는 [소유되지 않은 자기]를 사용할 수 있습니다. 캡처 목록은 선택적 구문입니다.

[unowned self]셀이 절대로 0이 아니기 때문에 여기서 잘 작동합니다. 그렇지 않으면 사용할 수 있습니다[weak self]


1
셀은 자아가 아니며 셀 클래스에 있지 않으며 아마도
뷰 컨트롤러에 있습니다

0

필요 이상으로 충돌하는 경우 [약한 자기]

내 생각에 당신이 만들고있는 블록이 어떻게 든 연결되어 있습니다.

PrepareForReuse를 작성하고 그 안에있는 onTextViewEditClosure 블록을 지우십시오.

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

충돌이 발생하지 않는지 확인하십시오. (그냥 추측에 불과합니다).


0

폐쇄 및 강력한 참조주기 [정보]

아시다시피 Swift의 클로저는 인스턴스를 캡처 할 수 있습니다. 그것은 self폐쇄 내부 에서 사용할 수 있음을 의미합니다 . 특히 escaping closure[About]strong reference cycle어떤 것을 만들 수 있습니다 . 그런데 당신은 명시 적으로 selfinside을 사용해야 합니다 escaping closure.

스위프트 클로저에는 Capture List캡처 된 인스턴스에 대한 강력한 참조가 없기 때문에 이러한 상황을 피하고 참조주기를 중단 할 수있는 기능이 있습니다. 캡처 목록 요소는 weak/unowned 클래스 또는 변수에 대한 참조입니다.

예를 들어

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- 가능하면 가능 하면 사용하십시오
  • unowned -인스턴스 소유자의 수명이 클로저보다 클 때 사용하십시오.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.