Swift. ARC 정리

ARC, Weak, Unowned

Posted by MinJun on Sunday, February 18, 2018 Tags: Swift   7 minute read

ARC(Auto Reference Counting)

  • 자동 레퍼런스 카운트 관리
  • 객체의 레퍼런스 카운트 관리 코드 자동 생성(컴파일)
  • 래퍼런스 타입(Class)객체에만 적용
  • 벨류 타입(구조체, Enum) 객체에는 미적용

객체 생성 -> 메모리 차지 -> 메모리 공간의 제약 -> 메모리 관리, 객체가 많아지면 메모리 사용량이 증가하게됨. 이때 적절하게 메모리 해제를 해주어야 함

var ptr: Myclass? = MyClass() // Reference Count 0 -> 1 
ptr = nil // Reference Count 1 - > 0

객체 해제 확인 하기

class MyClass {
	deinit {
		print("객체가 메모리에서 해제")
	}
}

var obj : MyClass! = MyClass()  // 객체 생성
obj = nil // 객체 해제 

객체가 해제되면 deinit 매서드가 호출되서 콘솔창에 작성해놓은 스트링이 출력됩니다.


콜렉션과 소유권

  • 콜렉션에 객체 저장: 콜렉션이 객체 소유
  • 콜렉션에서 객체 삭제: 소유권 해제
  • 콜렉션 객체 해제: 소유권 해제
var obj: MyClass = MyClass()
var array = [obj] // obj 객체의 소유권을 배열이 가지고 있기 때문에, 아래에서 obj의 객체를 해제 해도, 배열의 원소로 있는 obj 객체는 해제되지 않음.  
obj = nil // obj 객체 해제,

* 배열에서 삭제 - 소유권 해제
array.remove(at:0)

강한 순환 참조

  • 두 개 이상의 관계에서도 가능
  • 서로 소유하므로 해제되지 않음(메모리 누수)
  • 수동으로 해제되도록 작성 해야함
** 메모리 정상 해제
class Person {
    var apartment: Apartment?
    deinit {
        print("Person 객체 해제")
    }
}

class Apartment {
    var tenant: Person?
    deinit {
        print("Apartment 객체 해제")
    }
}

var man: Person? = Person()
var home: Apartment? = Apartment()

man = nil // Person 객체 해제
home = nil // Apartment 객체 해제

** 강한 순환 참조 
class Person {
    var apartment: Apartment?
    deinit {
        print("Person 객체 해제")
    }
    
}
class Apartment {
    var tenant: Person?
    deinit {
        print("Apartment 객체 해제")
    }
}

var man: Person? = Person()
var home: Apartment? = Apartment()

man?.apartment = home
home?.tenant = man

man = nil // 해제 메세지 없음. 하지만 man 객체 는 nil
home = nil // 해제 메세지 없음. 하지만 home 객체 는 nil

	- > 메모리 누수 
	- > 해당 메모리 누수를 해결하기위해서는, 참조한 객체들을 직접 해제 해주어야함.

** 직접 해제

class Person {
    var apartment: Apartment?
    deinit {
        print("Person 객체 해제")
    }
    
}
class Apartment {
    var tenant: Person?
    deinit {
        print("Apartment 객체 해제")
    }
}

var man: Person? = Person()
var home: Apartment? = Apartment()

man?.apartment = home
home?.tenant = man

man?.apartment = nil
home?.tenant = nil
man = nil // Person 객체 해제
home = nil // Apartment 객체 해제

약한 참조

  • 강한 순한 참조 문제 해결 하기
    • 객체를 소유하는 강한 참조만 사용하면 문제가 될수 있음
    • 객체를 소유하지 않는 약한참조(Weak Reference) 사용
  • 객체를 소유하지 않는 포인터: weak, unowned

- weak

  • 참조하던 객체가 해제되면 자동 nil -> 옵셔널일수 있음
  • nil이 되므로 옵셔널
  • 상호 독립적으로 존재하는 객체에 사용
class Person {
    var apartment: Apartment?
    deinit {
        print("Person 객체 해제")
    }
    
}
class Apartment {
    weak var tenant: Person?
    deinit {
        print("Apartment 객체 해제")
    }
}

var man: Person? = Person() // Person Reference Count +1 = 1 
var home: Apartment? = Apartment() // Apartment Reference Count +1 = 1 

man?.apartment = home // Apartment Reference Count +1 = 2
home?.tenant = man // Person Reference Count = 1 

man = nil // Person 객체 해제 // Reference Count 0 -> Person 객체 해제 
home = nil // Apartment 객체 해제 // Referece Count 1 -> man 객체 해제되면서, home.tenant 에서 참조 하고있던 Person 의 Reference Count 없어짐 -> 결과적으로 home의 Reference Count 0 -> 객체 해제 

- unowned

  • 옵셔널 타입으로 선언 불가(Initializer)를 생성 할수도 있음
  • 완전히 종속적인 경우에 사용
  • 참조하던 객체가 해제되어도 nil로 변하지 않음 -> Dangling pointer 위험
  • 단독을 존재 못하는 종속적인 경우
class Country {
    var capital : Capital!
    deinit {
        print("Country 객체 해제")
    }
}

class Capital {
    unowned var country: Country
    init(country: Country) {
        self.country = country
    }
}

var korea: Country! = Country()
var seoul: Capital! = Capital(country: korea)
korea.capital = seoul
korea = nil // Country 객체 해제 

Reference

WWDC Introducing Automatic Reference Counting
WWDC Adopting Automatic Reference Counting
About Memory Management
Memory Management Programming Guide for Core Foundation