Swift에서 클래스의 강한 참조, 약한 참조, unowned 참조 완벽 가이드
1. 강한 참조(Strong Reference)란?
Swift에서 강한 참조(Strong Reference)는 객체의 소유권을 유지하는 참조 방식입니다. 기본적으로 모든 참조는 강한 참조로 설정되며, 객체가 다른 객체를 강한 참조하면 참조 카운트가 증가하여 그 객체가 메모리에서 해제되지 않도록 보장됩니다.
특징
- 기본 참조 방식으로, 객체의 소유권을 유지합니다.
- 객체를 참조하는 동안 메모리에서 해제되지 않습니다.
- 순환 참조를 발생시킬 수 있으므로, 상호 참조가 필요한 경우 주의해야 합니다.
사용 예시
강한 참조는 대부분의 상황에서 사용되며, 객체가 독립적으로 존재할 때 적합합니다.
class Person {
var name: String
var pet: Pet? // 기본적으로 강한 참조
init(name: String) {
self.name = name
}
}
class Pet {
var type: String
var owner: Person? // 기본적으로 강한 참조
init(type: String) {
self.type = type
}
}
let john = Person(name: "John")
let cat = Pet(type: "Cat")
john.pet = cat
cat.owner = john
설명
위 코드에서는 Person
객체 john
과 Pet
객체 cat
이 서로를 강한 참조합니다. 이 경우, john
과 cat
이 서로를 참조하고 있기 때문에, 순환 참조(Circular Reference)가 발생할 수 있습니다. 이로 인해 john
과 cat
은 서로를 참조한 채 메모리에서 해제되지 않습니다. 이를 해결하려면 약한 참조 또는 unowned 참조를 사용해야 합니다.
2. 약한 참조(Weak Reference)란?
약한 참조(Weak Reference)는 객체의 소유권을 유지하지 않는 참조 방식입니다. 약한 참조는 객체가 더 이상 사용되지 않을 때 메모리에서 해제될 수 있도록 하며, 순환 참조를 방지하는 데 주로 사용됩니다. 약한 참조는 항상 옵셔널(Optional
) 타입으로 선언되어야 하며, 객체가 해제되면 자동으로 nil
로 설정됩니다.
특징
- 객체의 소유권을 유지하지 않고, 객체가 해제되면 자동으로
nil
로 설정됩니다. - 약한 참조는 항상 옵셔널 타입으로 선언되어야 합니다.
- 순환 참조를 방지할 때 주로 사용됩니다.
사용 예시
약한 참조는 보통 상호 참조를 방지하기 위해 한쪽의 참조를 약한 참조로 설정합니다.
class Person {
var name: String
var pet: Pet? // 강한 참조
init(name: String) {
self.name = name
}
}
class Pet {
var type: String
weak var owner: Person? // 약한 참조
init(type: String) {
self.type = type
}
}
let john = Person(name: "John")
let cat = Pet(type: "Cat")
john.pet = cat
cat.owner = john // 약한 참조이므로 순환 참조 방지
설명
위 코드에서 owner
속성은 weak
키워드를 사용하여 약한 참조로 선언되었습니다. 따라서 Person
인스턴스가 메모리에서 해제되면 Pet
인스턴스의 owner
속성은 자동으로 nil
로 설정됩니다. 이로써 순환 참조 없이 두 객체가 서로 참조할 수 있습니다.
3. Unowned 참조란?
Unowned 참조는 약한 참조와 비슷하게 객체의 소유권을 유지하지 않으면서 참조하는 방식입니다. 하지만 약한 참조와 다르게 Optional
이 아니며, 참조가 항상 메모리에 존재한다고 확신할 때 사용합니다. 객체가 해제된 후에 unowned
참조에 접근하면 런타임 오류가 발생하므로, 수명이 보장된 경우에만 사용해야 합니다.
특징
- nil을 허용하지 않는 비옵셔널 참조입니다.
- 객체의 소유권을 유지하지 않으므로, 메모리 해제에 영향을 미치지 않습니다.
- 참조 대상보다 수명이 짧거나 종속적인 객체에 적합합니다.
사용 예시
unowned 참조는 보통 참조 대상이 항상 유효할 것으로 확신할 때 사용합니다.
class Company {
var name: String
var ceo: Employee?
init(name: String) {
self.name = name
}
}
class Employee {
var name: String
unowned var company: Company // unowned 참조
init(name: String, company: Company) {
self.name = name
self.company = company
}
}
let apple = Company(name: "Apple")
let tim = Employee(name: "Tim", company: apple)
apple.ceo = tim
설명
위 코드에서 Employee
의 company
속성은 unowned 참조로 선언되었습니다. 이 설정으로 Employee
는 Company
객체가 해제될 때까지 company
속성을 참조할 수 있습니다. Employee
는 항상 Company
와 함께 해제되므로 순환 참조의 위험 없이 안전하게 참조할 수 있습니다.
4. 참조 타입의 사용 시기와 사례
Swift에서는 메모리 관리와 객체 간의 관계를 명확히 하기 위해 참조 타입을 상황에 맞게 사용합니다.
참조 타입 | 사용 시기 및 사례 |
---|---|
강한 참조 | 기본적인 참조 방식으로, 객체 간의 단방향 참조에 적합합니다. 순환 참조가 발생하지 않을 때 대부분 강한 참조를 사용합니다. |
약한 참조 | 순환 참조가 발생할 수 있는 경우에 사용합니다. 예를 들어, 부모-자식 관계에서 자식이 부모를 약한 참조로 설정하여 순환 참조를 방지합니다. |
Unowned 참조 | 참조 대상이 항상 메모리에 남아있거나 한쪽 객체가 다른 객체에 종속적인 경우 사용합니다. 예를 들어, Company 와 Employee 에서 Employee는 항상 Company에 종속적이므로 unowned 로 설정합니다. |
5. 요약
Swift의 강한 참조, 약한 참조, unowned 참조는 객체 간의 관계를 정의하고, 메모리 관리 및 순환 참조 문제를 해결하기 위해 중요한 역할을 합니다.
- 강한 참조는 기본적인 참조 방식으로, 객체의 소유권을 유지합니다.
- 약한 참조는 순환 참조를 방지하고, 객체가 해제될 때
nil
로 설정됩니다. - Unowned 참조는 객체가 항상 메모리에 존재할 때 사용하며, 순환 참조 없이 비옵셔널 참조를 가능하게 합니다.
최종 정리
이와 같이 참조 방식을 상황에 맞게 선택하여 객체 간의 관계를 설정하고 메모리 관리를 효율적으로 할 수 있습니다. Swift의 자동 메모리 관리(ARC) 덕분에, 개발자는 강한 참조와 약한 참조를 적절히 사용하여 메모리 누수 없이 안전한 코드를 작성할 수 있습니다.