[iOS] ARC

r1verfuture·2022년 4월 13일
0

iOS

목록 보기
12/30

ARC 란 ?

  • Automatic Reference Counting
  • 메모리 영역 4가지 (코드 / 데이터 / 힙 / 스택) 중 힙 영역을 관리하는 것 (힙 영역을 관리하는 방법에는 GCRC가 있다.)
  • 앱의 메모리 사용을 추적하고 관리하기 위해 사용한다.
  • 클래스 인스턴스가 더 이상 필요하지 않게 되면 메모리를 자동으로 해제해준다.

GC

  • Garbage Collection
  • 참조 계산 시점 : 앱 실행동안 주기적으로 참조를 추적해서 사용하지 않는 인스턴스를 해제한다.
  • RC에 비해 인스턴스가 해제될 확률이 높다.
  • 개발자가 참조 해제 시점을 알 수 없다.
  • 앱 실행동안 계속 참조를 추적하는 추가 리소스가 필요하기 때문에 성능 저하가 발생할 수 있다.

RC

  • Reference Counting
  • 참조 계산 시점 : 컴파일할 때 언제 참조되고 해제되는지 결정해서 런타임 때 그대로 실행된다.
  • 개발자가 참조 해제 시점을 알 수 있다.
  • 앱 실행동안 추가 리소스가 필요하지 않다.
  • 순환 참조가 발생할 때 영구적으로 메모리가 해제되지 않을 수 있다.

MRC (MRR)

  • Manual Reference Counting
  • 힙에 메모리를 직접 할당 / 해제 해주는 것 (힙에 메모리를 자동으로 할당 / 해제 해주는 ARC와 가장 큰 차이점)
  • 2011년 이전까지 Objective-C가 사용했던 방법이지만, 2011년 이후부터는 Objective-C도 ARC를 사용하기 시작했다. (오래된 코드보면 가끔 'retain', 'release' 같은 함수들을 볼 수 있는데, 이 함수들이 MRC 사용할 때 쓰는 것이다.)

ARC 의 메모리 관리 방법

  • Reference Count 방식
  • 메모리의 참조 횟수를 계산해서 참조 횟수가 0이 되면 더 이상 사용하지 않는 메모리라고 판단하여 메모리를 해제한다.
  • 어떠한 인스턴스가 현재 참조되고 있는 횟수
  • 모든 인스턴스는 자신의 RC 값을 가지고 있다. (인스턴스가 생성될 때 힙에 같이 저장된다.)
  • 인스턴스의 주소값을 변수에 할당하는 순간 RC값이 1 올라간다.
  • 인스턴스를 참조하고 있던 변수가 메모리에서 해제되는 순간, 해당 인스턴스의 RC값이 1 줄어든다.
  • 인스턴스를 참조하고 있던 변수에 nil이 부여되는 순간, 해당 인스턴스의 RC값이 1 줄어든다.
  • 인스턴스를 참조하고 있던 변수에 다른 값이 대입되는 순간, 해당 인스턴스의 RC값이 1 줄어든다.
  • 프로퍼티의 경우, 속해 있는 클래스 인스턴스가 메모리에서 해제될 때 해당 인스턴스의 RC값이 1 줄어든다.
// MARK: [코드 1] Developer 클래스 생성
class Developer {
	var name: String?
    var language: String?
    
    init(name: String?, language: String?) {
    	self.name = name
        self.language = language
    }
}
// MARK: [코드 2] Developer 클래스의 인스턴스 생성
let r1verfuture = Developer(name: "밀애1", language: "Swift")
  • [코드 1], [코드 2] 를 실행하면 지역 변수 r1verfuture스택에 할당되고, 실제 Developer 인스턴스에 할당된다.
  • 지역 변수 r1verfuture 에는 힙에 할당된 인스턴스의 주소값이 들어간다. (이런 식으로 새로운 변수에 인스턴스가 대입되면 해당 인스턴스의 RC가 증가한다.)
  • Developer 인스턴스의 RC값이 1이 된다.
// MARK: [코드 3] 기존의 r1verfuture 변수를 r2verfuture 변수에 대입
let r2verfuture = r1verfuture
  • [코드 1], [코드 2], [코드 3] 을 실행하면 Developer 인스턴스의 RC값이 2가 된다.
  • 지역 변수 'r2verfuture' 에도 'r1verfuture' 랑 마찬가지로 Developer 인스턴스의 주소값이 들어간다.
// MARK: [코드 4] Developer 복제하는 함수 생성
func copyDeveloper(_ developer: Developer) {
	let cloneDeveloper = developer
}

copyDeveloper(r1verfuture)
  • [코드 1], [코드 2], [코드 4] 를 실행하면 [코드 2] 의 'r1verfuture' 가 생성되는 순간 Developer 인스턴스의 RC값이 1이 된다.
  • copyDeveloper 함수가 실행되면 Developer 인스턴스의 RC값이 2가 된다.
  • copyDeveloper 함수가 종료되어 지역 변수 'cloneDeveloper' 의 메모리가 스택에서 해제되는 순간 Developer 인스턴스의 RC값이 1이 된다. (1 줄어든다.)
// MARK: [코드 5] Developer 인스턴스 메모리 해제되는 과정 예시
var r1verfuture : Developer? = .init(name: "밀애1", language: "Swift")
var r2verfuture = r1verfuture

r2verfuture = nil
r1verfuture = nil
  • [코드 1], [코드 5] 를 실행하면 [코드 5] 의 첫번째 줄에서 Developer 인스턴스의 RC값이 1이 된다.
  • [코드 5] 의 두번째 줄에서 Developer 인스턴스의 RC값이 2가 된다.
  • [코드 5] 의 네번째 줄에서 Developer 인스턴스의 RC값이 1이 된다.
  • [코드 5] 의 다섯번째 줄에서 Developer 인스턴스의 RC값이 0이 된다. (Developer 인스턴스가 메모리에서 해제된다.)
// MARK: [코드 6] 인스턴스를 참조하고 있던 변수에 다른 값을 대입한 경우
var r1verfuture : Developer? = .init(name: "밀애1", language: "Swift")
var r2verfuture : Developer? = .init(name: "밀애2", language: "Swift")

r1verfuture = r2verfuture
  • [코드 1], [코드 6] 를 실행하면 [코드 6] 의 첫번째 줄에서 Developer 인스턴스의 RC값과 r1verfuture 인스턴스의 RC값은 각각 1이 된다.
  • [코드 6] 의 두번째 줄에서 Developer 인스턴스의 RC값은 2가 되고, r2verfuture 인스턴스의 RC값은 1이 된다.
  • [코드 6] 의 네번째 줄에서 r1verfuture 인스턴스의 RC값은 0이 되고 (r1verfuture은 메모리에서 해제된다.), r2verfuture 인스턴스의 RC값은 2가 된다. (r1verfuture에 r2verfuture 주소값이 들어가면서 RC도 변경된다.)
// MARK: [코드 7] Contacts 클래스와 Person 클래스 생성
class Contacts {
	var phone: String?
    var address: String?
    
    init(phone: String?, address: String?) {
    	self.phone = phone
        self.address = address
    }
    
    deinit { print("Contacts Deinit") }
}

class Person {
	var name: String?
    var age: Int?
    var contacts: Contacts? = .init(phone: "010-1234-5678", address: "Korea")
    
    init(name: String?, age: Int?) {
    	self.name = name
        self.age = age
    }
    
    deinit { print("Person Deinit") }
}
// MARK: [코드 8] Person 클래스 인스턴스 생성한 후 nil 값 부여
let r3verfuture: Person? = .init(name: "밀애3", age: 25)
r3verfuture = nil
  • [코드 7], [코드 8] 을 실행하면 Person 클래스 안에 'contacts' 라는 Contacts 클래스 인스턴스가 프로퍼티로 존재한다.
  • [코드 8] 의 첫번째 줄에서 'r3verfuture' 라는 Person 클래스 인스턴스를 생성하면 r3verfuture와 contacts 인스턴스가 생성되며, 두 인스턴스 각각 RC값이 1 증가한다.
  • [코드 8] 의 두번째 줄에서 'r3verfuture' 에 nil 을 부여하는 순간, r3verfuture와 contacts 인스턴스의 RC값이 각각 1씩 감소한다. (RC값이 각각 0이 되기 때문에 메모리에서 둘 다 해제된다.)
  • [코드 8] 에서는 contacts 인스턴스가 한번만 참조되었기 때문에 RC값이 1 감소하면 0이 돼서 메모리에서 해제되었지만 contacts 인스턴스가 r3verfuture 말고도 다른 곳에서 참조되고 있다면 RC값이 1 감소하기만 할 뿐 메모리에서 해제되지는 않는다.
  • deinit : 인스턴스가 메모리에서 해제될 때 호출되는 함수

참고

profile
#iOS #Swift #Developer #Python

0개의 댓글

관련 채용 정보