일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- swift6
- RxCocoa
- vim
- NullObject
- NavigationLink
- typeorm
- init
- operator
- Xcode
- dismiss
- nestjs
- @Binding
- Operater
- URL(string:)
- RxSwift
- RFC1738/1808
- @EnvironmentObject
- SWIFT
- graphql
- nonisolated
- init?
- Operators
- @Environment
- Bug
- subject
- ios14
- SwiftUI
- Creating Operators
- IOS
- @State
- Today
- Total
Tunko Development Diary
Swift ARC [weak self] 란? 본문
ARC(Automatic Reference Counting) 을 통해서 대부분의 문제를 해결했지만 Velue Type 이 아닌 Reference type 에서는 Retain Count를 관리해야 합니다.
Swift 코딩중에 클로저와 같은 것을 사용하게 되면 종종 [weak self] 가필요합니다. 이게 왜 필요한지 차근차근 알아보려고 합니다.
ARC란?
Automatic Reference Counting - The Swift Programming Language (Swift 5.7)
Swift에서 우리는 코드의 관계 사이에 필요한 정보를 ARC에서 제공하기 위해 weak self 와 unowned self를 사용해야 합니다. weak self 와 unowned self를 사용하지 않으면 기본적으로 strong reference (강한 참조) 상태로 메모리상에 유지되며 ARC를 통해 자동적으로 할당된 메모리가 해지되지 않게 됩니다. 즉, 메모리 누수가 발생합니다.
참조 카운트는 클래스 인스턴스에서만 적용됩니다. 구조 및 열거형등 값타입에는 적용되지 않습니다.
weak self 는 언제 사용해야 될까?
우선 약한 참조는 nil 참조가 할당 해제될 때 ARC에 의해 자동으로 설정될 수 있으므로 항상 선택적 변수로 선언됩니다. 다음은 예시입니다.
class Blog {
let name: String
let url: URL
var owner: Blogger?
init(name: String, url: URL) { self.name = name; self.url = url }
deinit {
print("Blog \\(name) is being deinitialized")
}
}
class Blogger {
let name: String
var blog: Blog?
init(name: String) { self.name = name }
deinit {
print("Blogger \\(name) is being deinitialized")
}
}
deinit을 통해 메모리가 해지되면 print메시지가 출력됩니다.
var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")
blog!.owner = blogger
blogger!.blog = blog
blog = nil
blogger = nil
// Nothing is printed
위 코드를 보면 서로가 서로를 가지고있습니다. nil을 할당했다고해도 계속해서 참조카운트가 남아있게되고 메모리를 잡고있습니다. 따라서 약한 참조를 도입합니다.
class Blog {
let name: String
let url: URL
weak var owner: Blogger?
init(name: String, url: URL) { self.name = name; self.url = url }
deinit {
print("Blog \\(name) is being deinitialized")
}
}
class Blogger {
let name: String
var blog: Blog?
init(name: String) { self.name = name }
deinit {
print("Blogger \\(name) is being deinitialized")
}
}
var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")
blog!.owner = blogger
blogger!.blog = blog
blog = nil
blogger = nil
// Blogger Antoine van der Lee is being deinitialized
// Blog SwiftLee is being deinitialized
weak var owner: Blogger?
이렇게 하므로서 blog 와 blogger에 nil 을 넣어주면 deinit이 되는것을 확인할 수 있습니다.
다음은 blog에 Post 구조체를 도입하겠습니다
struct Post {
let title: String
var isPublished: Bool = false
init(title: String) { self.title = title }
}
class Blog {
let name: String
let url: URL
weak var owner: Blogger?
var publishedPosts: [Post] = []
init(name: String, url: URL) { self.name = name; self.url = url }
deinit {
print("Blog \\(name) is being deinitialized")
}
func publish(post: Post) {
/// Faking a network request with this delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.publishedPosts.append(post)
print("Published post count is now: \\(self.publishedPosts.count)")
}
}
}
var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")
blog!.owner = blogger
blogger!.blog = blog
blog!.publish(post: Post(title: "Explaining weak and unowned self"))
blog = nil
blogger = nil
출력
// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: 1
// Blog SwiftLee is being deinitialized
publish메소드에 DispatchQueue.main.asyncAfter 를 통해 약간의 딜레이를 주었습니다. 네트워크 요청을 가정한 딜레이 입니다.
출력된 로그를 보면 blog가 deinit되기전에 DispatchQueue.main.asyncAfter 내부 코드가 실행된것을 알 수 있습니다. 즉, publishedPosts 배열에 post가 하나 들어가게 됩니다. blog에는 nil을 할당하여 메모리 해지를 했는데도 말이죠.
그럼 이를 수정해보겠습니다 weak self 를 통해서 말이죠.
func publish(post: Post) {
/// Faking a network request with this delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.publishedPosts.append(post)
print("Published post count is now: \\(self?.publishedPosts.count)")
}
}
출력
// Blogger Antoine van der Lee is being deinitialized
// Blog SwiftLee is being deinitialized
// Published post count is now: nil
publish 요청이 완료되기 전에 blog에 nil이 할당되어 메모리해지 되었습니다.
게시된 게시물의 로컬 상태를 업데이트 할 수 없습니다. “Published post count is now: nil” 이 나온 이유입니다.
결론
클로저가 실행되는 즉시 참조 인스턴스르 수행할 작업이 있는 경우weak self를 사용하지 않도록 해야 합니다.
클로저에서 Weak references 유지 주기
class Blog {
let name: String
let url: URL
weak var owner: Blogger?
var publishedPosts: [Post] = []
var onPublish: ((_ post: Post) -> Void)?
init(name: String, url: URL) {
self.name = name
self.url = url
// Adding a closure instead to handle published posts
onPublish = { post in
self.publishedPosts.append(post)
print("Published post count is now: \\(self.publishedPosts.count)")
}
}
deinit {
print("Blog \\(name) is being deinitialized")
}
func publish(post: Post) {
/// Faking a network request with this delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.onPublish?(post)
}
}
}
var blog: Blog? = Blog(name: "SwiftLee", url: URL(string: "www.avanderlee.com")!)
var blogger: Blogger? = Blogger(name: "Antoine van der Lee")
blog!.owner = blogger
blogger!.blog = blog
blog!.publish(post: Post(title: "Explaining weak and unowned self"))
blog = nil
blogger = nil
출력
// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: 1
blog 에 nil 이 할당되었습니다.
따라서 print("Blog \(name) is being deinitialized") 문구가 출력되어야 하지만 나오지 않습니다.
하지만 blogger에 nil 할당했고 정상적으로 메모리 해지 된것을 확인할 수 있습니다.
이 상황에서 weak self 를 도입하면 참조 유지되는 현상이 해결됩니다.
onPublish = { [weak self] post in
self?.publishedPosts.append(post)
print("Published post count is now: \\(self?.publishedPosts.count)")
}
출력
// Blogger Antoine van der Lee is being deinitialized
// Published post count is now: Optional(1)
// Blog SwiftLee is being deinitialized
참고 :
'Development > iOS 개발' 카테고리의 다른 글
associatedtype 이란? (0) | 2022.06.17 |
---|---|
swift) required init 요구 이니셜라이저 (0) | 2022.06.17 |
xcode 13 vim Editing Mode (0) | 2021.10.13 |
iOS13,14,15 네비게이션 바 투명 상태 처리 (0) | 2021.09.23 |
[SwiftUI] NavigationView 와 NavigationLink 정리 (0) | 2021.03.02 |