본문 바로가기

Swift

Objects and Classes - A Swift Tour 3일차

반응형

class 키워드 뒤에 클래스 이름을 붙여 새로운 클래스를 생성할 수 있다. 클래스에 속성을 정의하는 방법은 상수, 변수 정의에 사용된 방법과 동일하나, 클래스 컨텍스트 안에 존재한다는 점만 다르다. 마찬가지로, 메소드와 함수 정의도 같은 방법을 따른다.

    class Shape {
        var numberOfSides = 0
        func simpleDescription() -> String {
            return "A shape with \(numberOfSides) sides."
        }
    }

클래스 이름 뒤에 괄호를 붙여 클래스 인스턴스를 만들 수 있다. .점을 통해 클래스 인스턴스의 변수, 메소드에 접근할 수 있다.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Shape 클래스는 클래스의 중요한 부분인 initializer, 생성자가 없다. 생성자는 클래스 인스턴스가 생성될 때 변수를 설정한다. init을 사용하여 만들 수 있다.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

name 속성과 name 인자를 구분하는 데 self가 어떻게 쓰였는지 주목하자. 클래스 인스턴스를 생성할 때, 인자는 함수 호출과 같이 넘어온다. 모든 속성은 값이 할당되어야 한다. 선언과 동시에 (numberOfSides의 경우) 혹은 생성자에서 (name과 같은 경우)

deinit을 사용하여 오브젝트가 할당 해제되기 전에 메모리 정리를 수행하는 소멸자를 생성한다.

subclass는 superclass 이름을 자신의 클래스 이름: 뒤에 포함한다. 서브클래스가 어떠한 표준 root class도 반드시 상속할 필요는 없다.

수퍼 클래스의 구현 사항을 override한 서브 클래스의 메소드는 메소드 이름 앞에 override를 표기한다. 해당 표기가 없으면 에러가 발생한다. 또한, 컴파일러는 수퍼 클래스에 존재하지 않는 메소드를 override 하는 경우에도 컴파일러 에러를 발생시킨다.

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

저장되는 속성을 단순하게 만들기 위해 속성은 getter와 setter를 가질 수 있다.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
// Prints "9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)
// Prints "3.3000000000003"

perimeter setter에서 새로운 값은 암시적으로 newValue라는 이름의 변수가 된다. set 뒤에 괄호로 명시적인 이름을 표기할 수도 있다.

EquilateralTriangle 클래스가 생성자가 세 가지 다른 단계를 가지는 것에 주목하자:

  1. 서브 클래스가 정의한 속성의 값을 설정
  2. 수퍼 클래스의 생성자를 호출
  3. 수퍼 클래스에서 설정된 속성값을 변경. 메소드, getter, setter를 사용하는 추가 작업도 수행될 수 있다

속성을 계산할 필요가 없지만 새로운 값을 설정하기 전과 후에도 코드를 제공해야 한다면, willSetdidSet을 사용하라. 제공된 코드는 생성자 밖에서도, 값이 변하는 어느 시점에서든 실행된다. 예를 들어, 아래 클래스는 삼각형의 side length가 항상 사각형의 side length와 같다.

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }

    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }

    init(size: Double, name: String) {
         square = Square(sideLength: size, name: name)
         triangle = EquilateralTriangle(sideLength: size, name: name)
     }
}

var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)    // 10.0
print(triangleAndSquare.triangle.sideLength)    // 10.0

triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)    // 50.0

옵셔널 값을 다룰 때는 ?를 메소드, 속성의 뒤에 붙여 사용한다. ? 이전의 값이 nil이면 ? 이후의 구문은 무시되고 전체 표현의 값이 nil이 된다. 다시 말해, 옵셔널 을 unwrap 되면 ? 이후 값은 모두 옵셔널 자료형이 된다. 두 경우 모두, 전체 표현식의 값은 옵셔널 값이다.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength      // optionalSquare가 nil이면 nil, 아니면 sideLength 값을 반환. 두 경우 모두를 포함하는 자료형은 Int?
반응형