우리는 항상 Swift에서 [소유하지 않은 자체] 클로저를 사용해야합니까?
WWDC 2014 세션 403 Intermediate Swift and transcript 에서 다음 슬라이드가있었습니다.
이 경우 스피커는 사용하지 않으면 [unowned self]
메모리 누수가 발생 한다고 말했다 . 항상 [unowned self]
내부 폐쇄를 사용해야한다는 의미 입니까?
에 스위프트 날씨 응용 프로그램의 ViewController.swift 라인 (64) , I는 사용하지 마십시오 [unowned self]
. 하지만 일부 사용하여 UI를 업데이트 @IBOutlet
처럼들 self.temperature
하고 self.loadingIndicator
. @IBOutlet
내가 정의한 모든 것이이므로 괜찮을 수도 있습니다 weak
. 그러나 안전을 위해 항상 사용해야 [unowned self]
합니까?
class TempNotifier {
var onChange: (Int) -> Void = {_ in }
var currentTemp = 72
init() {
onChange = { [unowned self] temp in
self.currentTemp = temp
}
}
}
아니요, 사용하고 싶지 않은 시간이 있습니다 [unowned self]
. 때로는 클로저가 호출 될 때까지 가까워 지도록 클로저가 자체 캡처를 원할 수도 있습니다.
예 : 비동기 네트워크 요청
비동기 네트워크 요청을 하는self
경우 요청이 완료 될 때 클로저를 유지하려고합니다 . 해당 객체가 다른 방식으로 할당 해제되었을 수 있지만 여전히 요청 마무리를 처리 할 수 있기를 원합니다.
사용시기 unowned self
또는weak self
당신이 정말로 사용하고 싶을 때 [unowned self]
또는 [weak self]
당신이 강한 참조주기를 만들 때 입니다. 강력한 참조주기는 객체가 서로를 소유하게되는 소유권 루프가있는 경우 (제 3자를 통해 가능할 수 있음) 서로를 고수하기 때문에 할당 해제되지 않는 경우입니다.
클로저의 특정한 경우에는 그 안에서 참조되는 변수가 클로저에 의해 "소유"된다는 것을 인식하면됩니다. 폐쇄가 주위에있는 한, 그 물체는 주위에 있어야합니다. 해당 소유권을 중지하는 유일한 방법은 [unowned self]
또는 을 수행하는 것 [weak self]
입니다. 따라서 클래스가 클로저를 소유하고 해당 클로저가 해당 클래스에 대한 강력한 참조를 캡처하면 클로저와 클래스 사이에 강력한 참조주기가 있습니다. 클래스가 클로저를 소유 한 것을 소유하고 있는지도 포함합니다.
비디오의 예에서 구체적으로
슬라이드의 예에서 멤버 변수를 TempNotifier
통해 클로저를 소유합니다 onChange
. 그들이 선언하지 않은 경우 self
로 unowned
, 폐쇄는 자신의 것이 self
강한 참조주기를 생성.
차이 사이 unowned
와weak
의 차이 unowned
와 weak
그가있다 weak
(선택 사양) 동안과 같이 선언 unowned
하지 않습니다. 그것을 선언함으로써 weak
어떤 시점에서 클로저 내부가 없을 수있는 경우를 처리하게됩니다. unowned
nil 인 변수 에 액세스하려고 하면 전체 프로그램이 중단됩니다. 따라서 unowned
클로저가 주변에있는 동안 변수가 항상 주변에 있다는 긍정적 인 경우 에만 사용 하십시오.
2016 년 11 월 업데이트
이 답변을 확장 (ARC 가하는 일을 이해하기 위해 SIL을 보며)에 대한 기사를 썼습니다 . 여기 에서 확인 하십시오 .
원래 답변
이전 답변은 실제로 다른 것을 사용할 때와 그 이유에 대한 간단한 규칙을 제공하지는 않으므로 몇 가지를 추가하겠습니다.
소유되지 않거나 약한 토론 은 변수 의 수명 과 변수를 참조하는 클로저에 대한 문제로 귀결됩니다 .
시나리오
가능한 두 가지 시나리오가 있습니다.
클로저는 변수의 수명이 동일하므로 변수에 도달 할 때까지 클로저 에 도달 할 수 있습니다. 변수와 클로저의 수명은 동일합니다. 이 경우 참조를 소유하지 않은 것으로 선언해야합니다 . 일반적인 예는
[unowned self]
부모의 맥락에서 무언가를하고 다른 곳에서 참조되지 않는 부모의 수명이 다하지 않는 작은 폐쇄의 많은 예 에서 사용됩니다.클로저 수명은 변수 중 하나와 독립적이며 변수에 더 이상 도달 할 수없는 경우 클로저를 계속 참조 할 수 있습니다. 이 경우 참조를 약한 것으로 선언하고 사용하기 전에 참조 가 아닌지 확인해야합니다 (포장 풀기 금지). 이것의 일반적인 예는
[weak delegate]
완전히 관련되지 않은 (평생 현명한) 델리게이트 객체를 참조하는 클로저의 일부 예에서 볼 수 있는 것입니다.
실제 사용법
그렇다면 실제로 어떤 시간을 사용해야합니까?
Unowned는 더 빠르며 불변성과 비 선택성을 허용합니다.
약한 것이 필요하지 않으면 사용하지 마십시오.
소유하지 않은 *
내부 작업 에 대한 자세한 내용은 여기를 참조하십시오 .
*
일반적으로 소유되지 않은 참조에 액세스하기 전에 런타임 검사 (유효하지 않은 참조로 인해 충돌이 발생 함)가 수행됨을 나타 내기 위해 unowned (safe)라고도합니다.
뷰 컨트롤러에 대한 구체적인 예제를 추가 할 것이라고 생각했습니다. 여기에 스택 오버플로에 대한 설명뿐만 아니라 많은 설명이 실제로 훌륭하지만 실제 예제를 사용하여 더 잘 작동합니다 (@drewag는 이것에 대해 잘 시작했습니다).
- 네트워크 요청에 대한 응답을 처리 할 수있는 클로저가있는 경우에는
weak
수명이 길기 때문에 use를 사용 합니다. 요청이 완료되기 전에 뷰 컨트롤러가 닫힐 수 있으므로self
클로저가 호출 될 때 더 이상 유효한 객체를 가리 키지 않습니다. 단추의 이벤트를 처리하는 클로저가있는 경우 이는
unowned
뷰 컨트롤러가 사라지 자마자 버튼 및 참조하는 다른 항목self
이 동시에 사라 지기 때문일 수 있습니다 . 폐쇄 블록도 동시에 사라집니다.class MyViewController: UIViewController { @IBOutlet weak var myButton: UIButton! let networkManager = NetworkManager() let buttonPressClosure: () -> Void // closure must be held in this class. override func viewDidLoad() { // use unowned here buttonPressClosure = { [unowned self] in self.changeDisplayViewMode() // won't happen after vc closes. } // use weak here networkManager.fetch(query: query) { [weak self] (results, error) in self?.updateUI() // could be called any time after vc closes } } @IBAction func buttonPress(self: Any) { buttonPressClosure() } // rest of class below. }
클로저에 self 가 없을 경우 [weak self]를 사용하십시오 .
클로저에서 self 가 절대로없는 경우 [owned self]를 사용하십시오 .
Apple Swift 문서에는 강력 함 , 약함 및 소유하지 않은 클로저 사용의 차이점을 설명하는 이미지가 포함 된 훌륭한 섹션이 있습니다 .
다음은 맛있는 세부 사항을 설명한 Apple 개발자 포럼의 훌륭한 인용문입니다 .
unowned
vs unowned(safe)
vsunowned(unsafe)
unowned(safe)
객체가 여전히 살아 있다는 액세스를 주장하는 비 소유 참조입니다.x!
액세스 할 때마다 암시 적으로 래핑되지 않는 약한 선택적 참조와 같습니다 . ARCunowned(unsafe)
와 비슷합니다__unsafe_unretained
. 소유하지 않은 참조이지만 객체가 여전히 액세스 상태에 있는지 런타임 검사가 없으므로 매달려있는 참조는 가비지 메모리에 도달합니다.unowned
는 항상unowned(safe)
현재와 동의어 이지만 런타임 검사가 비활성화되면 빌드unowned(unsafe)
에서 최적화됩니다-Ofast
.
unowned
vs weak
unowned
실제로보다 간단한 구현을 사용합니다weak
. Native Swift 객체는 두 개의 참조 카운트를 가지며unowned
참조 는 강력한 참조 카운트 대신 소유되지 않은 참조 카운트 를 충돌시킵니다 . 강한 참조 카운트 가 0에 도달하면 객체가 초기화 되지 않지만, 소유되지 않은 참조 카운트 도 0에 도달 할 때까지 실제로 할당이 해제되지 않습니다 . 이로 인해 소유되지 않은 참조가있을 때 메모리가 약간 더 길게 유지되지만 일반적으로 문제가되지 않습니다.unowned
어쨌든 관련 객체의 수명이 거의 같아야하므로 약한 참조를 0으로 만드는 데 사용되는 사이드 테이블 기반 구현보다 훨씬 간단하고 오버 헤드가 높습니다.
업데이트 : 현대 스위프트에서는 weak
내부적으로 동일한 메커니즘 사용 unowned
하지를 . 따라서 Objective-C weak
와 Swift를 비교하기 때문에이 비교는 올바르지 않습니다 unonwed
.
원인
참조가 0에 도달 한 후에 메모리를 활성 상태로 유지하는 목적은 무엇입니까? 코드가 초기화되지 않은 후 소유되지 않은 참조를 사용하여 객체로 무언가를 시도하면 어떻게됩니까?
보유 횟수를 계속 사용할 수 있도록 메모리가 활성 상태로 유지됩니다. 이렇게하면 누군가 소유하지 않은 객체에 대한 강력한 참조를 유지하려고 할 때 런타임에서 객체를 안전하게 유지하기 위해 강한 참조 횟수가 0보다 큰지 확인할 수 있습니다.
개체가 소유하거나 소유하지 않은 참조는 어떻게됩니까? 초기화되지 않은 객체의 수명이 객체에서 분리 되었습니까? 아니면 마지막 소유하지 않은 참조가 해제 된 후 객체가 할당 해제 될 때까지 메모리가 유지됩니까?
객체가 소유 한 모든 리소스는 객체의 마지막 강력한 참조가 해제되고 deinit가 실행 되 자마자 해제됩니다. 소유되지 않은 참조는 메모리를 활성 상태로 유지합니다. 참조 횟수가있는 헤더 외에는 내용이 잘못되었습니다.
흥분 되나요?
여기에 좋은 답변이 있습니다. 그러나 Swift가 약한 참조를 구현하는 방법에 대한 최근의 변화는 모든 사람의 약한 자체 대 소유되지 않은 자체 사용 결정을 바꿔야합니다. 이전에는 소유하지 않은 자아에 액세스하는 것이 약한 자아에 액세스하는 것보다 훨씬 빠르기 때문에 소유하지 않은 자아를 사용하여 최상의 성능이 필요한 경우, 자아가 절대 없을 수 있다고 확신 할 수있는 한, 자존심이 약한 자보다 우수했습니다.
그러나 Mike Ash는 Swift가 사이드 테이블을 사용하기 위해 약한 변수의 구현을 업데이트 한 방법과 약한 자체 성능을 향상시키는 방법을 문서화했습니다.
https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html
약한 자아에 대한 상당한 성능 저하가 없으므로 앞으로는 기본적으로 사용하는 것이 좋습니다. 약한 자아의 장점은 선택 사항이므로 더 정확한 코드를 작성하는 것이 훨씬 쉬워집니다. 기본적으로 Swift가 훌륭한 언어입니다. 소유하지 않은 자아를 사용하는 것이 안전한 상황을 알고 있다고 생각할 수도 있지만 다른 개발자 코드를 많이 검토 한 경험은 대부분 그렇지 않습니다. 나는 일반적으로 컨트롤러가 할당 해제 된 후 백그라운드 스레드가 완료되는 상황에서 소유되지 않은 자체 할당이 취소되는 많은 충돌을 수정했습니다.
버그와 충돌은 프로그래밍에서 가장 시간이 많이 걸리고 고통스럽고 비싼 부분입니다. 올바른 코드를 작성하고 피하기 위해 최선을 다하십시오. 랩핑 해제 옵션을 강요하지 않고 약한 자아 대신 소유하지 않은 자아를 사용하지 않는 것이 좋습니다. 포장을 풀고 소유하지 않은 자아는 실제로 안전합니다. 그러나 충돌 및 버그를 찾아서 디버그하기 어려운 것을 제거하면 많은 이점을 얻을 수 있습니다.
Apple-doc 에 따르면
약한 참조는 항상 선택적인 유형이며 참조하는 인스턴스가 할당 해제되면 자동으로 nil이됩니다.
캡처 된 참조가 절대로 0이되지 않으면 약한 참조가 아닌 항상 소유되지 않은 참조로 캡처해야합니다.
예 -
// if my response can nil use [weak self]
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
// Only use [unowned self] unowned if guarantees that response never nil
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
폐쇄를위한 강력한 기준주기
클래스 인스턴스의 속성에 클로저를 할당하고 해당 클로저의 본문이 인스턴스를 캡처하는 경우에도 강력한 참조주기가 발생할 수 있습니다. 이 캡처는 클로저의 본문이와 같은 인스턴스의 속성에 액세스 self.someProperty
하거나 클로저가와 같은 인스턴스의 메서드를 호출하기 때문에 발생할 수 있습니다 self.someMethod()
. 두 경우 모두 이러한 액세스로 인해 클로저가 "캡처" self
되어 강력한 참조주기가 생성됩니다. 클로저에서 값을 캡처하는 방법에 대한 자세한 내용
Swift는 클로저 캡처리스트 라고하는이 문제에 대한 우아한 솔루션을 제공 합니다 . 캡처 목록은 클로저 본문 내에서 하나 이상의 참조 유형을 캡처 할 때 사용할 규칙을 정의합니다. 두 개의 클래스 인스턴스 사이의 강한 참조주기와 마찬가지로, 당신은으로 캡처 된 각 참조를 선언 weak
하거나 unowned
오히려보다 참조를 strong
참조. 의 적절한 선택 weak
또는 unowned
코드의 다른 부분 사이의 관계에 따라 달라집니다. 여기 더
- 수명이 다할 때
weak
까지 해당 참조가 유효 할 때마다 참조를 사용하십시오nil
. - 사용
unowned
하면 참조 것이라는 점을 알고있을 때 참조를 결코 없을nil
이 초기화하는 동안 설정 한 후.
위의 어느 것도 의미가 없다면 :
tl; dr
마찬가지로 , 사용 시점에서 참조가 무효가되지 않도록 보장
implicitly unwrapped optional
할 수 있는 경우, 소유하지 않은 것을 사용하십시오. 그렇지 않으면 약한 것을 사용해야합니다.
설명:
아래에서 다음을 검색했습니다 : weak unowned link . 내가 수집 한 것에서, 소유하지 않은 자아 는 무질서 할 수 없지만 약한 자아는 될 수 없으며, 소유하지 않은 자아는 매달려있는 포인터로 이어질 수 있습니다 ... Objective-C에서 악명 높은 것. 그것이 도움이되기를 바랍니다.
"사용하지 않는 약한 참조 및 소유되지 않은 참조는 비슷하게 동작하지만 동일하지는 않습니다."
약한 참조 와 같이 소유되지 않은 참조는 참조 되는 개체의 유지 횟수를 늘리지 않습니다 . 그러나 Swift에서 소유하지 않은 참조는 Optional이 아닌 추가 이점이 있습니다. 따라서 선택적 바인딩을 사용하지 않고 관리 하기가 더 쉽습니다 . 이것은 Implicitly Unwrapped Optionals와 다르지 않습니다. 또한 소유되지 않은 참조는 0 이 아닙니다 . 이것은 객체가 할당 해제 될 때 포인터를 제로화하지 않음을 의미합니다. 즉, 소유하지 않은 참조를 사용하면 포인터가 매달려 있을 수 있습니다.. 저처럼 Objective-C 시절을 기억하는 괴상한 사람들에게는 소유되지 않은 참조가 unsafe_unretained 참조에 매핑됩니다.
여기가 약간 혼란스러워집니다.
약하고 소유되지 않은 참조는 모두 보유 횟수를 증가시키지 않습니다.
둘 다 유지주기를 중단하는 데 사용할 수 있습니다. 그래서 언제 사용합니까?!
애플의 문서 에 따르면 :
그 때마다 "약한 참조를 사용하여 유효한 해당 참조의 수명 동안 어떤 시점에서 nil이 될 수 있도록. 반대로 초기화하는 동안 참조가 설정되지 않은 경우에는 참조가 없음을 알고있는 경우 소유되지 않은 참조를 사용하십시오.”
'Programming' 카테고리의 다른 글
C #에서 URL에 대한 쿼리 문자열을 작성하는 방법은 무엇입니까? (0) | 2020.02.16 |
---|---|
속성의 값으로 인덱스를 갖는 ngFor (0) | 2020.02.16 |
C #에서 선택적 매개 변수를 어떻게 사용할 수 있습니까? (0) | 2020.02.16 |
쿼리 빌더가 원시 SQL 쿼리를 문자열로 출력하도록하려면 어떻게해야합니까? (0) | 2020.02.16 |
JavaScript로 파일 확장자를 얻으려면 어떻게해야합니까? (0) | 2020.02.16 |