-> 일반적으로 프로그래밍언어에서는 객체를 생성하게되면은 메모리에 로드되게된다, 하지만 무지성으로 메모리에 자원을 로드하게되면은 메모리는 무한하지않기때문에 메모리누수가 일어날 수도 있다, 그래서 이 메모리관리라는것은 되게 중요한것인데 Swift에서는 ARC라는것으로 메모리를 관리하게된다, 이에 대해 알아보자!!!
ARC는 Automatic Reference Count의 약자로 말그대로 어떤것을 참조하고있는지 자동으로 카운트해주는 역할을 하는것인데 일반적으로 Strong Reference일 경우에 레퍼런스 카운트가 1 증가한다
-> 우리가 보통 객체를 생성하는것 자체가 Strong Reference이다, 나는 코드로 개발을 하다보니 몰랐지만 스토리보드로 만약에 개발을 하게되면은 스토리보드에 있는 UI를 코드로 끌고올때 Strong이라고 써있는 부분이 그것이다
-> 위 그림처럼 보통 클래스의 객체를 생성하게되면은 ARC가 1증가한다 (스택에 저장되어있는 strongClass변수가 힙에 저장되어있는 인스턴스를 가리키고있으니까 ㅇㅇ) 그런데 여기서 strongClass에 nil을 넣었더니 콘솔창에 deinit을 띄운다, 즉 ARC는 자신이 카운트가 하고있는 숫자가 0이 될 경우에 객체를 deinit시킨다는것이다 !!!
-> 이는 말 그대로 계속 뺑뺑돈다는것이다, 쉽게 말해서 앞서 strongClass에 대해 nil을 넣어줬더니 deinit이 되었지만 이와는 다르게 서로가 서로를 가리키고있어서 저놈이 언제 인스턴스를 해제시킬까하고 대기중인 상황인것이다
-> 혹은 escaping closure안에서 self를 통해서 자기자신의 전역변수를 클로저안에서 참조하고있고 클로저의 특성중 캡처하는것이 있어서 클로저안에서 이 클래스가 deinit되더라도 레퍼런스가 살아있는것이다, 그래서 ARC가 줄어들지않게되고 그렇게되면 계속 메모리에 로드되있어 메모리누수까지 발생하는것이다
-> Swift에서는 이 Retain Cycle 문제를 해결할 수 있는 2가지 방법을 제시한다, 첫번째는 weak, 두번째는 unowned이다
-> 저 둘을 이용하여 프로퍼티를 선언하게되면 "저는 deallocated할때 방해하지않는 프로퍼티입니다!!"하고 말해주는것이라 생각하면 편하다
-> 위 코드를 보면은 testClass프로퍼티를 weak로 선언해준다, 그렇기때문에 사실 weak라는 키워드로 선언해주지않으면 각각의 인스턴스내부에서도 서로를 가리키는 상황이되어 RetainCycle문제가 발생한다, 그래서 이때 weak를 씀으로써 weak하게 선언해주면 ARC는 "어? 여기에 이 놈을 참조하고있는놈이 아직도 있네? 근데 weak로 선언했으니 애네들은 ARC를 증가하지않아도 되겠구나" 이런 느낌으로 생각하는것이다
-> unowned도 weak처럼 선언해주면 똑같이 해결가능하다, 하지만 차이점 또한 있다, weak같은 경우는 nil을 허용한다 그렇기때문에 되게 안전하다, 왜냐면 만일 우리가 아무 인스턴스도 남지 않은 곳을 가리키는 변수에 접근하려하면 runtime exception을 발생시키기 때문이다
-> 하지만 unowned로 선언하면 nil을 허용하지않는다, 우리는 일반적으로 deallocated된 인스턴스에 접근하는것을 권장하지않는다, unowned로 선언되고 deallocated된 이후에 접근하려하면은 runtime에 crash나게된다
-> 즉 unowned는 deallocated된 인스턴스에 접근하지않는다는것에 확신이 있을때 아니라면 weak로 선언하는것이다, 하지만 보통 weak로 그냥 선언하는것이 안전하다 (우리가 옵셔널바인딩에서 !보다 ?를 자주 사용하는것과 같은 맥락)
-> 위 코드를 보게되면은 Example클래스에서 delegate으로 프로퍼티를 선언하고 Example1클래스에서는 Example클래스에서 정의해야할 일을 자신이 하겠다고 말했다, 이 애기는 Example1인스턴스에서 Example인스턴스의 delegate을 가리키고있고 Example의 인스턴스에서는 delegate으로 역할을 위임해주었기때문에 Example1인스턴스를 가리키게되며 서로가 서로를 바로보며 언제 끊으려나 서로가 서로를 눈치보고있는 상황이 되는것이다(만일 Example1이 nil이 된다면 Example에서는 delegate에 대한것을 Example1에게 위임했기때문에 접근할 수 있는 방법이 없다)
-> 이때 사용하는것이 weak이다
-> 위에서 설명했다시피 weak 혹은 unowned키워드를 사용하여 프로퍼티를 정의하는것은 저는 deinit할때 신경쓰지않아도 되는 프로퍼티입니다~ 라고 말하는것이라했다
***사실 이번에 이 글을 쓰면서 처음 안 사실인데 위 예시에 빨간줄을 보면 알겠지만 weak 나 unowned키워드를 쓰려할때 프로토콜을 저런식으로 선언하면안된다(뭔가 너무 기분이 좋다, 새로운 지식을 얻은 느낌)
프로토콜은 클래스와 구조체, 열거형에 사용 가능한데 앞에서 선언한 프로토콜은 클래스에서 사용되는지 아니면 구조체나 열거형에서 사용되는지 알 수 없기 때문에 reference count관리를 위해 사용되는 unowned이나 weak 키워드를 사용할 수 없는 것 !!!
-> 위 코드처럼 이 프로토콜은 클래스에서만 선언가능한 프로토콜이니까 레퍼런스카운트를 관리하는 weak나 unowned키워드 사용가능해요!! 라고 알리는것이다 ㅎㅎㅎ
-> 사실 몇몇 블로그에 있는 예시는 나한테 크게 와닿지않아서 내 코드중 하나를 가져와보았다, 우리가 사실 코딩을 하면서 자주 보는 클로저같은경우는 함수의 트레일링클로저에 @escaping으로 정의되어있는 클로저에서 위와 같은 코드를 자주 볼것이다
-> 이 클로저안에서 지역변수는 전혀 상관이 없지만 전역변수의 프로퍼티를 가져와 쓰려고하면은 반드시 self를 앞에 붙여주어 참조한다, 하지만 이때 문제가 발생하는데 이 클로저라는것이 무언가 참조할때 자기 울타리안에서 계속 가지고있는 특성이 있다(캡처기능) 그렇기때문에 해당 클래스가 nil이 되더라도 이 클로저안에서는 참조한 프로퍼티가 남아있게되어 deinit되지않고 계속 메모리누수가 일어나게 되는것이다
-> 이때 사용하는것이 [weak self]이다, weak으로 선언했기에 self는 뒤에 옵셔널이 붙게되고 이렇게 선언해준다면 메모리누수걱정할 필요가 없다
***이렇게된거 클로저에 관한 설명도 정리해서 나만의 글을 올리면 좋을것같다