티스토리 뷰

iOS

[Swift] 프로퍼티 (Property) (1/3)

Peppo 2022. 3. 2. 07:37
728x90

이전에 프로퍼티에 대해 블로깅을 했었지만

복습겸 공식문서를 보고 한번더 정리해보려고 합니다.

 


프로퍼티 

프로퍼티는 클래스, 구조체, 열거형과 관련한 값입니다.

 

추가로, 프로퍼티 옵저버를 정의하여 값의 변경사항을 모니터링할 수 있으며, 사용자 지정작업으로 응답할 수 있습니다. 

프로퍼티 옵저버는 사용저가 정의한 저장프로퍼티와 하위 클래스가 상위클래스에서 상속받는 프로퍼티에 추가될 수 있습니다. (질문)

 

종류

  • 저장 프로퍼티 (Stored Properties) - 값을 저장하고 있는 프로퍼티 ( 클래스, 구조체
  • 연산 프로퍼티 (Computed Properties) - 값을 저장하지 않고 계산 값을 반환해주는 프로퍼티 ( 클래스, 구조체, 열거형 )

 


저장 프로퍼티 (Stored Properties)

값을 저장하고 있는 프로퍼티로, 

상수(let), 변수(var)를 선언해 사용할 수 있습니다.

 

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

 

위 예제는 firstValue와 length에 각각 프로퍼티에 값을 저장해 범위 값을 표현합니다.

 

 

상수 구조체 인스턴스의 저장 프로퍼티 (Stored Properties of Constant Structure Instances)

 

상수로 인스턴스화한 구조체는 해당 인스턴스의 프로퍼티를 수정할 수 없습니다. 

( 수정하는 프로퍼티가 변수더라도 수정 불가 )

 

//struct FixedLengthRange {
//    var firstValue: Int
//    let length: Int
//}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)  // let으로 변경
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6  // 에러
// this will report an error, even though firstValue is a variable property

 

이유는 struct가 값 타입이기 때문입니다. 

값 타입의 인스턴스가 상수로 선언되면, 모든 프로퍼티의 경우 수정할 수 없게 됩니다. 

 

반대로, class의 경우 참조 타입이기 때문에 프로퍼티 값을 변경할 수 있습니다.

 

 

 

지연 저장 프로퍼티 (Lazy Stored Properties)

값이 처음 사용되기까지 계산되지 않는 프로퍼티 입니다. 

지연 저장 프로퍼티는 lazy 키워드를 사용하여 나타낼 수 있습니다.

 

NOTE

lazy
프로퍼티는 항상 var 키워드와 함께 써야합니다. 
상수는 (let) 초기화 되기전에 항상 값을 갖고 있어야 하므로, lazy와 함께 쓰이면 안됩니다.

 

유용하게 사용될 때 

  • 외부요인에 의존적이어서 인스턴스의 초기화 완료까지 값을 알지 못하는 경우.
  • 복잡한 계산이나 부하가 많이 걸리는 작업일 경우.

 

아래 예제는 lazy 저장 프로퍼티를 써서 복잡한 클래스의 불필요한 초기화를 피하도록 하기 위해 사용되었습니다.

 

class DataImporter {
    // DataImporter는 외부파일에서 데이터를 가지고 오는 클래스
    // 이 클래스는 초기화 하는데 많은 시간이 걸리고, 데이터를 가져오는 기능을 한다고 가정
    var filename = "data.txt"
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // 이 클래스는 데이터관리 기능이 있다고 가정
}

let manager = DataManager()
manager.data.append("data1")
manager.data.append("data2")
// DataImporter 인스턴스는 이 시점에 생성되어 있지 않습니다. (lazy 프로퍼티 때문)

 

위의 코드를 풀이 해보자면

DataManager 클래스에 DataImporter라는 클래스를 인스턴스화 해서 갖고 있습니다. 

DataImporter는 데이터파일을 가져와 많은 시간이 걸리기 때문에, 

lazy var importer = DataImporter()

lazy 프로퍼티로 선언을 해줌으로써 DataManager가 호출되어도 importer가 불려지지 않는이상 실행 되지 않습니다. 

 

아래와 같이 manger.importer로 직접 접근하면 그제서야 importer 인스턴스가 생성됩니다.

print(manager.importer.filename) // importer 호출
// DataImporter 인스턴스 생성
// print: data.txt

 

NOTE

lazy 프로퍼티가 여러 스레드에서 접근되고, 프로퍼티가 아직 초기화 되지 않았을때
프로퍼티가 한번만 초기화 될거라는건 보장할수 없습니다. (질문)

 

저장 프로퍼티와 인스턴스 변수 (Stored Properties and Instance Variables)

Objective-C의 경우 값을 저장하는방법과 메모리 관리 개념도 프로퍼티에 함께 명시한다고 하는데

 

Swift에서는 프로퍼티의 선언사용에 혼란을 피하기위해 

프로퍼티의 이름, 타입, 메모리 관리등 모든 정보를 프로퍼티 선언하는 곳에서 정의하게 됩니다. 

 


연산 프로퍼티 (Computed Properties)

 

저장프로퍼티 뿐만 아니라 클래스, 구조체, 열거형에서 연산프로퍼티를 선언할 수 있습니다.

연산프로퍼티는 실제로 값을 저장하지 않는 대신,  다른 프로퍼티와 값을 간접적으로 탐색하고 설정하기 위해 getter와 옵셔널한 setter를 제공합니다. 

 

아래는 width: 10, height: 10인 사각형(square) 가운데(center)의 좌표를 (x: 0, y: 0) -> (x: 15, y: 15) 이동 시키는 예제 입니다. 

 

그림으로 먼저 보면

struct Point {
    var x = 0.0, y = 0.0
}

struct Size {
    var width = 0.0, height = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}

// 1
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
                  
// 2                  
let initialSquareCenter = square.center
// initialSquareCenter is at (5.0, 5.0)

// 3
square.center = Point(x: 15.0, y: 15.0)

코드 풀이

더보기

위 예제에 세가지 구조체 (Point, Size, Rect)가 있습니다. 

  • Point 는 좌표 (x, y) 
  • Size 는 크기 (width, height)
  • Rect 는 
    • origin (좌표)
    • size (크기)
    • center (가운데) 연산 프로퍼티가 있습니다. 

1. 변수 square 선언 - Point 값 (x: 0.0, y: 0.0), Size 값 (width: 10.0, height: 10.0)

2. initialSquareCenter = square.center 부분을 보면 

    centerX = origin.x(0) + (size.width(10.0) / 2) 

    centerY = origin.y(0) + (size.height(10.0) / 2)

    return Point(x: 5.0, y: 5.0) 가 됩니다.

 

3. square.center = Point(x: 15.0, y: 15.0)  x, y 값을 바꿔줬네요.

    center의 값을 바꿔준거니 set으로 이동해서 newCenter는 아래처럼 연산됩니다.

    origin.x = newCenter.x (15) - (size.width(10) / 2)

    origin.y = newCenter.y (15) - (size.height(10) / 2) 

 

   고로 square의 현재 좌표는 (x: 10, y: 10) 크기는 (width: 10, height: 10)이 됩니다.

 

 

간략한 Setter 선언 (Shorthand Setter Declaration)

 

위 예제에서 setter 부분에 (newCenter)로 이름을 지정해주지 않으면, 

newValue라는 기본값으로 사용할 수 있습니다.

아래는 set (newCenter) 를 사용하지 않은 예제 입니다. 

 

struct Point {
    var x = 0.0, y = 0.0
}

struct Size {
    var width = 0.0, height = 0.0
}

struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

 

 

읽기전용 연산 프로퍼티 (Read-Only Computed Properties)

 

getter만 있는 연산 프로퍼티는 읽기 전용 연산 프로퍼티로 알려져 있습니다. 

읽기 전용 연산 프로퍼티는 항상 값을 반환하며,  .(점 문법)으로 접근할 수 있지만, 다른 값을 지정할 수는 없습니다. 

 

NOTE

읽기전용 연산 프로퍼티를 포함한 연산프로퍼티들은 값이 고정되어 있지 않기 때문에 var키워드로 선언해야 합니다.
(계산 값에 따라 값이 변할수 있기 때문에)

 

아래는 읽기전용 연산 프로퍼티를 사용한 예제 입니다.

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}

let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of. ourByFiveBy Two is \(fourByFiveByTwo.volume)")

// the volume of. ourByFiveBy Two is 40.0

 

width, heigth, depth 를 가지고 연산을 하기 때문에 값이 항상 바뀌므로 var 키워드 사용

 


다음 블로깅에서는 

프로퍼티 옵저버

타입 프로퍼티

에 대해 블로깅 해보겠습니다. 

728x90