개발/iOS

ARC in Swift: Basics and beyond

빙수킹 2021. 9. 1. 20:05

 

swift는 struct와 enum같은 강력한 value type을 제공한다.

보통은 value type을 사용한다. reference type의 unintended sharing(의도하지 않은 공유)를 피하기 위해서.

 

class를 사용한다면, 이것의 메모리는 ARC에 의해 관리된다.

 

이제 객체의 lifetime이 어떻게 관리되는지 보자.

 

Object lifetimes and ARC

 

Object의 생명주기는 initialization으로 시작되고, 마지막 사용으로 끝난다.

ARC는 object의 생명주기가 끝나고 dealloc(해제) 한다.

ARC는 객체의 reference count를 계속 track해서 객체의 lifecycle을 결정한다.

ARC는 Swift compiler에 의해 retain과 release를 넣는 방식으로 동작한다.

런타임에 retain은 reference count를 증가시키고 release는 감소시킨다.

reference count가 0이 되면 객체가 deallocated된다.

 

 

 

example을 보자.

 

traveler1을 보자.

traveler1은 참조가 다음 사진처럼 시작되고, 끝난다.

 

그리고 실제 컴파일러가 코드를 넣으면 아래처럼 된다. (init에서 begin, 마지막 사용 시 end)

컴파일러는 retain을 넣지는 않는다.

왜냐면 init으로 만들어진 객체는 reference count가 1이 되었기 떄문이다.

 

 

이번에는 traveler2를 보자.

아래 사진처럼 시작되고, 끝난다.

 

이 때 retain과 release 코드는 다음과 같이 들어간다.

 

 

그리고 Traveler object에 대해 보면,, 

init에서 1이고 traveler2에 할당되었을 때 2가 되고

traveler1의 마지막 사용이므로 1이 되고, traveler2의 마지막 사용 때 0이 된다.

그리고 object는 deallocated된다.

reference count가 0이 되면 object가 deallocated될 수 있다.

 

swift의 object lifetime은 use-based하다. 

lifecycle은 init에서 시작되고 마지막 사용에서 끝나는게 보장된다.

이게 C++같은 언어랑 다른점이다. C++는 closing brace에서 lifecycle이 끝나는게 보장된다.

하지만!!!!

실제로는, 그들의 최소 보장(마지막 사용에서 release)과 다르게, 마지막 사용 "이후"에 lifecycle이 끝날 수 있다.

아래처럼..

 

 

대부분의 경우는 정확히 어디서 해제되는지.. 그런게 별로 상관없지만, 아래같은 경우에는 아닐 수 있다.

 

 

Observable object lifetimes

 

 

그리고 프로그램이 guaranteed object lifetime(최소조건)이 아닌 observed object lifetime(실제로 행해지는 lifecycle)에 의존하고 있으면 문제가 생길 수 있다!! 지금은 잘 되지만 .. 이건 우연에 불과하다. 나중에 Swift Compiler가 변경되면 이건 문제가 생길 수 있다.

 

 

lifetime observable하게 만드는 Language features에 대해 알아보자

-> 1) Weak & unowned 사용할 때, 2) Deinit 사용할 때 문제점과 해결방안!!!

그리고 안전하게 하는 법에 대해 알아보자.

 

 

 

1) Weak and unowned references

weak unowned reference count를 증가시키지 않음. 그래서 reference cycle을 해결하는데 보통 사용된다.

 

 

일단 reference cycle이 무엇인가 알아보자.

요런상황임. 

이 코드에서 마지막에 각각 reference count1로 남고.. 둘다 해제되지 않는다. 메모리 릭을 일으킨다.

 

 

weak & unowned를 사용하면 이 상황을 해결할 수 있다.

object가 해제되었을 때, weak에 접근하면 nil을 리턴하고, unowned에 접근하면 trap(런타임 에러 말하는듯?)이 일어난다.

 

weak나 unowned를 사용하면 reference cycle을 없애주지만, observed object lifetime(실제로 행해지는 lifecycle)에 의존하면 나중에 문제가 생길 수 있다.

 

 

예시를 보자.

아래 코드에서 traveler이 weak한 프로퍼티이다.

지금은 account.printSummary()가 잘 동작한다.

하지만, guaranteed object lifetime(최소조건)을 만족한다면 이건 동작하지 않을것이다.

마지막 use가 그 전줄이기 때문에 traveler가 이미 해제되었을 것이다.. 

 

그러면 printSummary()에서 traveler!.name에서 crash가 일어날 것이다.

물론 optional binding을 사용하면 crash가 나진 않겠지만, optional binding을 하는것은 bug worsen하는거지 해결하는것은 아니다. (silent bug라고 표현한다)

 

 

자 여기 해결방안들이 있다. 3가지!!!

1) - 1) Weak and unowned references - Safe techniques

 

 

 

1번 방법: withExtendedLifetime()

-> 이걸 쓰면 lifetime을 이떄까지 늘려준다. (아래 3가지 다 똑같음.. 그냥 방식차이)

이 방법은 좋아보이지만. fragile(부서지기 쉬운) 하다.

내가 수정의 책임을 가지게 된다. 그리고 매번 모든코드에 이걸 써줘야한다. 유지보수 손해..

별로임.

 

 

 

2번 방법: Redesign to access via strong reference

private로 외부 접근을 방지. 모두 로컬이면(private) side effects가 생기지 않을것이다.

 

 

 

3번쨰 설계를 똑바로하는 방법

 

Before

 

After!!

 

 

다음 문제..

 

2) Deinitializer side-effects

 

 

Deinit은 무엇인가?

 

아래 example에서 현재는

Done Traveling 다음에 deinit이 불리지만 arc에 의하면 나중에 그 위로 바뀔수도 있다.

 

이제 다른 예제.

 

 

현재는 computeTravelInereset 다음에 deinit이 불리겠지만,

실제 lifecycle은 그 위까지다.

 

 

이번에도 해결방법을 알아보자.

 

2) - 2) Deinitializer side-effects - Safe techniques

 

예를 들어 보자.

 

예1)

 

예2)

computeTravleInterest()전에 deinit이 불려서 nil이 publish 될 것이다.

 

 

해결방안들..

 

 

해결 방안 1

자 아까랑 똑같슈. 첫번째 별로임.

유지보수 cost 증가.

 

 

해결방안 2

아까랑 똑같다.. private 하게 숨기기.

 

 

3번째 - 설계 똑바로 하기

 

 

근데 님들!!! 지금까지 이 해결방안을 알려준 이유가 있어요..

xcode 13에 생긴 새로운 기능을 소개합니다!!! 객체 생명주기 최적화 기능

이걸 키면 스위프트 컴파일러가 제때(마지막 사용 시) 메모리를 해제할 확률이 높아진다.

그러니까 이 기능은.. object가 위에서 계속 언급된 guaranteed object lifetime(최소조건)를 만족할 확률을 올려주는 옵션이다.

 

 

 

출처

https://developer.apple.com/videos/play/wwdc2021/10216/

 

ARC in Swift: Basics and beyond - WWDC21 - Videos - Apple Developer

Learn about the basics of object lifetimes and ARC in Swift. Dive deep into what language features make object lifetimes observable,...

developer.apple.com