-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ARC in Swift: Basics and beyond #2
Comments
들어가며
Swift는 ARC(Automatic Reference Counting)를 통해서 메모리를 관리해줄거예요! 이번 세션에서는 object lifetime를 보고 ARC에 대해서 다룹니다. 그리고 observable object lifetime이 무엇인지 알아봅시다. Object’s lifetime
Example) Travel appclass Traveler {
var name: String
var destination: String?
}
func test() {
let traveler1 = Traveler(name: "Lily")
let traveler2 = traveler1
traveler2.destination = "Big Sur"
print("Done traveling")
} Traveler 객체를 자동으로 메모리 관리를 해주기 위해서 Swift compiler는
를 삽입해줍니다. 1) traveler1 객체의 life cycle
💡 retain operation를 reference가 시작됐을 때 넣지 않은 이유는 객체 초기화가 reference count를 1로 설정했기 때문입니다. 2) traveler2 객체의 life cycle
3) test 함수의 Runtime Flow
📗 Swift에서 객체의 수명은 use-based 입니다. 객체의 보장된 최소 수명은 initalization 시에 시작되고 마지막 사용이 끝나면 해제된다는 겁니다. 방금 예시에서 봤듯 객체가 마지막 사용 후에 즉시 할당 해제됩니다. 하지만, 실제로 객체의 수명은 Swift compiler에 의해 삽입되는 몇몇 경우에서 객체가 마지막 사용하고 나서 그 후에 할당이 해제됩니다. 💡 대부분의 경우는 object의 정확한 수명이 얼마인지는 별로 중요하지 않습니다. Observable object lifetimes
Relying on observed object lifetimes causes bugs.
Example) Travel app with reference countclass Traveler {
var name: String
var account: Account?
func printSummary() {
if let account = account {
print("\(name) has \(account.points) points")
}
}
}
class Account {
var traveler: Traveler
var points: Int
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
traveler.printSummary()
}
1) test 함수의 Runtime Flow
아마 이런 reference cycle를 weak, unowned reference를 사용해서 깰 수도 있습니다. why?
Example) Travel app with Weak referenceclass Traveler {
var name: String
var account: Account?
func printSummary() {
if let account = account {
print("\(name) has \(account.points) points")
}
}
}
class Account {
weak var traveler: Traveler?
var points: Int
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
traveler.printSummary()
}
Traveler 객체가 사라지고나면, 💡 Account 객체가 할당 해제된다! 우리는 weak reference를 reference cycle를 깨는데만 사용을 했다. 하지만,
💡 관련 없는 이유로 관찰된 객체 수명이 변경되면 버그가 발생할 수 있다. Example) Travel app with printSummary move to Account Classvar name: String
var account: Account?
}
class Account {
weak var traveler: Traveler?
var points: Int
func printSummary() {
print("\(traveler!.name) has \(points) points")
}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
account.printSummary()
}
Weak and unowned references
Example) Safe Techniques for handling weak references - withExtendedLifetime()func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
withExtendedLifetime(traveler) {
account.printSummary()
}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
account.printSummary()
withExtendedLifetime(traveler) {}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
defer {withExtendedLifetime(traveler) {}}
traveler.account = account
account.printSummary()
} 1)
2) 기존 영역의 맨 끝에 있는 3) 더 복잡한 경우에는 compiler에게 defer를 사용해서 현재 영역의 끝까지 객체의 수명을 연장하도록 요청할 수 있습니다. 🎁 withExtendedLifetime()은 객체 수명 오류에서 벗어나는 방법으로 보여요.하지만, 이 기술은 취약하기 때문에 정확성에 대한 책임을 당신에게 떠넘깁니다. 제대로 컨트롤하지 않으면 Redesign to access via strong reference객체에 대한 참조를 strong reference로 국한하는 경우, object lifetime surprise를 막을 수 있습니다. In example,
reference cycle은 알고리즘을 다시 생각하고 순환 클래스 관계를 트리 구조로 변환함으로써 피할 수 있습니다. In our example,
Deinitalizer side-effects
Example) Deinitalizer Exampleclass Traveler {
var name: String
var destination: String?
deinit {
print("\(name) is deinitializing")
}
}
func test() {
let traveler1 = Traveler(name: "Lily")
let traveler2 = traveler1
traveler2.destination = "Big Sur"
print("Done traveling")
}
💡 이 예시에서 deinitalizer의 부작용은 관찰이 되었지만 의존하지는 않았습니다. Example) Travel app: TravelMetricsclass Traveler {
var name: String
var id: UInt
var destination: String?
var travelMetrics: TravelMetrics
// Update destination and record travelMetrics
func updateDestination(_ destination: String) {
self.destination = destination
travelMetrics.destinations.append(self.destination)
}
// Publish computed metrics
deinit {
travelMetrics.publish()
}
}
class TravelMetrics {
let id: UInt
var destinations = [String]()
var category: String?
// Finds the most interested travel category based on recorded destinations
func computeTravelInterest()
// Publishes id, destinations.count and travel interest category
func publish()
}
func test() {
let traveler = Traveler(name: "Lily", id: 1)
let metrics = traveler.travelMetrics
...
traveler.updateDestination("Big Sur")
...
traveler.updateDestination("Catalina")
metrics.computeTravelInterest()
}
verifyGlobalTravelMetrics()
언제든지 destination이 업데이트되면, TravelMetrics class에 기록이 됩니다. 결국 Traveler 객체를 deinitalize할 때 metrics이 global record에 게시됩니다. travelMetrics.publish() 게시된 Metrics는 traveler의 익명 ID, 조회된 destination 수 및 계산된 여행 범주입니다. class TravelMetrics {
let id: UInt
var destinations = [String]()
var category: String?
}
withExtendedLifetime()** 1)
2)
3)
🎁 객체 수명을 관찰할 수 있는 Swift의 특성을 이해하고 observed object lifetime에 대한 잠재적으로 잘못된 의존을 제거하여 갑자기 버그를 발견하지 않도록 하는 것이 중요합니다. Enable powerful lifetime shortening optimizationXcode 13부터는 “Optimize Object Lifetimes”라는 새로운 experimental build setting을 Swift compiler로부터 사용할 수 있게 되었습니다.
🌈 세션에서 얘기했던 안전한 기술들을 사용해서 이러한 버그들을 모두 제거할 수 있습니다! ❤️ |
ARC in Swift
The text was updated successfully, but these errors were encountered: