궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Grammar

Swift : 고급문법 [ARC 메모리 관리 3 - 약한참조(weak)]

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

본 게시글은 yagom님의 Swift 프로그래밍 3판을 참고하여 작성되었습니다.

 

약한 참조

  • 강한 참조와 달리 자신이 참조하는 인스턴스의 참조 횟수를 증가시키지 않음
  • 참조 타입의 프로퍼티나 변수의 선언 앞에 weak 키워드를 사용하면 그 프로퍼티나 변수는 자신이 참조하는 인스턴스를 약한 참조함
  • 약한 참조를 사용하면 자신이 참조하는 인스턴스가 메모리에서 해제될 수 도 있다는 것을 예상할 수 있음
  • 참조하는 인스턴스에 대한 참조를 강하게 유지하지 않기 때문에 약한 참조로 참조되고 있는 동안에도 해당 인스턴스가 할당 해제될 수 있음
  • ARC는 인스턴스가 할당 해제될 때 해당 인스턴스를 약한 참조하는 프로퍼티를 nil로 초기화함
  • 때문에 약한 참조는 항상 옵셔널 변수에만 사용 가능하며, 약한 참조는 상수에서 쓰일 수 없음

TIP
 
 

약한 참조와 상수, 옵셔널
만약 자신이 참조하던 인스턴스가 메모리 해제되면 nil이 할당될 수 있어야 하기 때문에 약한 참조는 상수에서 쓰일 수 없다. 그래서 약한 참조를 할 땐 자신의 값을 변경할 수 있는 변수로 선언해야 한다. 또, nil이 할당될 수 있어야 하므로 약한참조는 당연히 항상 옵셔널이여야 한다. 즉, 옵셔널 변수에만 약한참조를 할 수 있다.

강한 참조 순환에서 사용했던 코드

<hide/>
class Person {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    var room: Room?
    deinit {
        print("\(name) is being deinitialized!")
    }
}
 
class Room {
    let number: String
    
    init(number: String) {
        self.number = number
    }
    var host: Person?
    
    deinit {
        print("Room \(number) is being deinitialized!")
    }
}

var seogun: Person? = Person(name: "서근") // Person 인스턴스의 참조 횟수 : 1
var myRoom: Room? = Room(number: "50") // Room 인스턴스의 참조 횟수 : 1
 
seogun?.room = myRoom // Room 인스턴스의 참조 횟수 : 2
myRoom?.host = seogun // Person 인스턴스의 참조 횟수 : 2
 
seogun?.room = nil // Room 인스턴스의 참조 횟수 : 1
myRoom?.host = nil // Person 인스턴스의 참조 횟수 : 1
 
seogun = nil // Person 인스턴스의 참조 횟수 : 0
// 서근 is being deinitialized!
 
myRoom = nil // Room 인스턴스의 참조 횟수 : 0
// Room 50 is being deinitialized!

 

강한 참조 순환에서 사용했던 Room 클래스에서 host 프로퍼티를 약한 참조로 바꿔보면 다음과 같다.

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    var room: Room?
    deinit {
        print("\(name) is being deinitialized!")
    }
}

class Room {
    let number: String
    
    init(number: String) {
        self.number = number
    }
    //var host: Person?
    weak var host: Person?
    
    deinit {
        print("Room \(number) is being deinitialized!")
    }
}

그리고 seogunmyRoom의 관계는 이러했다.

var seogun: Person? = Person(name: "서근") // Person 인스턴스의 참조 횟수 : 1
var myRoom: Room? = Room(number: "50") // Room 인스턴스의 참조 횟수 : 1
 
seogun?.room = myRoom // Room 인스턴스의 참조 횟수 : 2
myRoom?.host = seogun // Person 인스턴스의 참조 횟수 : 1 (약한 참조니까!!!!)

Person 인스턴스는 Room 인스턴스에 대에 강한 참조를 여전히 유지하고 있는데 반해, Room인스턴스는 Person 인스턴스에 대해 약한 참조를 가지게 됐다.

 

이는 seogun 변수에 nil을 할당해 Person 인스턴스에 대한 강한 참조를 해제하게 된다면, Person 인스턴스는 더 이상 강한 참조를 갖지 않게 된다.

 

그러니까 PersonRoom 클래스는 둘 다 강한 참조이고, myRoom 변수가 Room 인스턴스에 강한 참조 했고, 이것을 Person 인스턴스에 약한 참조로 전달했으니(참조 횟수 증가 안 함) Person 인스턴스는 var seogun 변수를 제외한 모든 인스턴스가 약한 참조인 것! 

 

여기서 유일한 강한 참조인 var seogunnil을 주면? 

더 이상 Person 인스턴스에 대한 강한 참조가 존재하지 않으므로, Person 인스턴스가 할당 해제 되었고, Person 인스턴스에 대해 약한 참조를 가지고 있던 host 프로퍼티는 nil이 된다.

print(myRoom?.host) //옵셔널 Person

seogun = nil  // Person 인스턴스 참조 횟수 : 0 , Room 인스턴스의 참조 횟수 : 1
//서근 is being deinitialized!

print(myRoom?.host) //nil

hostnil 이냐? Person이 존재 하지 않으니까!

weak var host: Person? //Person이 존재하지 않으니까 host도 nil

이렇게 Room 인스턴스에 대한 강한 참조는 myRoom 변수가 가지는 강한 참조만 남게 된다. 이 강한 참조 마저 해제해 버린다면? Room 인스턴스는 더 이상 강한 참조를 가지지 않게 되면서 디이니셜라이저를 호출한다.

myRoom = nil // Room 인스턴스의 참조 횟수 : 0
//Room 50 is being deinitialized!

전체적인 코드를 보면 이러하다.

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    var room: Room?
    deinit {
        print("\(name) is being deinitialized!")
    }
}

class Room {
    let number: String
    
    init(number: String) {
        self.number = number
    }
    weak var host: Person?
    
    deinit {
        print("Room \(number) is being deinitialized!")
    }
}

var seogun: Person? = Person(name: "서근") // Person 인스턴스의 참조 횟수 : 1
var myRoom: Room? = Room(number: "50") // Room 인스턴스의 참조 횟수 : 1

seogun?.room = myRoom // Room 인스턴스의 참조 횟수 : 2
myRoom?.host = seogun // Person 인스턴스의 참조 횟수 : 1 (약한 참조니까!!!!)


print(myRoom?.host) //옵셔널 Person

seogun = nil // Person 인스턴스의 참조 횟수 : 0 , Room 인스턴스의 참조 횟수 : 1
//서근 is being deinitialized!

print(myRoom?.host) //nil

myRoom = nil // Room 인스턴스의 참조 횟수 : 0
//Room 50 is being deinitialized!

 

다음 게시글은 미소유 참조에 대해 알아보도록 하겠습니다.

 

 

읽어주셔서 감사합니다 🤟

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.