Lazy variables
이전의 글들을 보셨거나 스위프트 문법 공부를 해보신 분들이라면 스위프트에서 메모리는 굉장히 예민한 주제인 것을 알 수 있습니다. 저 역시 그렇게 느꼈고, 그런 예민함이 보다 메모리를 효율적으로 관리할 수 있는 방향으로 이어졌습니다. 오늘은 이러한 메모리와 관련된 문법 중 하나인 lazy
에 대해 알아보도록 하겠습니다.
이전에 작성한 메모리 관리에 관한 글도 읽어보시기 바랍니다.
애플의 공식 문서에서는 이렇게 설명하고 있습니다.
"A lazy stored property is a property whose initial value is not calculated until the first time it is used"
직역을 해보자면 lazy
변수는 처음 사용되기 전까지는 연산이 되지 않는다는 것입니다. 이를 보다 이해하기 쉽게 설명해보도록 하겠습니다.
인스타그램을 예로 들어보도록 하겠습니다. 인스타그램을 실행시키면 가장 상단에 본인의 팔로워들의 아이콘들이 존재하고 이 아이콘을 클릭하면 해당 유저가 올린 스토리 영상을 확인할 수 있습니다. 여러분이 인스타그램을 실행시켰을 때 이러한 영상들도 모두 서버로부터 가져오는 작업을 할까요? 만일 여러분이 어플리케이션을 실행하는 동안 한번도 스토리들을 클릭하여 확인하지 않는다면 이는 쓸데 없이 메모리만 차지하고 어플리케이션을 실행할 때 딜레이도 존재할 수 있습니다. 해당 영상들은 여러분이 클릭했을 때만 보여주기만 하면 되기 때문입니다.
즉, 처음 사용되기 전까지는 로드되지 않다가 영상을 보려고 클릭하면 해당 영상들이 서버로부터 로드되는 것입니다.
실제로
lazy
를 사용하지 않아도 이해를 돕기 위해 해당 예를 사용하였습니다.
이처럼 필요하지만 사용하기도 전에 불러오기 부담스러운 것들은 lazy
를 통해 선언해주는 것입니다.
lazy
를 사용하기 위해서는 다음과 같은 고려사항이 필요합니다.
lazy
는 반드시var
와 함께 쓰여야 한다.
lazy
는 반드시var
와 사용되어야 합니다. 이는 한번만 생각해보시면 쉽게 이해하실 수 있습니다. 기본적으로lazy
로 선언된 변수는 초기에는 값이 존재하지 않고 이후에 값이 생기는 것이기 때문에let
으로는 선언될 수 없습니다.struct
,class
기본적으로lazy
는struct
와class
에서만 사용할 수 있습니다lazy
vsComputed Property
Computed Property
에는lazy
키워드를 사용할 수 없습니다.lazy
는 처음 사용될 때 메모리에 값을 올리고 그 이후 부터는 계속해서 메모리에 올라온 값을 사용합니다. 사용할 때마다 값을 연산하여 사용하는Computed Property
에는 사용할 수 없습니다.lazy
andclosure
lazy
에 어떤 특별한 연산을 통해 값을 넣어주기 위해서는 코드 실행 블록인closure
를 사용합니다.
class
나struct
의 다른 프로퍼티의 값을lazy
변수에서 사용하기 위해서는closure
내에서self
를 통해 접근이 가능합니다. 기본적으로 일반 변수들은 클래스가 생성된 이후에 접근이 가능하기 때문에 클래스내의 다른 영역(메소드, 일반 프로퍼티)에서는self
를 통해 접근할 수 없지만lazy
키워드가 붙으면 생성 후 추후에 접근할 것이라는 의미이기 때문에closure
내에서self
로 접근이 가능합니다.
class Person {
var name:String
lazy var greeting:String = {
return "Hello my name is \((self.name))"
}()
init(name:String){
self.name = name
}
}
var me = Person(name:"John")
print(me.greeting // Hello my name is John
me.name = "James"
print(me.greeting // Hello my name is John
lazy var greeting
을 보시면 클로저 내부에서 self.name
을 통해 name
프로퍼티에 접근하는 것을 보실 수 있습니다. 이전 포스팅을 보셨던 분들이라면 이렇게 클래스 내부의 클로저에서 클래스 객체를 self
로 참조한다면 메모리 누수가 발생하는 위험이 있다는 것을 아실 것입니다. 하지만 뒤의 ()
를 통해 그 즉시 실행하고 결과를 돌려주고 끝나버리기 때문에 메모리 누수의 걱정은 없습니다
또한 프로퍼티가 James
으로 변경이 되어도 처음 사용할 때 메모리에는 John
이 올라가 있기 때문에 John
이 출력이 됩니다.
class Person {
var name:String
lazy var greeting: ()->String = { [weak self] in
return "Hello my name is \(((self?.name))!)"
}
init(name:String){
self.name = name
}
}
var me = Person(name:"John")
print(me.greeting()) // Hello my name is John
me.name = "James"
print(me.greeting()) // Hello my name is James
만일 변수가 lazy var greeting:String
이 아닌 lazy var greeting:()->String
으로 클로저 실행의 결과를 담는 것이 아닌 클로저 자체를 담고 있는 변수라면 반드시 [weak self]
를 통해 메모리 누수를 방지해주어야 합니다.
또한 값이 아닌 클로저 자체가 메모리에 올라가 있고 self
는 내부에서 계속해서 클래스를 참조하기 때문에 계속 John
이 출력이 되는 것이 아닌 James
가 출력이 되는 것입니다.
마무리
오늘은 이렇게 간단하게 lazy
에 대해 알아보았습니다. 계속해서 iOS 제작에 관련된 포스팅을 올리지만 이렇게 스위프트 자체의 문법을 아는 것이 중요하기 때문에 문법 공부를 게을리하지 않고 지속해서 포스팅하도록 노력하겠습니다. 감사합니다.
참고자료