ARC(Automatic Reference Counting) 자동 참조 카운팅


ARC란?

메모리를 모니터링하고 관리해주는 자동참조카운팅(ARC)라는 기능을 제공한다. ARC의 역할은 프로그램에서 사용한 메모리를 추적해서 인스턴스가 사용한 메모리가 더 이상 필요 없는 경우 자동으로 해제 역할을 한다.


ARC 기능

클래스를 정의 한 후에 인스턴스를 생성하게 되면 ARC는 자동으로 그 인스턴스에 대한 정보를 저장할 메모리를 할당한다. 이 메모리는 해당 인스턴스가 가진 속성들의 값들을 비롯한 인스턴스의 타입에 대한 정보를 저장하고 있다. 인스턴스가 해제될 때 ARC는 자동으로 인스턴스가 사용한 메모리를 해제한다. ARC의 동작은 클래스에서만 동작한다. 클래스는 인스턴스를 생성할 때 참조 타입(reference type)으로 생성하기 때문이다. 구조체나 열거형은 값 타입(value type)이기 때문에 ARC입장에서는 메모리를 관리 할 필요가 없다.


인스턴스 생성과 해제

인스턴스의 속성, 상수, 변수들이 각 클래스의 인스턴스에서 몇번 참조되었는가를 계속 트래킹하고 있다가 참조하는 수가 0이 되면 해당 인스턴스를 해제하고 0보다 크면 참조를 계속 유지하는 방식이다.

import UIKit

class MyGrade {
    var score: Int
    init(score: Int) {
        self.score = score
        print("초기화 되었습니다.")
    }
    deinit {
        print("해제 되었습니다")
    }
}

var ref: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.

ref = MyGrade(score: 100) //인스턴스를 생성하고 score에 대한 메모리가 할당한다.
ref = nil //다시 nil값을 할당하여, 인스턴스를 해제하는 코드입니다.

//콜솔에서의 결과
초기화 되었습니다.
해제 되었습니다

Strong reference

클래스의 인스턴스를 다른 클래스의 속성으로 만드는 경우를 스토롱참조라고 한다. 스트롱 참조의 경우는 사용자가 편하고 코드를 간결하게 만들수 있다는 장점이 있다.

import UIKit

class MyGrade {
    var score: Int
    init(score: Int) {
        self.score = score
        print("초기화 되었습니다")
    }
    deinit {
        print("해제 되었습니다")
    }
}

var ref_1: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.
var ref_2: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.
var ref_3: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.

ref_1 = MyGrade(score: 100) //인스턴스를 생성하고 score에 대한 메모리가 할당한다.
ref_2 = ref_1 //ref_2에 ref_1를 Strong reference
ref_3 = ref_1 //ref_3에 ref_1를 Strong reference

//순차적으로 nil값을 대입해보면
ref_1 = nil //ref_2, ref_3 메모리가 할당되어 있기 때문에 해제되지 못한다.
ref_2 = nil //ref_3 메모리가 할당되어 있기 때문에 해제되지 못한다.
ref_3 = nil //메모리에 할당된 참조 카운트가 0이되기에 인스턴스가 해제 된다.

//콜솔에서의 결과
초기화 되었습니다.
해제 되었습니다.

하지만 여러 클래스에서 스트롱참조를 사용한다보면 예상하지 못한 문제에 부딪힐수 있다. 문제로는 Strong reference cycle이 있다.

Strong reference cycle

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    var mymember: Friend?
    init(number: Int) {
        self.number = number
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개") //인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3) //인스턴스를 생성하고 number에 대한 메모리가 할당된다.

//?는 옵셔널 타입을 가르킨다면, !는 참조 타입의 속성을 가리킨다.
myFriend!.myClass = myClass //다른 클래스의 인스턴스를 참조시킨다.
myClass!.mymember = myFriend //다른 클래스의 인스턴스를 참조시킨다.

myFriend = nil //다른 클래스의 인스턴스를 참조하고 있기 때문에 메로리가 해제되지 않는다
myClass = nil //다른 클래스의 인스턴스를 참조하고 있기 때문에 메로리가 해제되지 않는다

//콜솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.

서로의 인스턴스를 속성으로 연결할 경우 둘다 속성이 남아있기때문에 메모리에서 해제될수 없다. 이런 현상을 String reference cycle이라고 한다. 이문제의 해결 방법으로는 weak reference와 unknown reference가 있다.


weak reference

위크 참조는 인스턴스를 속성값으로 저장해두지 않기 때문에 ARC가 동작하는데 큰 문제가 없도록 한다.

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    weak var mymember: Friend?
    init(number: Int) {
        self.number = number
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개") //인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3) //인스턴스를 생성하고 number에 대한 메모리가 할당된다.

//?는 옵셔널 타입을 가르킨다면, !는 참조 타입의 속성을 가리킨다.
myFriend!.myClass = myClass //다른 클래스의 인스턴스를 참조시킨다.
myClass!.mymember = myFriend //위크 참조이기에 다른 클래스의 인스턴스를 참조시키지 않는다.

myFriend = nil
myClass = nil

//콜솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.
명아무개이 해제 되었습니다.
3반이 해제 되었습니다.

위크 참조로 속성을 선언하면 “값이 없다”고 설정 할 수 있기 때문에 모든 위크 참조는 옵셔널 타입으로 선언되어야 한다. 위크 참조는 인스턴스의 참조를 저장하지 않기 때문에 위크 참조인 속성이 여전히 다른 인스턴스를 참조하고 있다고 하더라도 해당 인스턴스를 해제할 수 있다.


unknown reference

위크 참조하는 달리 언노운 참조는 항상 값을 갖고 있다고 가정한다.

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    unowned var mymeber: Friend
    init(number: Int, friend: Friend) {
        self.number = number
        self.mymeber = friend
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개")//인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3, friend: myFriend!) //언노운 참조이기에 다른 클래스의 인스턴스를 파라미터로 받아서 초기화 한다.

myFriend = nil

//콘솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.
명아무개이 해제 되었습니다.

언노운 참조는 Friend 이니셜라이즈는 두개의 속성에 대해 파라미터로 값을 받아서 초기화를 해야 한다는 점이다.


맞치며

Swift에서는 참조 타입의 속성에 대해 다른 인스턴스를 얼마나 많이 참조하는가를 카운트해두었다가 그 카운트가 0이 되는, 즉 더는 참조하는 인스턴스가 없다고 판단되는 경우에만 인스턴스를 해제한다. 두개 이상의 클래스들이 서로 간의 참조를 속성값으로 갖고 있는 경우에는 참조 카운터가 0이 되지 못하기 때문에 발생한다. 이러한 문제를 스토롱 참조 사이클이라고 하며, 위크참조와 언노운참조를 통해 문제를 해결 할수 있다.

ARC 자체만으로도 개발자에게 메모리 누수라는 문제를 해결 할수 있는 강력한 도구이다. 하지만 잘 알고 사용을 해야 완벽한 메모리 누수를 잡을 수 있다. 이번 ARC를 공부 및 정리를 하다보니 실전도 중요하지만 이론도 중요하다는 결론이 나온다. 부족한 실력으로 정리를 했지만 도움이 되길 바라면서 틀린점이나 궁금한점이 있으면 댓글 남겨주세요. 감사합니다 :)


출처

가장 쉽게 설명하는 Swift



'iOS 프로그래밍 > Swift' 카테고리의 다른 글

ARC(Automatic Reference Counting) 자동 참조 카운팅  (0) 2017.12.22
스위프트란? (swift)  (0) 2015.11.19


안녕하세요! 저는 스타트업 회사에 몸을 담고 IOS개발을 하고 있습니다.

교롭게도 안드로이드 개발을 공부하다가 이번 프로젝트를 시작하면 IOS개발을 작년 5월쯤 시작했습니다. 한달뒤 애플은 스위프트를 기습적으로 발표를 하고 많은 사람들이 놀랬던 기억이 있습니다. 그때 당시의 고민이 스위프트로 개발 해야 할까 아님 이미 오랫동안 사용 되었던 오브젝티브-C를 통해 개발을 해야 할까라는 많은 고민을 했습니다. 하지만 저를 가르쳐주는 사람도 없었고 결국은 그냥 예제등 많은 시간동안 축척 되어있는 오브젝티브-C를 이용해서 개발을 하자 해서 지금 까지 오브젝티브-C를 통해 개발을 하고 있습니다. (살짝 후회가 되기도 합니다.) 

애플이 처음으로 발표를 하고 1년5개월 정도 되었고 2.0버전까지 발표를 했으며, 오픈소스화 발표까지 해버렸습니다. 오브젝티브-C가 오랫동안 애플의 대표적인 언어로 활용 되어 왔기때문에 당장은 스위프트와 공존을 하게 되겠지만 시간이 지나면 지날수록 스위프트는 중요 해지는 당연한 수순일까 해서 더 늦기 전에 스위프트를 공부하고 그 공부를 바탕으로 포스팅을 할 생각입니다. 물론 시간은 걸리겠지만 지금 하고 있는 프로젝트도 서서히 스위프트로 교체를 해야되지 않을까 싶습니다. 


위키백과 에서는 스위프트 언어를 이렇게 정의 하고 있습니다.

스위프트(Swift)는 애플의 IOS와 OS X를 위한 프로그래밍 언어로 2014년6월2일 애플 세계 개발자 회의에서 처음 소개되었다. 기존의 애플 운영체제용 언어인 오브젝티브-C와 함께 공존할 목적으로 만들어졌다. 오브젝티브-C와 마찬가지로 LLVM으로 빌드 되고 같은 런타임을 공유한다. 클로저, 다중 리턴 타입, 네임스페이스, 제네릭스, 타입 유추 등 오브젝티브-C에는 없었던 현대 프로그래밍 언어가 갖고 있는 기능을 많이 포함 시켰으며 코드 내부에서 C나 오브젝티브-C 코드를 섞어서 프로그래밍 하거나 스크립트 언어 처럼 실시간으로 상호 작용하며 프로그래밍 할 수도 있다. (출처 : 한국 위키 백과)


지디넷에서 발표 당시 기존 애플 운영체제(OS) 기반 앱 개발자들이 염두에 둬야 할 항목 10가지를 다음과 같이 정리했다.

첫째, 스위프트는 'C가 빠진 오브젝티브C'같은 언어다. 이 새로운 언어는 과거 'C이긴 C인데 이상한 C'라 불리던 오브젝티브C의 문제를 보완했다는 평가다. 이는 애플이 스위프트를 '빠르고 안전하고 인터랙티브한' 프로그래밍 언어로 묘사했다는 점에서 짐작 가능하다.

둘째, 스위프트는 애플 iOS와 OS X 환경에서 작동한다. 단지 iOS 기기에서 돌아가는 앱을 개발하는 용도로 만들어진 게 아니다. 개발자들은 OS X에서 돌아가는 앱을 만들 때도 오브젝티브C 대신 스위프트를 사용할 수 있다. 애플은 자사 플랫폼의 '주력 프로그래밍 언어'라는 지위를 오브젝티브C에서 떼어내려는 듯하다.

셋째, 스위프트는 빠르다. 애플이 근거로 삼은 자료에 따르면 스위프트는 컴플렉스 오브젝트 정렬이나 RC4 암호화같은 작업을 수행할 때 파이썬이나 오브젝티브C를 넘어서는 처리 성능을 발휘한다. 이는 개발자들이 앱을 만들 때 코드 최적화에 쏟아야 하는 시간을 줄이고 개발에 더 전념케 해준다. 

넷째, 스위프트는 언어 스펙에 다중 리턴 타입, 클로저, 제네릭스, 타입 인터페이스, 네임스페이스 등을 포함한다. 간단히 말해 현대적인 언어에서 지원하는 주요 기능들을 대거 흡수했다는 얘기다. 개발자들은 이로써 앱 개발 언어를 오브젝티브C에서 스위프트로 바꾸기 위해 특별히 필요로하는 프로그래밍 언어 특성을 포기할 필요가 없다.

다섯째, 스위프트는 개발자가 오브젝티브C와 C 언어를 함께 써 온 기존 방식에 그대로 도입할 수 있기 때문에, 개발자들의 업무흐름을 깨뜨리게 하지 않는다. 개발자들은 스위프트를 쓰기로 했더라도 필요하다면 여러 언어를 혼합한 앱을 개발할 수 있다.

여섯째, 스위프트는 기존 애플의 코코아 및 코코아터치 프레임워크 개발환경에 그대로 적용 가능하다. 스위프트 역시 동일한 LLVM컴파일러를 사용해 만들어졌다. 또한 동일한 옵티마이저와 오토벡터링, 동일한 ARC메모리관리자, 오브젝티브C와 동일한 런타임을 사용한다.

일곱째, 스위프트는 디버깅이 쉽다. 통합개발환경(IDE) X코드(Xcode)의 디버깅 콘솔은 스위프트 언어의 인터랙티브 버전을 포함하고 있다. '인터랙티브 플레이그라운드'라 부르는 기능인데, 이는 개발자가 스위프트 문법을 검증하고 스크립트언어처럼 구동중인 앱과 상호작용하면서 그대로 새로운 알고리즘을 고안하거나 코드를 써넣는 식으로 개발이 가능하게 해준다. 이는 X코드 콘솔이나 터미널모드에서 사용 가능하다.

여덟째, 스위프트는 안전하지 않은 코드를 걷어낼 수 있게 해준다. 애플은 안전하지 않은 코드의 전체 클래스를 제거하는 방향으로 스위프트를 설계해 왔다. 변수들은 항상 사용되기 전에 선언된다. 배열과 정수는 오버플로 현상에 대비해 확인을 받는다. 메모리는 자동으로 관리된다.

아홉째, 스위프트는 애플이 기존 오브젝티브C에 익숙한 개발자들이 개발 언어를 바꿀 수 있도록 만들어졌다. 이를테면 앞서 오브젝티브C로 만들어진 앱의 코드를 스위프트 기반으로 쉽게 변환할 수 있도록 했다는 평가다. 이 과정은 새 개발 언어의 코드를 더 구조화되고 논리적으로 만들고 성능도 높여 준다.

열째, 스위프트는 당장 사용 가능하다. 개발자들은 애플이 공개한 X코드를 즉시 내려받을 수 있다. 또한 아이튠스 앱스토어와 맥 앱스토어를 통해 내려받을 수 있는 '아이북스' 전자책 형태로 공개한 스위프트 프로그래밍 언어 개발용 전자책도 공개돼 있다.




이렇듯 애플은 오랫동안 준비 하고, 오브젝티브C를 대처 하기 위한 언어로 스위프트를 내세웠기 때문에 OS X, IOS 애플리케이션 개발자들에게는 필수 코스가 되지 않을까 싶습니다. 공부를 하면서 스위프트에 대해서 포스팅을 하겠습니다. 그리고 스위프트를 이용한 다른 프로젝트도 같이 진행할 예정이고요. 일을 하고 하루에 한두시간정도 투자해서 하는 거라 더디고 더디고 굼벵이가 지나가는것보다 느릴수 있지만 조금씩이나마 블로그를 채워 나가도록 하겠습니다. 잘 부탁드립니다.


ps. 물론 지금은 휴가중이라 조금만 쉬고 공부를 시작하는데로 올리겠습니다...ㅎㅎ;


'iOS 프로그래밍 > Swift' 카테고리의 다른 글

ARC(Automatic Reference Counting) 자동 참조 카운팅  (0) 2017.12.22
스위프트란? (swift)  (0) 2015.11.19

+ Recent posts