Swift - Struct / Class (1_초기화, 프로퍼티, 인스턴스)

이한솔·2023년 8월 4일
0

Swift 문법 🍎

목록 보기
13/32

💡 스위프트는 객체지향 언어이다.
객체를 만들어내는 주요 대상이 구조체와 클래스이다.

구조체와 클래스의 정의 구문

// 구조체의 정의 형식
struct 구조체이름 {
    property
    method
    initializer
    subscript
}


struct Resolution {
    var width = 0 
    var height = 0
    
    func desc() -> String {
        return "Resolution 구조체"
    }
}


// 클래스의 정의 형식
class 클래스이름 {
    property
    method
    initializer
    deinitialize
    subscript
}

class VideoMode {
    var interlaced = false
    var frameRate = 0.0
    var name: String?
    
    func desc() -> String {
        return "VideoMode 클래스"
    }
}


초기화

옵셔널 타입으로 선언되지 않은 프로퍼티는 명시적으로 초기화 해야한다.
1. 프로퍼티를 선언하면서 동시에 초기값을 지정
2. 초기화 메서드 내에서 프로퍼티의 초기값을 지정

구조체

모든 프로퍼티의 값을 인자값으로 입력받아 초기화하는 기본 초기화 구문을 사용할 수 있다. 프로퍼티를멤버 변수라고 부르므로 이 초기화 구문은멤버와이즈 초기화 구문이라고 한다.
구조체의 프로퍼티 초기화는 멤버와이즈 초기화 구문을 사용하거나 초기값이 지정되어있어야 한다.

// 구조체 멤버와이즈 초기화 구문 (Memberwise Initializer)
// width와 height를 매개변수를 하여 Resolution 인스턴스를 생성
let defaultRes = Resolution(width: 1024, height: 768)

print("width = \(defaultRes.width), height = \(defaultRes.height)")
// 출력값: width = 1024, height = 768

클래스

구조체와 달리 멤버와이즈 초기화 구문을 사용할 수 없다.
모든 프로퍼티는 빈 괄호 형태의 기본 초기화 구문으로 정의할 때 초기값을 주던가 옵셔널 타입으로 선언해야한다.



프로퍼티

구조체나 클래스 내에서 정의된 변수나 상수

💡 인스턴스 프로퍼티
저장 프로퍼티, 연산프로퍼티
프로퍼티가 인스턴스에 관련된 값을 저장하고 다루므로 클래스 또는 구조체 인스턴스를 생성한 후 인스턴스를 통해서만 참조할 수 있다.

💡 타입 프로퍼티
인스턴스에 관련된 값이 아니라 클래스나 구조체, 또는 열거형과 같은 객체 자체에 관련된 값을 다룰때 인스턴스를 생성하지 않고 클래스나 구조체 자체에 값을 저장한 것이다.


저장 프로퍼티

상수 및 변수를 사용해서 입력된 값을 저장하거나 저장된 값을 제공하는 역할을 한다.
클래스와 구조체에서는 사용할 수 있고, 열거형에서는 사용할 수 없다.

// 고정 길이 범위 구조체
struct FixedLengthRange {
    var startValue: Int // 시작값
    let length: Int // 값의 범위
}

// 가변 길이 범위 구조체
struct FlexibleLengthRange {
    let startValue: Int // 시작값
    var length: Int // 값의 범위
}

// 구조체 인스턴스는 정수값 0,1,2를 의미
var rangeOfFixedIntegers = FixedLengthRange(startValue: 0, length: 3)

// 아래처럼 시작값을 변경하면 객체 인스턴스는 정수값 4,5,6을 의미
rangeOfFixedIntegers.startValue = 4

// 아래 구조체 인스턴스는 정수값 0,1,2를 의미 
var rangeOfFlexibleIntegers = FlexibleLengthRange(startValue:0, length: 3)

// 아래처럼 범위값을 변경하면 객체 인스턴스는 정수값 0,1,2,3,4를 의미
rangeOfFlexibleIntegers.length = 5

구조체는 값 타입이기 때문에 상수로 선언된 구조체 인스턴스는 내부의 변수 프로퍼티도 변경할 수 없다.

struct MyStruct {
    var variableProperty: Int
    let constantProperty: Int
}

var myStructInstance = MyStruct(variableProperty: 10, constantProperty: 20)
myStructInstance.variableProperty = 30 // 가능
myStructInstance.constantProperty = 40 // 불가능, 컴파일 에러

let myConstantStructInstance = MyStruct(variableProperty: 10, constantProperty: 20)
myConstantStructInstance.variableProperty = 30 // 불가능, 컴파일 에러
myConstantStructInstance.constantProperty = 40 // 불가능, 컴파일 에러

클래스는 참조 타입이기 때문에 상수로 선언된 클래스 인스턴스는 내부의 변수 프로퍼티를 변경할 수 있다. 이는 상수로 선언된 것은 인스턴스의 참조(주소)이며, 인스턴스 내의 프로퍼티는 변수로 선언된 경우 변경 가능하기 때문이다.

class MyClass {
    var variableProperty: Int
    let constantProperty: Int
    
    init(variableProperty: Int, constantProperty: Int) {
        self.variableProperty = variableProperty
        self.constantProperty = constantProperty
    }
}

var myClassInstance = MyClass(variableProperty: 10, constantProperty: 20)
myClassInstance.variableProperty = 30 // 가능
myClassInstance.constantProperty = 40 // 불가능, 컴파일 에러

let myConstantClassInstance = MyClass(variableProperty: 10, constantProperty: 20)
myConstantClassInstance.variableProperty = 30 // 가능
myConstantClassInstance.constantProperty = 40 // 불가능, 컴파일 에러


연산 프로퍼티

실제 값을 저장했다가 반환하지는 않고 대신 다른 프로퍼티의 값을 연산 처리하여 간접적으로 값을 제공한다.
변수로만 쓸 수 있고 클래스, 구조체, 열거형 모두 사용 가능하다.

class/struct/enum 객체명 {

    var 프로퍼티명: 타입 {
        get {
            필요한 연산 과정
            return 반환값
        }
        set(매개변수명) {
            필요한 연산구문
        }
    }
}


var x = 10
var y = 20

var sum: Int {
    get {
        return x + y
    }
    set {
        sum = x + y
    }
}
// get: 연산 프로퍼티
// set: 타입 프로퍼티
// 프로퍼티의 값을 얻기 위해 get구문을 쓰고, set구문으로 값으로 설정해 타입프로퍼티로도 쓸 수 있다. 
// set구문은 선택이지만 get구문은 꼭 써야한다.

print(sum)
// 출력값: 30


타입 프로퍼티

인스턴스를 생성하지 않고 클래스나 구조체 자체에 저장한 값으로 모든 인스턴스가 공통으로 사용할 수 있다. 이 값은 복사된 값이 아니라 실제 하나의 값이기 때문에 하나의 인스턴스에서 값을 변경하면 나머지 인스턴스의 값도 변경된다.

static: 자식 클래스에서 재정의 불가능, 구조체는 상속이 불가능하므로 static 키워드만 사용해서 타입 프로퍼티 및 메서드 정의
class: 자식 클래스에서 재정의(override) 가능

static let/var 프로퍼티명 = 초기값
// 타입 프로퍼티로 선언하려면 사용할 프로퍼티 앞에 static 키워드를 추가하면 된다.

// 타입 프로퍼티는 인스턴스 생성 과정이 없으므로 반드시 초기값을 할당해야한다.
struct structProperty {
    // 타입 저장 프로퍼티
    static var a = "구조체 타입 프로퍼티값"
    
    // 타입 연산 프로퍼티
    static var b: Int {
        return 1
    }
}

print(structProperty.a) // 출력값: 구조체 타입 프로퍼티값

structProperty.a = "새로운 값"
print(structProperty.a) // 출력값: 새로운 값


class classProperty {
    // 타입 저장 프로퍼티
    static var a = "클래스 타입 프로퍼티값"
    
    // 타입 연산 프로퍼티
    static var b: Int {
        return 10
    }
    
    // 재정의가 가능한 타입 연산 프로퍼티
    class var oFoo: Int {
        return 100
    }
}

// class 키워드를 사용하여 정의한 oFoo는 classProperty클래스를 상속받는 하위 클래스에서 재정의 할 수 있는 타입 프로퍼티이다.
// 타입 프로퍼티는 인스턴스를 생성한 다음 점 구문을 이용하여 읽으려고 하면 선언되지 않은 프로퍼티라는 오류가 발생한다. 
// 타입 프로퍼티는 반드시 클래스나 구조체, 또는 열거형 자체와 같이 사용해야 한다.

print(classProperty.a) // 출력값: 클래스 타입 프로퍼티값
print(classProperty.b) // 출력값: 10


프로퍼티 옵저버

특정 프로퍼티를 계속 관찰하고 있다가 프로퍼티의 값이 변경되면 알아차리고 반응한다. 프로퍼티의 값이 설정되거나 동일한 값이 재할당되더라도 호출된다.

willSet

저장되기 직전 값 호출된다. 새로운 값이 newValue가 된다.

didSet

새 값이 저장된 직후에 값 호출된다. 이전 값이 oldValue가 된다.

class StepCounter {
    var totalSteps: Int = 200 {
    
        willSet(newTotalSteps) {
            print("\(newTotalSteps)로 총 단계를 설정하려고 합니다.")
        }
        
        didSet {
            if totalSteps > oldValue  {
                print("\(totalSteps - oldValue) 단계가 추가되었습니다.")
            }
        }
        
    }
    
}

let stepCounter = StepCounter()

stepCounter.totalSteps = 360
// 출력값: 360로 총 단계를 설정하려고 합니다.
// 출력값: 160 단계가 추가되었습니다.

stepCounter.totalSteps = 896
// 출력값: 896로 총 단계를 설정하려고 합니다.
// 출력값: 536 단계가 추가되었습니다.



인스턴스

틀 역할을 하는 구조체나 클래스를 정의해 실질적으로 값을 담을 여러 개의 그릇을 만들어낸 것, 프로퍼티는 인스턴스 생성 후에만 . 점 문법을 이용하여 접근 가능하다. (타입 프로퍼티는 예외)
컴퓨터공학적으로는 타입의 설계도를 사용하여 메모리 공간을 할당받은 것이다.

// Resolution 구조체에 대한 인스턴스를 생성하고 상수 insRes에 할당
let insRes = Resolution()

// VideoMode 클래스에 대한 인스턴스를 생성하고 상수 insVMode에 할당
let insVMode = VideoMode()

// 함수를 호출할때 함수명뒤에 ()는 함수 호출 연산자라하고
// 클래스나 구조체 이름 다음에 ()는 인스턴스 생성연산자라고 한다.


let width = insRes.width

print("insRes 인스턴스의 width 값은 \(width)입니다")
// 출력값: insRes 인스턴스의 width 값은 0입니다

1개의 댓글

comment-user-thumbnail
2023년 8월 4일

잘 봤습니다. 좋은 글 감사합니다.

답글 달기