스위프트에서 항상 내부 폐쇄를 사용해야 합니까?
WWDC 2014 세션 403 Intermediate Swift 및 스크립트에는 다음 슬라이드가 있었습니다.
그 경우에 우리가 사용하지 않는다면, 연사는 말했습니다.[unowned self]
거기서, 그것은 메모리 누수가 될 것입니다.우리가 항상 사용해야 한다는 뜻인가요?[unowned self]
내부 폐쇄?
뷰컨트롤러 64라인에서 스위프트웨더 앱을 사용하지 않습니다.[unowned self]
하지만 몇 가지를 사용하여 UI를 업데이트합니다.@IBOutlet
과 같은self.temperature
그리고.self.loadingIndicator
괜찮을지도 몰라 왜냐면 다@IBOutlet
는 " 정의는한내 s"입니다.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]
강력한 기준 주기를 생성할 때입니다.강력한 참조 주기는 객체가 서로 소유하게 되는 소유권 루프가 발생하는 경우입니다(아마도 타사를 통해). 따라서 둘 다 서로 유지되도록 보장하기 때문에 할당이 해제되지 않습니다.
폐쇄의 특정한 경우에는 그 내부에서 참조되는 변수가 폐쇄에 의해 "소유"된다는 것을 인식하면 됩니다.폐쇄가 주변에 있는 한, 그 물체들은 주변에 있을 것이 보장됩니다.그 소유권을 막을 수 있는 유일한 방법은,[unowned self]
또는[weak self]
클래스가 폐쇄를 소유하고 있고 해당 폐쇄가 해당 클래스에 대한 강력한 참조를 캡처하는 경우 폐쇄와 클래스 간의 강력한 참조 사이클이 발생합니다.클래스가 폐쇄를 소유하는 무언가를 소유하고 있는지 여부도 포함됩니다.
특히 비디오의 예에서.
에서, 슬이드의예서에라,TempNotifier
은 를통폐소니다합유를을 하고 있습니다.onChange
멤버 변수입니다.이 그이선않경우은지를 선언하지 .self
~하듯이unowned
폐쇄는 또한 소유할 것입니다.self
강력한 기준 주기를 생성합니다.
사의차이의 unowned
그리고.weak
의 unowned
그리고.weak
은 것은입니다.weak
선택 사항으로 선언되는 동안unowned
아닙니다.선언함으로써weak
당신은 어느 시점에서 폐쇄 내부에서 0일 수도 있는 사건을 처리할 수 있습니다.에 ,unowned
변수가 0이면 전체 프로그램이 충돌합니다.만 하세요.unowned
때, 변수는 항상 존재합니다.
11/2016 업데이트
저는 이 답변을 확장하는 기사를 썼습니다(ARC가 무엇을 하는지 이해하기 위해 SIL을 조사합니다). 여기에서 확인하십시오.
원답
앞의 답변들은 다른 답변보다 언제 사용해야 하는지, 왜 사용해야 하는지에 대한 간단한 규칙을 제공하지 않으므로 몇 가지 추가하겠습니다.
소유하지 않거나 약한 논의는 변수의 수명과 이를 참조하는 폐쇄의 문제로 요약됩니다.
시나리오
두 가지 시나리오를 사용할 수 있습니다.
폐쇄는 변수의 수명이 동일하므로 변수에 도달할 수 있을 때까지만 폐쇄에 도달할 수 있습니다.변수와 폐쇄의 수명은 동일합니다.이 경우 참조를 소유하지 않음으로 선언해야 합니다.일반적인 예는 다음과 같습니다.
[unowned self]
부모의 맥락에서 무언가를 하고 다른 곳에서 언급되지 않는 작은 폐쇄의 많은 예에서 사용됩니다.폐쇄 수명은 변수 중 하나와 독립적이며, 변수에 더 이상 연결할 수 없는 경우에도 폐쇄를 참조할 수 있습니다.이 경우 참조를 사용하기 전에 약한 것으로 선언하고 0이 아닌지 확인해야 합니다(랩을 강제로 풀지 마십시오).일반적인 예는 다음과 같습니다.
[weak delegate]
일부 폐쇄 예제에서는 전혀 관련이 없는(비공식적으로) 대리자 개체를 참조하는 것을 볼 수 있습니다.
실제 사용량
그렇다면 실제로 대부분의 시간을 사용할 것인가요?
트위터에서 Joe Groff의 말을 인용합니다.
소유하지 않음은 더 빠르고 불변성과 비선택성을 허용합니다.
약함이 필요하지 않으면 사용하지 마세요.
소유자가 없는 것에 대해 더 많이 알게 될 것입니다.*
이곳의 내부 작업
*
일반적으로 소유되지 않은 참조에 액세스하기 전에 런타임 검사(잘못된 참조에 대한 충돌로 이어지는)가 수행됨을 나타내기 위해 소유되지 않은(안전)이라고도 합니다.
뷰 컨트롤러에 대한 구체적인 예를 몇 가지 추가할 것이라고 생각했습니다.Stack Overflow에 대한 설명뿐만 아니라 많은 설명이 정말 좋지만 실제 예제를 사용하면 더 잘 작동합니다(@drewag는 이에 대해 좋은 시작을 했습니다).
- 할 수 시간이 네워크요응처리수경있을 사용합니다.
weak
왜냐하면 그들은 오래 살았기 때문입니다.에 뷰 수 " " "라는 메시지가 표시됩니다.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. }
폐쇄에서 자아가 0이 될 수 있다면 [나약한 자아]를 사용하십시오.
폐쇄에서 자아가 결코 0이 되지 않는다면 [무소유의 자아]를 사용하십시오.
Apple Swift 설명서에는 폐쇄 시 강한 것과 약한 것, 소유하지 않은 것의 차이를 설명하는 이미지가 포함된 훌륭한 섹션이 있습니다.
다음은 Apple Developer Forum에서 제공하는 멋진 세부 정보입니다.
unowned
대unowned(safe)
대unowned(unsafe)
unowned(safe)
액세스할 때 개체가 아직 살아 있음을 주장하는 중요하지 않은 참조입니다.그것은 암묵적으로 포장되지 않은 약한 선택적 참조와 같습니다.x!
액세스할 때마다 표시됩니다.unowned(unsafe)
는 것과 같은__unsafe_unretained
ARC에서—소유하지 않는 참조이지만 액세스 시 개체가 아직 활성 상태인지 런타임 검사가 없으므로 매달린 참조가 가비지 메모리에 도달합니다.unowned
는항의동니다입어의상▁for의 입니다.unowned(safe)
현재, 하지만 의도는 그것이 최적화될 것입니다.unowned(unsafe)
-Ofast
런타임 검사가 비활성화된 경우 빌드됩니다.
unowned
대weak
unowned
실제로 보다 훨씬 간단한 구현을 사용합니다.weak
는 두 카운트를 하며 Native Swift 개체는 Native Swift 개체와 함께 됩니다.unowned
참조는 강한 참조 카운트 대신 소유하지 않은 참조 카운트를 범핑합니다.개체의 강력한 참조 카운트가 0에 도달하면 개체가 초기화 해제되지만 소유하지 않은 참조 카운트도 0에 도달할 때까지 실제로 할당 해제되지 않습니다.이로 인해 소유하지 않은 참조가 있을 때 메모리가 약간 더 오래 유지되지만 일반적으로 다음과 같은 경우에는 문제가 되지 않습니다.unowned
이는 관련 개체의 수명이 거의 짧아야 하기 때문에 사용되며 약한 참조를 제로로 만드는 데 사용되는 사이드 테이블 기반 구현보다 훨씬 간단하고 지연 시간이 짧기 때문입니다.
업데이트: 현대의 Swift는 내부적으로 동일한 메커니즘을 사용합니다.따라서 이 비교는 목표-C를 비교하기 때문에 부정확합니다.weak
스위프트와 함께unonwed
.
이유들
소유 참조가 0에 도달한 후 메모리를 활성화하는 목적은 무엇입니까?코드가 초기화되지 않은 후 소유하지 않은 참조를 사용하여 개체에 대해 작업을 수행하려고 하면 어떻게 됩니까?
메모리는 유지 카운트를 계속 사용할 수 있도록 활성 상태로 유지됩니다.이러한 방식으로 누군가가 소유하지 않은 개체에 대한 강력한 참조를 유지하려고 할 때 런타임은 개체를 유지하는 것이 안전한지 확인하기 위해 강력한 참조 수가 0보다 큰지 확인할 수 있습니다.
개체가 보유한 참조를 소유하거나 소유하지 않은 경우 어떻게 됩니까?개체가 초기화 해제될 때 개체의 수명이 분리됩니까? 아니면 마지막으로 소유되지 않은 참조가 해제된 후 개체가 할당 해제될 때까지 메모리도 유지됩니까?
개체가 소유한 모든 리소스는 개체의 마지막 강력한 참조가 해제되고 해당 deinit가 실행되는 즉시 해제됩니다.소유하지 않은 참조는 메모리만 활성 상태로 유지합니다. 헤더와 참조 수를 제외하고 내용은 정크입니다.
신나죠?
여기에 몇 가지 훌륭한 답이 있습니다.그러나 Swift가 약한 참조를 구현하는 방법에 대한 최근의 변화는 모든 사람의 약한 자아와 소유하지 않은 자아 사용 결정을 변화시킬 것입니다.이전에는, 소유하지 않은 자아를 사용하여 최고의 성능을 필요로 한다면, 소유하지 않은 자아에 접근하는 것이 약한 자아에 접근하는 것보다 훨씬 빠르기 때문에, 소유하지 않은 자아가 결코 0이 되지 않을 것이라는 것을 확신할 수 있는 한, 약한 자아에 접근하는 것보다 뛰어났습니다.
그러나 Mike Ash는 Swift가 사이드 테이블을 사용하기 위해 약한 팀의 구현을 업데이트한 방법과 이것이 약한 자체 성과를 실질적으로 개선하는 방법을 문서화했습니다.
https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html
나약한 자신에 대한 성능 저하가 크지 않기 때문에 앞으로는 기본적으로 사용해야 한다고 생각합니다.약한 자아의 이점은 선택 사항이라는 것입니다. 이것은 더 정확한 코드를 쓰는 것을 훨씬 쉽게 만듭니다. 기본적으로 스위프트가 훌륭한 언어인 이유입니다.당신은 어떤 상황이 소유하지 않은 자신의 사용에 안전한지 알고 있다고 생각할지 모르지만, 제가 다른 많은 개발자 코드를 검토해 본 경험은 대부분 그렇지 않습니다.소유하지 않은 자가 할당 해제된 충돌을 많이 해결했습니다. 대개 컨트롤러 할당 해제 후 백그라운드 스레드가 완료되는 상황입니다.
버그와 충돌은 프로그래밍에서 가장 시간이 많이 걸리고 고통스러우며 비용이 많이 드는 부분입니다.올바른 코드를 작성하고 이를 피하기 위해 최선을 다하십시오.저는 선택지를 강제로 풀지 않고 약한 자신 대신 소유하지 않은 자신을 절대 사용하지 않는 것을 규칙으로 만들 것을 추천합니다.당신은 시간을 그리워하는 어떤 것도 잃지 않을 것입니다. 강제로 포장을 풀고 소유하지 않은 자신은 실제로 안전합니다.그러나 찾기 어려운 오류 및 버그를 제거하여 많은 이점을 얻을 수 있습니다.
Apple-doc에 따르면
약한 참조는 항상 선택적인 유형이며 참조하는 인스턴스의 할당이 취소되면 자동으로 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)
}
소유되지 않은 것은 약한 것과 비슷하지만, 소유되지 않은 것은 더 이상 존재하지 않는 개체에 대한 참조가 존재하지 않을 때 0으로 변하는 약한 변수입니다. 일반적인 닐 검사로 처리할 수 있습니다. 소유되지 않은 것은 그냥 쓰레기가 될 것입니다. 더 이상 쓰레기가 아니라고 말할 수 없고 사용하면 충돌할 것입니다.약한 것의 문제는 만약 어떤 물체가 약한 변수에 의해 그것을 언급한다면, 그것이 파괴되었을 때, 그것은 그것에 대한 모든 참조를 거쳐야 하고 그 변수를 0으로 설정해야 한다는 것입니다. 이것은 분명히 비용이 많이 들 것입니다. 대신 소유하지 않은 것을 사용하는 것은 단지 충돌하고 이런 종류의 버그를 찾는 것은 어려울 것입니다.소유하지 않은 데이터를 사용할 수 있는 한 가지 방법은 명확한 인터페이스를 가지고 있고 내부에 직접 액세스할 수 없는 신중하게 포함된 데이터 유형을 만드는 경우입니다. 구현 시 많은 순환 참조를 갖는 것이 유용할 수 있지만 자체적으로 포함된 비소유 참조를 사용하여 이러한 순환 참조를 사용할 수 있습니다.예를 들어 약한 변수를 희생하지 않고 노드 트리가 있을 수 있으며 각 노드는 상위 노드에 대한 참조가 필요합니다. 노드를 삭제하면 모든 하위 노드가 삭제되므로 모든 하위 노드가 0으로 설정되어야 하는 것은 아닙니다.
위의 내용 중 어느 것도 의미가 없는 경우
tl;dr
치마처럼...
implicitly unwrapped optional
참조가 사용 시점에서 0이 되지 않을 것임을 보장할 수 있는 경우 소유하지 않은 것을 사용합니다.그렇지 않다면, 당신은 약한 것을 사용해야 합니다.
설명:
다음을 검색했습니다: 소유되지 않은 약한 링크.제가 수집한 바로는 소유되지 않은 자아는 0이 될 수 없지만 약한 자아는 0이 될 수 있고 소유되지 않은 자아는 매달린 포인터로 이어질 수 있습니다.오브젝티브-C에서 악명 높은 것.도움이 되길 바랍니다.
"소유하지 않은 약자와 소유하지 않은 참조는 유사하게 행동하지만 같지 않습니다."
소유하지 않은 참조는 약한 참조와 마찬가지로 참조 중인 개체의 보유 수를 늘리지 않습니다.그러나 Swift에서는 소유자가 없는 참조는 옵션이 아니라는 추가 이점이 있습니다.이렇게 하면 선택적 바인딩을 사용하는 대신 관리가 더 쉬워집니다.이것은 암묵적으로 포장되지 않은 옵션과 다르지 않습니다.또한 소유하지 않은 참조는 0이 아닙니다.즉, 개체가 할당 취소될 때 포인터가 0이 되지 않습니다.이는 소유하지 않은 참조를 사용하면 경우에 따라 포인터가 흔들릴 수 있음을 의미합니다.저처럼 목표-C 시절을 기억하는 여러분을 위해 소유하지 않은 참조는 안전하지 않은_보존되지 않은 참조에 매핑됩니다.
여기가 좀 헷갈리는 부분입니다.
약한 참조와 소유되지 않은 참조 모두 보존 카운트를 증가시키지 않습니다.
둘 다 유지 사이클을 중단하는 데 사용할 수 있습니다.그래서 우리는 언제 그것들을 사용합니까?!
Apple 문서에 따르면:
"해당 참조가 유효할 때마다 약한 참조를 사용합니다. 해당 참조가 수명 동안 어느 시점에서 0이 됩니다.반대로, 초기화 중에 설정된 참조가 영이 되지 않는다는 것을 알고 있는 경우에는 소유하지 않은 참조를 사용하십시오."
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "AnotherViewController")
self.navigationController?.pushViewController(controller, animated: true)
}
}
import UIKit
class AnotherViewController: UIViewController {
var name : String!
deinit {
print("Deint AnotherViewController")
}
override func viewDidLoad() {
super.viewDidLoad()
print(CFGetRetainCount(self))
/*
When you test please comment out or vice versa
*/
// // Should not use unowned here. Because unowned is used where not deallocated. or gurranted object alive. If you immediate click back button app will crash here. Though there will no retain cycles
// clouser(string: "") { [unowned self] (boolValue) in
// self.name = "some"
// }
//
//
// // There will be a retain cycle. because viewcontroller has a strong refference to this clouser and as well as clouser (self.name) has a strong refferennce to the viewcontroller. Deint AnotherViewController will not print
// clouser(string: "") { (boolValue) in
// self.name = "some"
// }
//
//
// // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser (self.name) has a weak refferennce to the viewcontroller. Deint AnotherViewController will print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
//
// clouser(string: "") { [weak self] (boolValue) in
// self?.name = "some"
// }
// no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser nos refference to the viewcontroller. Deint AnotherViewController will print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
clouser(string: "") { (boolValue) in
print("some")
print(CFGetRetainCount(self))
}
}
func clouser(string: String, completion: @escaping (Bool) -> ()) {
// some heavy task
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
completion(true)
}
}
}
만약 당신이 확신하지 못한다면.
[unowned self]
그 다음에 사용[weak self]
순환 참조를 피하기 위해 강력한 참조가 되지 않을 참조가 있습니다.따라서 어떤 시점에서 개체에 대한 마지막 강력한 참조가 제거되면 개체 자체가 제거됩니다.
강한 참조가 아닌 다른 참조는 어떻게 됩니까?분명히 그들은 더 이상 그 물체를 언급하지 않습니다. 이것은 문제가 있습니다.이 문제를 처리하는 방법에는 두 가지가 있습니다.
약한 참조.개체에 대한 마지막 강한 참조가 사라지면 모든 약한 참조가 0으로 설정되므로 개발자가 참조된 개체가 더 이상 있는지 확인할 수 있습니다.분명히 약한 참조는 선택 사항이어야 합니다. 그렇지 않으면 0으로 설정할 수 없습니다.약한 참조를 사용하는 전략:"iflet ref = weakref"라고 씁니다.참조가 여전히 존재하고 강력한 참조에 할당했으므로 "이 경우"가 끝날 때까지 유지됩니다.이렇게 하지 않으면 동일한 약한 참조에 두 번 액세스할 수 있으며, 첫 번째 액세스에서는 0이 아니라 두 번째 액세스에서는 0이 될 수 있습니다.
소유자가 없는 참조를 만듭니다.만약 그 물체가 사라진다면, 아무도 당신에게 말하지 않을 것입니다.참조된 개체가 사라지면 참조가 있는 것처럼 보입니다.참조된 개체가 일찍 사라질 수 없다고 100% 확신하는 경우에만 이 옵션을 사용해야 합니다.
속도가 더 빠르다는 것을 측정하고 100%일 때 개체가 사라졌을 때 쓰레기를 사용하지 않는다는 것을 측정한 경우 unowonnown을 사용합니다.
언급URL : https://stackoverflow.com/questions/24320347/shall-we-always-use-unowned-self-inside-closure-in-swift
'sourcetip' 카테고리의 다른 글
CSV 파일 읽기 및 배열에 값 저장 (0) | 2023.05.23 |
---|---|
wget을 사용하여 전체 디렉터리와 하위 디렉터리를 다운로드하는 방법은 무엇입니까? (0) | 2023.05.23 |
MongoDB에 포함된 문서에 대한 고유 ID를 만들려면 어떻게 해야 합니까? (0) | 2023.05.23 |
Eclipse Git 플러그인에서 기본 작성자 및 커밋을 변경하려면 어떻게 해야 합니까? (0) | 2023.05.23 |
문자열 배열을 문자열로 변환 (0) | 2023.05.23 |