iOS

함수 인자로 class를 받는다면 ARC의 변화가 있을까?

미니미니니

오늘은 함수 인자로 class의 인스턴스를 전달했을 때 ARC의 카운트가 증가하고 함수가 끝나면 카운트가 감소하는지에 대해서 직접 알아보도록 하겠습니다.

 

ARC는 참조 객체가 사용되면 자동으로 RC를 증가 시키고, 사용이 끝나면 자동으로 RC를 감소시켜줘서 RC가 0이 되었을 때 메모리에서 해제시켜 메모리 누수를 방지해주는 역할을 하고있습니다.

 

그런데 문득 함수의 인자로 class의 인스턴스를 넘겨주면 그 때 ARC의 카운트가 증가하는지, 그렇다면 함수가 끝나면 카운트가 자동으로 감소하는지 알아보고싶어져서 테스트를 해보았습니다.

 

먼저 테스트하기위해 아래와같이 코드를 작성해주었습니다.

 

test를 생성해준 뒤 useTest()에 생성한 test를 넘겨주는 코드입니다. 이때 useTest에서 ARC 카운트의 변화가 있을까? 이것이 오늘 찾아보려고한 부분입니다. 

class Test: NSObject {
  let name: String

  init(name: String) {
    self.name = name
    print("Test \(name) 생성됨")
  }

  deinit {
    print("Test \(name) 소멸됨")
  }
}

func useTest(test: Test) {
//break point
  let name = test.name
  print("Test\(name) 사용")

}

func checkARC() {
  print("==Test 시작==")

  let test = Test(name: "테스트")
  print("RC +1")
//break point
  useTest(test: test)
  print("함수 호출 완료")

  print("==Test 종료==")
}

checkARC()

일단 checkARC()를 실행했을 때 프린트 결과로는 useTest 함수 내부의 카운트 증감을 알 수 없었기 때문에 확인하고싶은 부분에 break point를 걸고 po CFGetRetainCount(test)라는 LLDB 명령어를 사용해서 RC를 직접 확인했습니다. 정확한 카운트 숫자는 생각한것과 다를 수 있지만 RC가 증가하면  +1이 되기 때문에 증감은 확인할 수 있는 명령어입니다. 

( po CFGetRetainCount(test) 명령어를 사용하기위해 Test에 NSObject를 상속받도록 해주었습니다. )

확인결과 RC가 증가하지 않았습니다.

하지만 함수의 인자로 인스턴스를 넘겼을 때 함수 안에서 진행되는 생명주기와 인스턴스를 생성한 곳인checkARC()의 test와 다른 생명주기를 가질 것이기 때문에 ARC가 증가하지 않는다면 test는 한쪽에서만 RC가 감소하면 메모리에서 아예 사라지게되어 치명적인 오류가 날 것이라고 생각했습니다. 그래서 이와 관련된 부분을 찾던 중, swift의 컴파일러가 임의로 판단하여 더이상 사용되지 않을 것 같을 때 ARC 카운터를 감소시킬 수도 있다는 글을 보게 되었고, 그 문제를 해결하기위해 withExtendedLifetime(_:_:)를 사용했습니다. 

https://developer.apple.com/documentation/swift/withextendedlifetime(_:_:)-4mmpv

 

withExtendedLifetime(_:_:) | Apple Developer Documentation

Evaluates a closure while ensuring that the given instance is not destroyed before the closure returns.

developer.apple.com

문서를 보시면

Evaluates a closure while ensuring that the given instance is not destroyed before the closure returns.

 

"주어진 인스턴스가 클로저의 실행이 끝나기 전에 메모리에서 해제되지 않도록 보장하면서, 그 클로저를 실행합니다."

라고 설명되어있습니다. 이 문장에서 "클로저의 실행이 끝나기 전에 메모리에서 해제되지 않도록 보장" 이라는 말은 클로저가 끝나기 전까지 RC를 감소하지않는다는 뜻이고, 그렇다면 컴파일러가 임의로 감소시키는 것을 무시하고 테스트를 할 수 있다는 뜻이됩니다. 그래서 아래와 같이 코드를 수정하고 다시 RC를 확인해보았습니다.

func useTest(test: Test) {
  withExtendedLifetime(test) {
  //break point
    let name = test.name
    print("Test\(name) 사용")
  }
}

그 결과 위 사진처럼 2에서 3으로 RC가 증가하는것을 확인할 수 있었습니다. 

 

오늘은 함수 인자로 인스턴스를 전달했을 때 RC가 증가하는지 직접 확인해보았습니다. 중간에 생각과 달라서 RC가 증가하지 않는건가? 라고 생각했지만 그건 아닌 것 같다.. 라는 직감을 믿고 더 찾아본 결과 역시 증가하는것이 맞다는 것을 알아내었습니다. 

다음번에는 클로저 캡쳐와 관련해서 ARC가 어떻게 작동되는지 알아보도록 하겠습니다.