[iOS] weak VS unowned

유인호·2024년 3월 5일
0

iOS

목록 보기
35/64

0. 서론

ARC를 이해하고나서 weak와 unowned를 아무리 생각해도 이해할 수 없었는데, 드디어 오늘 이해하여 게시물로 생각을 다시 한번 정리해 보았음.

0.5. 시작전에...

weak와 unowned가 RC를 올려주지 않는다는건 알고 있었음. 근데, 변수로 담으면 사용할 수 있으니까 RC를 올려주는거 아닌가? 같은 의문으로 햇갈리기 시작했음.

그러니까.. 처음에 이해했을땐 weak와 unowned는 RC를 한 0.5 정도 올려주는건가? 라는 생각으로 지금까지 살아왔는데, 오늘 실험으로 그 신념이 깨졌음.

class Guild {
	var name: String

	init(name: String) {
		self.name = name
	}

	deinit { print("Guild Deinit \(name)") }
}

weak var guild: Guild? = Guild(name: "Mcflurry")

Guild를 만들고 weak로 만들면, guild를 통해서 name에 접근할 수 있을 줄 알았는데, 선언하자마자 deinit이 실행이 되어 사용하지 못하게 됨.

그러니까, init되자마자 deinit이 되어버린것.

그리고 한가지 더 햇갈렸던것. deinit시 내부 프로퍼티를 가지고 나갈수 있나 해서 해봤는데, 아주 잘 실행되는걸 볼 수 있었다.

Guild Deinit Mcflurry

그럼 이제 시작

1. weak와 unowned의 공통점

은 솔직히 다른 곳에서도 좋은 설명 많으니까, 이번에 이해한 차이점을 위주로 적어보겠음. 그래도 단순 나열 하자면,

  • RC를 늘리지 않는다.
  • 따라서 순환참조를 방지한다.

2. weak와 unowned의 차이점

머리속으로 차이점은 이해하고 있었는데, 아직 가슴으로는 이해하지 못하고 있었다. 실험 전 weak에 대해 알고있는 지식으로는,

  • weak는 언제든 nil이 될 수 있다.
  • 그래서 weak는 무조건 옵셔널에 var로 선언해야 한다.
  • 본체보다 가지고 있는 프로퍼티(weak var)가 먼저 nil이 될 수 있을때 사용한다.

마지막줄이 조금 가슴으로 이해가 덜됬는데 코드를 보자면,

class Guild {
	var name: String
	weak var king: User?

	init(name: String) {
		self.name = name
	}

	deinit { print("Guild Deinit \(name)") }
}

class User {
	var nick: String

	init(nick: String) {
		self.nick = nick
	}
	deinit { print("User Deinit") }
}

var guild: Guild? = Guild(name: "Mcflurry")
var user: User? = User(nick: "프로미스나인")

guild?.king = user

user = nil // print -> User Deinit

print(guild?.king)

guild에서도 user를 바라보고 있으니 RC가 2가 되어서, user를 nil로 할당해도 꺼지지 않아야 하는데, weak로 선언했으므로 user의 RC는 nil로 할당한 시점에서 0이 되어 deinit이 실행되었고, deinit이 되었기 때문에 guild?.king에 접근했을때 nil이 나온다.

그렇다면 unowned는?
보기 전에 전제를 한번 깔아보도록 하자.

Guild의 king은 무조건 존재한다.

이 전제를 깔고 다시 코드를 보겠다.

class Guild {
	var name: String
	unowned var king: User?

	init(name: String) {
		self.name = name
	}

	deinit { print("Guild Deinit \(name)") }
}

class User {
	var nick: String

	init(nick: String) {
		self.nick = nick
	}
	deinit { print("User Deinit") }
}

var guild: Guild? = Guild(name: "Mcflurry")
var user: User? = User(nick: "프로미스나인")

guild?.king = user

user = nil

print(guild?.king)

이대로 실행하게 되면 마지막 print에선 다음과 같은 오류 메시지가 출력된다.

Fatal error: Attempted to read an unowned reference but object 0x60000021af20 was already deallocated

코드를 설명하자면, 앞서 전제가 Guild에는 King이 무적권 있어야 하는데, user가 nil이 되어버려서 king이 사라져버린것.

여기서 weak와 unowned의 차이를 알 수 있는데, weak는 대상이 nil이 되어버리면 자동으로 nil을 할당하면서 프로퍼티에서 갖고 있는 메모리 주소도 자동으로 삭제해 버리지만, unowned는 그렇지 않다. RC가 줄어들어 deinit이 됨은 분명하나, nil이 되었음에도 불구하고 프로퍼티에서 메모리 주소를 아직 갖고 있는 상태가 된다.

따라서 unowned는, 나 자신(Guild)보다 상대(user)가 더 메모리상에서 수명이 길어야 할때 사용해야한다.

profile
🍎Apple Developer Academy @ POSTECH 2nd, 🌱SeSAC iOS 4th

0개의 댓글