프로그래밍/Swift

Swift 기초 공부2

시르베어 2025. 1. 30. 22:46

구조체와 클래스

같은점

  • 프로퍼티, 메서드를 정의할 수 있음
  • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값에 접근하도록 서브스크립트를 정의할 수 있음
  • 이니셜라이저를 정의할 수 있음
  • 새로운 기능을 익스텐션을 통해 확장 할 수 있음
  • 특정 프로토콜을 준수 할 수 있음

차이점

  • 가장 큰 차이 : 구조체(값타입), 클래스(참조타입)
  • 구조체는 상속을 할 수 없음
  • 타입캐스팅은 클래스의 인스턴스에만 허용
  • 디이니셜라이저(소멸자)는 클래스의 인스턴스에서만 활용가능
  • 참조 횟수 계산(Reference Counting)은 클래스의 인스턴스에만 적용

프로퍼티, 메서드

프로퍼티

  1. 저장 프로퍼티 : 변수(var)/상수(let)
    • 클래스의 경우 프로퍼티 기본값을 지정해주지 않으면 이니셜라이저를 반드시 정의 해줘야한다(구조체는 자동으로 생성)
  2. 연산 프로퍼티 : 상태에 따른 값을 연산하는 프로퍼티
    • 접근자 : 인스턴스 내/외부의 값을 연산하여 적절한 값을 돌려줌
    • 설정자 : 은닉화된 내부의 프로퍼티 값을 간접적으로 설정
    • 사용하는 이유 : 외부에서 접근하려면 메서드를 두개를 생성해야하고, 생성하면 코드의 가독성이 나빠짐
struct CoordinatePoint {
    var x: Int // 저장프로퍼티
    var y: Int // 저장프로퍼티
    var oppositePoint: CoordinatePoint { // 연산프로퍼티
        get { // 접근자
            return CoordinatePoint(x: -x, y: -y)
        }
        set(opposite) { // 설정자
            x = -opposite.x
            y = -opposite.y
        }
    }
}
  1. 프로퍼티 감시자 : 프로퍼티의 값이 변경됨에 따라 적절한 작업 가능
    • 일반 저장 프로퍼티에 적용(지연X)
    • 프로퍼티 재정의를 통해 상속받은 저장 프로퍼티 또는 연산 프로퍼티에도 적용 가능
class Person {
    var name: String = "" {
        willSet { print("이름이 \(name)에서 \(newValue)로 변경될 예정") }
        didSet { print("이름이 \(oldValue)에서 \(name)로 변경됨") }
    }
}
let A: Person = Person()
A.name = "Red"
  1. 타입 프로퍼티 : 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티
    • 저장 타입 프로퍼티는 변수/상수 가능, 연산 타입 프로퍼티는 변수로만 선언
    • 저장 타입 프로퍼티의 경우 반드시 초깃값을 설정해야하며, 지연연산됨, 다중 스레드 환경에서도 단 한 번만 초기화가되는 것을 보장 받음
class A {
    static var typeProperty: Int = 0
    static var typeComputedProperty: Int {
        get { return typeProperty }
        set { typeProperty = newValue }
    }
}

A.typeProperty = 1234
print(A.typeProperty)
print(A.typeComputedProperty)

메서드

  1. 인스턴스 메서드
    • 특정 타입의 인스턴스에 속한 함수
    • 값타입에서 자신의 프로퍼티 값을 수정할때는 메서드 앞에 mutating 키워드를 붙여야한다
    • self 프로퍼티 : 명확히 인스턴스를 지칭할 때 사용
class character { // 참조타입
    var level: Int = 1

    func lvlUp(){
        self.level += 1
    }
}
let human1: character = character()
human1.lvlUp()    

struct character { // 값타입
    var level: Int = 1        
    mutating func lvlUp(){
        level += 1
    }
    mutating func jumpLvl(level: Int){
        self.level = level
    }
    mutating func reset(){
        self = character() // 새로운 인스턴스를 생성하여 인스턴스에 대입
    }
}
var human2: character = character()
human2.jumpLvl(level: 100) // level 100
human2.lvlUp() // level 101
human2.reset() // level 1
  1. 타입 메서드
    • 클래스 타입 메서드는 static과 class 키워드로 사용 가능(static으로 정의하면 상속 후 재정의가 불가능하고 class로 정의하면 상속 후 재정의가 가능하다)
    • 참고 : 상속이 안되는게 아닌 상속후 재정의가 안되는 것
class A {
    static func staticTypeMethod() {
        print("A staticTypeMethod")
    }
    class func classTypeMethod() {
        print("A classTypeMethod")
    }
}
class B: A {
    override static func staticTypeMethod() {
    }
    override class func classTypeMethod() {
        print("B classTypeMethod")
    }
}

접근제어

  1. open : 개방 접근 수준, 모듈 외부까지, 클래스에서만 사용, 공개수준과 비슷하지만 차이점이 존재, 대부분 다른 모듈에서도 부모클래스로 사용하겠다는 목적으로 설계
    • 다른 모든 접근 수준의 클래스는 클래스가 정의된 모듈 안에서만 상속가능
    • 다른 모든 접근 수준의 클래스 멤버는 멤버가 정의된 모듈 안에서만 재정의 가능
    • 개방 접근 수준의 클래스는 정의된 모듈 밖에서도 상속가능
    • 개방 접근 수준의 클래스 멤버는 정의된 모듈 밖에서도 재정의 가능
  2. public : 공개 접근 수준, 모듈 외부까지
    • 프레임워크에서 외부와 연결될 인터페이스 구현에 많이 사용
    • 스위프트 기본요소가 대부분 여기에 해당
  3. internal : 내부 접근 수준, 모듈 내부
    • 기본적으로 모든 요소에 암묵적으로 지정하는 기본 접근 수준
    • 모듈을 가져다 쓰는 외부 모듈에서는 접근을 할 수 없음
  4. fileprivate : 파일외부비공개 접근 수준, 파일 내부
    • 소스파일 외부에서 값이 변경되거나 함수를 호출하면 부작용이 생길 경우 사용
  5. private : 비공개 접근 수준, 기능 정의 내부
    • 기능을 정의하고 구현한 범위 내에서만 사용가능
    • 심지어 같은 소스파일 안이어도 구현범위가 다르면 사용불가