강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

ㅎㅇ님의 프로필 이미지
ㅎㅇ

작성한 질문수

앨런 Swift Concurrency for Swift 6 (Part-2)

ImageProject 관련 문의

해결된 질문

작성

·

31

0

안녕하세요,

ImageProject 관련하여 설명해주신 내용 중 궁금한 점이 있어 문의드립니다.

 

1.

await imageDownloader.cache.keys.contains(url)

let keys = await imageDownloader.cache.keys

if keys.contains(url)

중에

후자를 사용하는 것이 액터 재진입에 있어 더욱 안정적인 코드라고 말씀하셨는데요

액터 재진입 관점에서는 전자의 코드가 actor isolated 된 상태에서 contain 여부까지 확인이 되고

후자의 코드는 key값을 가져오고 contain 여부를 판단하는 시점에 actor에 다른 태스크가 진입하여 값이 변경될 가능성이 있기 때문에

전자가 더 안정적인 코드가 아닌가요?

어떤 이유 때문에 후자의 코드를 사용해야하는지 다시 설명해주시면 감사하겠습니다.

 

 

2.

privatelet storage = DiskStorage()

의 코드는

Global actor 'ImageDatabase'-isolated default value in a actor-isolated context

오류를 발생시켜 storage를 추후에 초기화 하도록 수정해주셨는데요

privatelazyvar storage = DiskStorage()

으로 하면 storage가 호출되는 시점에 초기화 되니 그 때에는 이미 actor의 격리영역이 정해진 상태라 이상이 없을 것이라고 생각했습니다만
같은 오류가 발생하더라구요.

@ImageDatabase 로 격리되어 있기 때문에 비동기적으로 초기화되어야 하지만 그렇지 못하기 때문에 오류가 발생하는 것인지 궁금합니다.

 

감사합니다.

답변 1

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

네 안녕하세요 ㅎㅇ 님.

<1번 질문에 대한 답변>
결국, 해당 코드에서는.. 다른 객체(imageDownloader)가 가지고 있는 캐시에 URL을 가지고 있는 여부의 확인을 하려고 하는 것인데, Swift Concurrency에서는 내부적으로 await 코드의 시점에 재진입 문제가 발생할 수 있다는 것이 문제죠.

결국 "요청 시작" 시점부터 ===> "포함 여부 확인 완료" 시점까지 중간에 await 코드가 포함되어 있는지의 여부가 중요합니다.

1) 번의 경우

let keys = await imageDownloader.cache.keys
        
if keys.contains(url) {
     return try await imageDownloader.image(from: url)
}

요청 시점 ==== await (O) ====> 캐시 정보(keys) 가져옴 ==== await (X) ====> 포함 여부

 

요청 시점에서 캐시 정보를 가져 올때까지는 await 코드가 있긴 하지만, 실제 캐시 정보를 확인한 시점 이후에는 그 캐시 정보가 바뀌지 않는다는 것을 가정하는 코드라고 보시면 되고요. (따라서, 일단 캐시 정보의 스냅샷을 확인한 시점이후 캐시 데이터는 변할 틈이 없다고 보면 됨)

 

2) 번의 경우

if await imageDownloader.cache.keys.contains(url) {
      return try await imageDownloader.image(from: url)
}

요청 시점 ==== await (O) ====> contains호출 시점 ==== await (O) ====> 포함 여부

 

2번의 경우는.. 어떻게 보면 요청 시점부터 확인 시점까지 전 구간에 걸쳐서, await코드가 있는 것이기 때문에.. 해당 객체(imageDownloader)에 여러 요청(메서드 호출 등)이 있다면, 실제 contains메서드를 호출한 시점부터도 재진입 문제가 발생할 수 있다는 것이 문제라고 보시면 됩니다.

 

(쉽게 말씀드리면.. (실제 코드가 그렇다는게 아니라) 논리적으로 아래 코드와 비슷하다는 뜻입니다.)

let keys = await imageDownloader.cache.keys
let isCashed = await keys.contains(url)        
if isCashed {
     return try await imageDownloader.image(from: url)
}

그렇기 때문에, 재진입 문제와 관련해서는 1번 코드가 더 안정적인 코드라고 말씀드리고 있는 겁니다.

 

<2번 질문에 대한 답변>

네네, lazy var로 선언하시더라도 (글로벌 액터로 격리된) DiskStorage 클래스의 경우, DiskStorage타입의 존재 자체가 ImageDatabase에 의존하고 있기 때문에 발생하는 문제라고 보시면 됩니다.


ImageDatabase가 먼저 초기화 되어야 ==> DiskStorage 존재 가능 ==> 존재하려면 ImageDatabase 필요

이런식으로 순환해서 의존(필요)하고 있기 때문이라고 보시면 될 것 같습니다. lazy로 선언했다고 해서 어차피 컴파일러 자체가 논리적인 모든 순서를 파악할 수 있는 것은 아니기 때문에, 컴파일러는 DiskStorage 타입 자체의 초기화 문제로 바라보고 있는 것입니다.

 

 

감사합니다. :)

ㅎㅇ님의 프로필 이미지
ㅎㅇ

작성한 질문수

질문하기