iOS 공부하는 감자

iOS) IBOutlet연결 Strong VS Weak 본문

iOS

iOS) IBOutlet연결 Strong VS Weak

DongTaTo 2022. 7. 7. 20:34
반응형

스토리보드에서 작업한 UI객체를 코드에 연결하기 위해서는 IBOutlet 또는 IBAction을 사용하여 연결해야 한다.

IB는 Interface Builder의 줄임말입니다.

 

 

스토리보드에서 control + 드래그를 사용하여 코드에 연결하면 Default값은 Weak가 설정되어 있음을 확인할 수 있는데..

 

Strong을 사용하여 연결하면 안되는가?? 에 대한 궁금증을 해결하기 위해 정보를 찾아보았다..!!

 

 


 

UI 요소들의 참조 구조(?)

우선, ViewController가 메모리에 올라왔을 때 내부적으로 어떻게 참조(Reference)가 발생하고 있는지에 대한 이해가 필요하다.

 

ViewController가 Heap메모리에 올라온 상황을 글쓴이가 앞발로 그려보았다. 

위쪽은 IBOutlet을 연결할 때, Strong 참조를 한 상황이고,

아래쪽은 Weak 참조를 한 상황이다.

 

 

 

 

 

 

VC : ViewController

 

UIView : VC에 기본으로 달려있는 View객체, 화면을 구성하기 위한 UI 요소들은 이 View 위에 올라온다.

 

뷰객체들 : View위에 올라오는 UI 요소들 (UILabel, UIButton 등...)

 

RC : Reference Count 

 

 

 

 

 

 

 

 

 

 

편한 글 작성을 위해 VC내부에 자동 생성(?)되는 UIView는 "View"라고 부르겠습니다.

 

 

좀 주저리주저리 설명을 붙여보자면..

 

모든 ViewController는 자신이 관리하는 View에 대한 강한 참조를 유지한다.

ViewController가 존재하는 한 View는 없어지면 안 되기 때문이다. (View위에 뷰객체들이 올라와서 화면을 구성하기 때문)

UIViewController의 정의 부분.. open var view: UIView!를 확인할 수 있다. (강한참조!)

 

View는 자신의 하위 뷰객체에 대한 강한 참조를 유지한다.

(== 화면을 구성하기 위해 개발자가 View에 올린 뷰객체들은 View의 강한 참조로 인해 기본적으로 ReferenceCount가 1 증가한다.)

 

뷰객체들은 ViewController의 인스턴스 내부에 저장되는 것이 아니다.

각 뷰객체의 인스턴스는 클래스로써 힙영역에 각자 저장되고, IBOutlet으로 ViewController와 연결되면

ViewController는 Outlet 변수로 각 뷰객체의 참조를 저장한다.

 

 

 

요걸 이제 종합해보면 IBOutlet으로 뷰객체를 연결할 때,

 

1. Strong으로 뷰객체를 참조한다면?

  • VC는 View를 강하게 참조한다      ⇨ View의 RC: 1
  • View는 뷰객체를 강하게 참조한다 ⇨ 뷰객체의 RC: 1
  • VC는 뷰객체를 강하게 참조한다    ⇨ 뷰객체의 RC: 2

 

2. Weak로 뷰객체를 참조한다면?

  • VC는 View를 강하게 참조한다      ⇨ View의 RC: 1
  • View는 뷰객체를 강하게 참조한다 ⇨ 뷰객체의 RC: 1
  • VC는 뷰객체를 약하게 참조한다    ⇨ 뷰객체의 RC: 1 (변동 X)

 

 

 


 

강한 참조로 인해 메모리 누수가 발생하는가?

메모리 공부를 해봤다면 Strong참조를 했을 때, 어딘가 모를 메모리 누수에 대한 염려가 생긴다. (아마도..?)

 

Storng하게 뷰객체를 참조한 상황으로 메모리 누수가 발생하는지 알아보쟈..

 

우선 결론부터 이야기하자면 메모리 누수가 발생할 수도 있고, 아닐 수도 있다. 😵‍💫?

 

 

 

1) 메모리 누수가 발생하지 않는 경우

ViewController가 정상적으로 메모리에서 해제되는 경우 메모리 누수가 발생하지 않는다.

  • VC가 메모리에서 해제되면서 View와 뷰객체에 대한 참조를 잃는다 ⇨ View의 RC: 0, 뷰객체의 RC: 1
  • View가 메모리에서 해제되면서 뷰객체에 대한 참조를 읽는다 ⇨ 뷰객체의 RC: 0

 

결론적으로 ViewController, View, 뷰객체 모두 RC가 0이 되어 메모리에서 잘 해제된다.

 

그럼 어떤 상황에서 메모리 누수가 발생할 수 있는가???

 

 

2) 메모리 누수가 발생하는 경우

앱을 사용하다가 메모리가 부족해지는 상황에서 메모리 누수가 발생한다.

 

메모리가 부족해지면 ViewController는 "didReceiveMemoryWarning()"라는 인스턴스 메서드를 호출하는데

이 메서드는 부족한 메모리를 확보하기 위해 VC 내부의 View를 nil 처리한다. (지워버린다.)View를 메모리에서 해제시키면서 View가 강하게 참조하는 뷰객체들도 메모리에서 해제시키기 위함이다!

 

didReceiveMemoryWarning 추가 설명 :
메모리 워닝이 발생하면 리소스 관리를 위해 view가 제거되고, 필요할 때 다시 생성되고를 반복한다.
ViewController의 생명주기 중 loadView와 viewDidLoad에서 말하는 view가 여기서 제거와 해제를 반복하는 view 이다.
즉, VC가 메모리에서 완전히 해제되어야만 loadView, viewDidLoad함수가 재호출 되는 건 아니고, 리소스 관리를 위해 View가 메모리에서 해제되었었다면 화면을 다시 띄우기 위해 위 생명주기 함수들이 재호출 된다.

 

 

그런데 리소스 관리를 위해 View가 메모리에서 해제되고, 참조하던 뷰객체들의 RC가 줄어들었지만..ViewController가 뷰객체들을 강하게 참조하고 있기 때문에 뷰객체들의 RC가 0으로 내려가지 못하고, 메모리에서 해제되지 않는다.

 

결국 메모리를 관리하기 위해 View를 해제시켰음에도 뷰객체들은 여전히 메모리에 남아서 자리를 차지(?)하는 문제가 발생할 수 있다.

 

 

 

 


 

 

Strong VS Weak

weak은 기본값이니까.. Strong을 사용하면 좋은 상황들을 알아보면 좋을 것 같다..!!

 

1) ViewController가 메모리에 남아있는 한 뷰객체가 해제되지 않기를 원할 때

뷰객체를 약하게(weak) 참조하면 didReceiveMemoryWarning() 메서드가 호출되어 View가 메모리에서 해제되었을 때 뷰객체들도 메모리에서 해제된다.

만약 이 상황에서 ViewController가 뷰객체에 접근한다면 런타임에러가 발생할 수 있다. (뷰객체는 메모리에서 해제되어 nil인데, VC에서는 뷰객체를 IUO로 두고 사용하기 때문)

 

 

2) 뷰객체가 메모리에서 해제되는 시점을 제어하고 싶을 때

뷰객체를 강하게 참조하면 ViewController가 메모리에서 해제되지 않는 한 뷰객체도 메모리에서 해제되지 않는다.

 

 

3) 복잡한 뷰객체 계층구조를 가진 경우

복잡하고 깊은 계층구조를 가진 화면에서 중간 계층에 있는 뷰객체가 갑자기 메모리에서 해제된다면, 해제된 뷰객체 하위의 요소들도 모두 메모리에서 해제될 수 있다. (약한 참조를 했다면)이럴 경우 의도치 않게 런타임에서 nil로 인한 에러가 발생할 수 있다.가능성이 높지는 않지만.. 아예 불가능한 상황은 아닌 것 같다..(?)

 

 

 

 


 

 

IBOutletCollection은 왜 Weak로 선언되지 않는가?

IBOutletCollection은 배열에 뷰객체들의 참조를 저장한다.

배열은 구조체로 만들어져 있고, 구조체는 값타입으로 참조 개념이 없다.

// 'weak' may only be applied to class and class-bound protocol types, not '[UITextField]'
@IBOutlet weak var textFields: [UITextField]!

 

IBOutletCollection을 사용하여 뷰객체를 배열에 담으면 RC는 증가한다.

CFGetRetainCount()메서드를 통해 객체의 ReferenceCount를 찍어보면

배열에 객체를 넣은 후, ReferenceCount가 증가함을 알 수 있다.

var someObject = UIView()

CFGetRetainCount(someObject)       // 2

var array = [someObject]

CFGetRetainCount(someObject)       // 3

 

 

 

 

 

 

 

 

 


참고

https://www.zerotoappstore.com/should-iboutlets-be-weak-or-strong.html

 

Should IBOutlets be weak or strong? - Zero To App Store

Eddy Chung I teach iOS development on ZeroToAppStore.com.

www.zerotoappstore.com

https://cocoacasts.com/should-outlets-be-weak-or-strong

 

Should Outlets Be Weak or Strong

Should outlets be declared weak or strong? It’s a question that pops up surprisingly frequently. In this tutorial, I’d like to definitively answer this question. I also explain the why of the answer.

cocoacasts.com

http://monibu1548.github.io/2018/05/03/iboutlet-strong-weak/

 

Interface Builder IBOutlet연결에 Strong과 Weak 어떤것을 써야할까? - JingyuJung's Blog

IBOutlet의 Strong vs Weak Interface Builder를 사용하는 프로젝트에서 View를 코드상에서 제어하기 위해 IBOutlet으로 스토리보드 <-> 코드 를 연결하게 됩니다. Ctrl키를 누르고 View를 .m 또는 .h 파일로 가져오

monibu1548.github.io

 

 

 

 

반응형