🔥 저장 속성

618자
8분

Swift에서 속성은 클래스나 구조체의 일부로 저장되는 상수나 변수를 말합니다. 속성에는 var 키워드로 도입되는 변수 저장 속성let 키워드로 도입되는 상수 저장 속성이 있답니다.

속성을 정의할 때 기본값을 제공할 수 있어요. 초기화 과정에서 저장 속성의 초기값을 설정하고 수정할 수도 있지요. 상수 저장 속성도 초기화 과정에서는 할당이 가능하답니다.

아래 예제는 생성 후에는 범위 길이를 변경할 수 없는 정수 범위를 나타내는 FixedLengthRange 구조체를 정의하고 있어요:

struct FixedLengthRange {
    var firstValue: Int  // 변수 저장 속성
    let length: Int      // 상수 저장 속성
}
 
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 범위는 정수 0, 1, 2를 나타냅니다.
 
rangeOfThreeItems.firstValue = 6
// 이제 범위는 정수 6, 7, 8을 나타냅니다.
swift

FixedLengthRange 인스턴스는 firstValue라는 변수 저장 속성과 length라는 상수 저장 속성을 가지고 있답니다. 위 예제에서 length는 새로운 범위가 생성될 때 초기화되며, 상수 속성이기 때문에 이후에는 변경할 수 없어요.

상수 구조체 인스턴스의 저장 속성

구조체 인스턴스를 생성하여 상수에 할당하면, 변수 속성으로 선언되었더라도 인스턴스의 속성을 수정할 수 없답니다:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 이 범위는 정수 0, 1, 2, 3을 나타냅니다.
 
rangeOfFourItems.firstValue = 6
// firstValue가 변수 속성임에도 에러가 발생합니다.
swift

rangeOfFourItems가 상수(let 키워드 사용)로 선언되었기 때문에, firstValue가 변수 속성임에도 불구하고 firstValue 속성을 변경할 수 없어요.

이러한 동작은 구조체가 값 타입이기 때문이랍니다. 값 타입의 인스턴스가 상수로 표시되면, 모든 속성도 상수가 되는 거죠.

하지만 참조 타입인 클래스에는 이런 규칙이 적용되지 않아요. 참조 타입의 인스턴스를 상수에 할당해도, 여전히 인스턴스의 변수 속성을 변경할 수 있답니다.

지연 저장 속성

지연 저장 속성는 처음 사용되기 전까지 초기값이 계산되지 않는 속성이에요. 선언 앞에 lazy 수정자를 작성하여 지연 저장 속성을 나타낼 수 있지요.

지연 속성은 속성의 초기값이 인스턴스가 생성된 후에야 결정되는 경우에 유용해요. 예를 들어, 초기값이 복잡한 계산이나 외부 요인에 의해 결정될 때 말이죠. 속성의 초기값 설정에 복잡하거나 계산 비용이 많이 드는 설정이 필요한 경우, 꼭 필요할 때까지 수행하지 않는 것이 좋겠죠.

아래 예제는 복잡한 클래스의 불필요한 초기화를 피하기 위해 지연 저장 속성을 사용하고 있어요. 이 예제는 DataImporterDataManager라는 두 클래스를 정의하고 있답니다:

class DataImporter {
    /*
     DataImporter는 외부 파일에서 데이터를 가져오는 클래스입니다.
     이 클래스는 초기화에 적지 않은 시간이 걸린다고 가정합니다.
     */
    var filename = "data.txt"
    // DataImporter 클래스는 여기에 데이터 가져오기 기능을 제공합니다.
}
 
class DataManager {
    lazy var importer = DataImporter()
    var data: [String] = []
    // DataManager 클래스는 여기에 데이터 관리 기능을 제공합니다.
}
 
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// importer 속성을 위한 DataImporter 인스턴스는 아직 생성되지 않았습니다.
swift

DataManager 클래스는 data라는 저장 속성을 가지고 있는데, 이는 새로운 빈 String 배열로 초기화됩니다. DataManager 클래스의 목적은 이 String 데이터 배열을 관리하고 접근을 제공하는 거예요.

DataManager 클래스의 기능 중 일부는 파일에서 데이터를 가져오는 능력이에요. 이 기능은 초기화에 적지 않은 시간이 걸리는 DataImporter 클래스에 의해 제공됩니다. DataImporter 인스턴스는 초기화될 때 파일을 열고 그 내용을 메모리로 읽어들여야 할 수도 있겠죠.

DataManager 인스턴스가 파일에서 데이터를 가져오지 않고도 데이터를 관리할 수 있기 때문에, DataManager 자체가 생성될 때 새로운 DataImporter 인스턴스를 생성하지 않아요. 대신 DataImporter 인스턴스를 처음 사용할 때 생성하는 것이 더 합리적이겠죠.

lazy 수정자로 표시되었기 때문에, importer 속성을 위한 DataImporter 인스턴스는 importer 속성에 처음 접근할 때만 생성됩니다. 예를 들어 filename 속성을 조회할 때 말이에요:

print(manager.importer.filename)
// importer 속성을 위한 DataImporter 인스턴스가 이제 생성되었습니다.
// "data.txt" 출력
swift

저장 속성과 인스턴스 변수

Objective-C 경험이 있다면, 클래스 인스턴스의 일부로 값과 참조를 저장하는 두 가지 방법을 제공한다는 것을 알고 있을 거예요. 속성 외에도 인스턴스 변수를 속성에 저장된 값의 백업 저장소로 사용할 수 있죠.

Swift는 이러한 개념을 하나의 속성 선언으로 통합했어요. Swift 속성에는 대응하는 인스턴스 변수가 없으며, 속성의 백업 저장소에 직접 접근하지 않습니다. 이러한 접근 방식은 값에 접근하는 방법이 다른 컨텍스트에서 혼동을 피하고, 속성의 선언을 간결하고 명확한 한 문장으로 단순화한답니다. 속성의 이름, 타입, 메모리 관리 특성을 포함한 모든 정보가 타입 정의의 일부로 한 곳에 정의되는 거죠.

lecture image

이 그래프를 통해 Swift 속성이 어떻게 통합된 개념인지, 인스턴스 변수와의 차이점은 무엇인지 한눈에 알 수 있겠죠?