BOID

[swift] 강한참조, 강한참조 순환, 약한참조, 미소유참조- HoonIOS 본문

swift 시작기

[swift] 강한참조, 강한참조 순환, 약한참조, 미소유참조- HoonIOS

HoonIOS 2021. 3. 30. 14:21

안녕하세요 HoonIOS입니다. :)

저번 시간에는 ARC의 개념에 대해 간단히 포스팅했습니다. 이번에는 ARC의 참조 관계의 종류에 대해 포스팅을 하겠습니다.

 

ARC와 강한참조의 관계

- 강한참조란 인스턴스가 계속해서 메모리에 남아있어야 하는 명분을 만들어주는 것입니다

* 강한 참조의 특징 

  • 인스턴스의 메모리참조가 0이 되면 메모리가 해체되는데, 인스턴스를 다른 인스턴스의 변수, 상수등에 할당할 때마다 강한 참조가 사용되 참조횟수가 1이 증가하고,
  • 강한참조를 사용하고 있는 변수, 상수에 nil값을 주면 인스턴스의 참조횟수가 1이 감소합니다.

- 아래 예시는 인스턴스를 처음 변수에 넣어 메모리에 생성 후 hoon2, hoon3에 할당해주는 것입니다.

강한 참조

- 왼쪽의 예시는 혹시 hoon1에만 이니셜 라이저로 인스턴스를 생성해줘 hoon1에만 nil값을 넣어 메모리를 해제해주면 안 될까 하고 hoon1에만 초기화해줬지만 어림도 없이 인스턴스 참조 횟수가 0이 되지 않았으므로 deinit 메소드(메모리 해체 시 발생하는 메소드)가 실행되지 않습니다.

- 오른쪽 사진은 인스턴스를 참조하던 변수를 순서대로 nil로 초기화를 해줘서 인스턴스 참조 횟수가 0이 되어야 deinit이 뜨는 것을 볼 수 있습니다.

- 위 예시를 보면 결과적으로 참조 횟수가 0이 되는 순간 인스턴스는 ARC의 규칙에 의해 메모리에서 해제되며 메모리에서 해제되기 직전에 deinit을 호출합니다.

 

- 이번에는 함수 내부에서의 인스턴스 참조 생성, 소멸에 대해 알아보겠습니다.

함수 이니셜라이저

- 함수 내부에 Student타입의 인스턴스를 이니셜 라이저로 생성 후 hoon상수에 할당할 때 인스턴스 참조 횟수가 1이 되고 지역변수인 hoon이 사용된 블록( { , } )의 코드 실행이 종료되면 인스턴스 참조가 0이 되어 deinit을 호출을 합니다.

 

- 만약에 함수 내부에 지역변수에 인스턴스를 생성한 게 아니라 인스턴스가 함수 밖인 전역 변수에 강한 참조를 하게 되면 1이 증가하게 되는데, 이경우 함수가 종료되어 전역변수 이므로 여전히 메모리에서 해제가 되지 않습니다. 

 

이번에는 인스턴스끼리 서로서로 강한 참조를 할때 생길수 있는 강한참조 순환에 대해 이야기를 해보겠습니다.

 

강한참조 순환

- 강한참조 순환의 안 좋은 예시를 한번 봐보겠습니다.

  1. - 각 Kim, room변수에 Student, School 클래스의 이니셜 라이저를 참조해줘 참조 횟수는 각각 1이 됩니다.
    인스턴스 참조 횟수: Student: 1, School: 1
  2. School 클래스 인스턴스의 저장 프로퍼티인 Lee에 Student클래스 인스턴스인 Kim변수를 할당해줘 Student클래스의 참조 횟수는 2가 됩니다.
    인스턴스 참조 횟수: Student: 2, School: 1 
  3. Kim이 참조하는 Student클래스의 저장 프로퍼인 room에 School 클래스가 참조하므로 School 클래스의 참조 횟수는 2가 됩니다.
    인스턴스 참조 횟수: Student: 2, School: 2
  4. Student클래스를 할당받은 Kim에 nil값을 주어 해체를 해주면 Student 인스턴스 참조는 1이 됩니다.
    인스턴스 참조 횟수: Student: 1, School: 2
  5. 근데 여기서 만약 School 클래스를 할당받은 room에 nil값을 주어 해체를 해주면 이제 room? 이 참조하던 School 클래스의 인스턴스에 접근할 방법도 Kim변수가 참조하던 Student클래스에 접근할 방법도 사라지게 되어 메모리가 해체되지 않아 메모리 누수가 발생을 하게 됩니다.
  6. 결과적으로 인스턴스 참조 횟수를 총 0으로 만들지 못한 결과가 나오게 됩니다. 

이렇게 해체해야 할 프로퍼티가 너무 많거나 깜박한 코드가 있기 마련입니다, 그럴 때는 약한 참조와 미소유 참조를 쓰는 게 편합니다.

 

약한 참조란?

- 약한 참조의 특징은 강한 참조와 달리 자신이 참조하는 인스턴스의 참조 횟수를 증가시키지 않습니다.

- 약한 참조의 형식은 참조 타입의 인스턴스나 변수의 선언 앞에 weak 키워드를 써주면 그 프로퍼티나 변수는 자신이 참조하는 인스턴스를 약한 참조를 합니다.

- 약한 참조를 사용하면 자신이 참조하는 인스턴스가 해제될 수 있다는 것을 예상해볼 수 있어야 합니다.

- 인스턴스 메모리를 해체하려면 nil을 할당할 수 있어야하고 옵셔널을 할당할수 있어야 하므로 약한 참조는 상수에 쓰일 수 없습니다.

약한참조의 예시

- 이 예제는 앞에서 참조 순환에서 쓰였던 예시에서 School 클래스의 Lee프로퍼티에 약한 참조를 하도록 weak 키워드를 추가해 정의를 해준 것입니다.

- 그 결과 room?.Lee = Kim에서 Lee는 약한 참조로 선언이 되었으므로 참조 횟수를 증가시키지 않습니다.

 

* 위 예제에서 눈여겨볼 두 가지가 있습니다.

  1.  Kim = nil 할당한 부분은 Student 클래스에 nil을 해주어 deinit을 해줬는데 자신의 프로퍼티가 강한 참조하던 School 클래스의 인스턴스 참조 횟수를 1 감소시키는 것을 볼 수 있습니다.
    이것은 Kim 인스턴스 부분이 deinit이 되었기 때문에 이를 참조하던 Kim?. room = room 참조하는 부분이 해제가 되기 때문에 참조가 1 감소하는 것입니다.
  2.  Kim 인스턴스가 해제가 되었다는 것은 room?. Lee의 약한 참조 프로퍼티의 참조하는 인스턴스가 메모리에서 해제가 되었다는 말입니다. 또 약한 참조였기 때문에 인스턴스가 메모리에서 해제되면 자동으로 nil값을 받는 것을 알 수 있습니다. 
    이것을 확인해보기 위해 print("\(room?. Lee))를 찍어보면 결과 값은 nil을 출력합니다.

미소유 참조

- 약한 참조와 마찬가지로 미소유 참조는 인스턴스의 참조 횟수를 증가시키지 않습니다.

- 단 다른 점은 미소유 참조는 약한 참조와 다르게 자신이 참조하는 인스턴스가 항상 메모리에 존재할 거라는 전제를 두고 동작을 합니다.

- 미소유 참조를 하는 변수나 프로퍼티는 옵셔널이나 변수가 아니어도 됩니다. 그 이유는 자신이 참조하는 인스턴스가 메모리에서 해체돼도 스스로 nil을 할당해주지 않습니다.

 

* 단! 미소유 참조를 하면서 메모리에서 해제된 인스턴스를 접근하려고 하면 런타임 오류와 함께 강제 종류가 됩니다.!

따!!!!!!라!!!!!!!!!!!서!!!!!!!!! 미소유 참조는 항상 해당 인스턴스 메모리에서 해제되지 않는다는 확신이 있을 때만 사용해야 합니다.

 

- 형식은 참조 타입의 변수나 프로퍼티의 정의 앞에 unowned 키워드를 써주면 그 변수(상수), 프로퍼티는 자신이 참조하는 인스턴스를 미오유 참조하게 됩니다.

 

미소유 참조

- if 조건절을 보면은 CreditCard 클래스 이니셜 라이즈를 통해 인스턴스를 생성을 해줘 Credit 인스턴스 참조를 1 증가시켜주었지만, CreditCard 클래스의 owner는 미소유 참조로 선언을 해주었기 때문에 Person참조 횟수가 증가하지 않습니다.

 

ARC의 참조 종류에 대해 알아보았습니다... 공부를 하면서 새로 안 부분이고 중요하게 생각한 부분이라 중간에 까먹지 않게 계속해서 공부를 해주는 게 중요할 거 같습니다.

 

 

 

반응형
Comments