Swift에서 lazy
키워드는 성능 최적화와 메모리 효율성을 위해 자주 사용된다. 특히 클로저와 함께 사용할 경우, 초기화 시점과 메모리 관리에 대한 이해가 필요하다. 이번 글에서는 lazy
프로퍼티와 클로저의 관계, 그리고 이를 활용하는 방법을 정리한다.
lazy
란?lazy
는 프로퍼티가 실제로 접근되기 전까지 초기화를 지연시키는 키워드이다. 보통 무거운 연산이 필요한 프로퍼티나 초기화 시점이 중요한 경우에 사용한다.
class Example {
lazy var number: Int = {
print("초기화 실행")
return 10
}()
}
let example = Example()
print("객체 생성 완료")
print(example.number) // 초기화 실행 후 10 출력
print(example.number) // 바로 10 출력 (초기화 재실행 X)
위 코드에서 example.number
가 처음 접근될 때 lazy
프로퍼티가 초기화되며, 이후에는 저장된 값을 사용한다.
lazy
와 클로저 활용lazy
는 클로저와 함께 사용하면 초기화 로직을 간결하게 작성할 수 있다.
class ViewController {
lazy var welcomeMessage: String = {
return "안녕하세요, 소담 앱에 오신 것을 환영합니다!"
}()
}
위와 같이 클로저 내부에서 값을 반환하면, 보다 깔끔하게 초기화할 수 있다.
class DataManager {
init() {
print("데이터 매니저 초기화")
}
}
class ViewController {
lazy var dataManager = DataManager()
}
let vc = ViewController()
print("뷰 컨트롤러 생성 완료")
_ = vc.dataManager // 이때 DataManager 초기화 실행
lazy
를 사용하지 않았다면 vc
가 생성될 때 DataManager
도 함께 초기화되었겠지만, lazy
덕분에 dataManager
가 실제로 필요할 때까지 초기화가 지연된다.
lazy
사용 시 주의할 점let
과 함께 사용할 수 없음class Example {
lazy let number = 10 // ❌ 'lazy'는 'let'과 함께 사용할 수 없음
}
lazy
프로퍼티는 선언 이후 값이 변경될 가능성이 있으므로, let
과 함께 사용할 수 없다. 항상 var
로 선언해야 한다.
lazy
프로퍼티는 구조체(struct)에서 사용할 수 없다.
struct Example {
lazy var text = "Hello" // ❌ 'lazy' properties are only supported in classes
}
구조체는 값 타입(value type)이며, 인스턴스가 복사될 때 모든 프로퍼티가 함께 복사되어야 하므로 lazy
프로퍼티를 사용할 수 없다.
self
를 캡처할 때 주의할 점클로저를 lazy
프로퍼티로 사용할 때 self
를 캡처하는 방식에 주의해야 한다.
class Example {
var name = "소담"
lazy var greeting: String = {
return "안녕하세요, \(self.name)님!"
}()
}
let example = Example()
print(example.greeting) // "안녕하세요, 소담님!" 출력
이렇게 self
를 캡처하면 인스턴스가 메모리에 남아 있을 가능성이 있다. 이를 해결하려면 [weak self]
를 사용하여 강한 참조를 방지할 수 있다.
class Example {
var name = "소담"
lazy var greeting: String = { [weak self] in
guard let self = self else { return "이름 없음" }
return "안녕하세요, \(self.name)님!"
}()
}
이렇게 하면 self
가 해제되었을 때도 안전하게 클로저를 실행할 수 있다.
lazy
와 클로저 활용 사례: 네트워크 요청 결과 캐싱lazy
프로퍼티를 사용하여 네트워크 요청 결과를 캐싱하는 방식도 가능하다.
class APIService {
func fetchData() -> String {
print("데이터 가져오는 중...")
return "서버에서 받은 데이터"
}
}
class ViewModel {
let apiService = APIService()
lazy var cachedData: String = {
return apiService.fetchData()
}()
}
let viewModel = ViewModel()
print("ViewModel 생성 완료")
print(viewModel.cachedData) // 네트워크 요청 발생
print(viewModel.cachedData) // 이전에 저장된 데이터 사용 (네트워크 요청 X)
이렇게 하면 네트워크 요청이 여러 번 발생하는 것을 방지할 수 있다.
lazy
는 프로퍼티가 실제로 필요할 때까지 초기화를 미루는 키워드이다. self
캡처 시 메모리 관리에 주의해야 한다. ([weak self]
사용) 적절한 상황에서 lazy
를 활용하면 성능 최적화와 코드의 가독성을 동시에 잡을 수 있다.