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)를 포함한다.
클래스가 모든 표준 루트 클래스를 하위 클래스화 할 필요 없으므로 필요에 따라 상위 클래스를 포함하거나 생략할 수 있다.
상위 클래스의 구현을 재정의하는 하위 클래스의 메소드는 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 side 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 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.3000000000000003"
perimeter의 setter에서 새로운 값은 암시적으로 newValue라는 이름을 가진다.
set 이후에 소괄호 안에 명시적으로 이름을 제공할 수 있다.
EquilateralTriangle 클래스의 초기화 구문은 세 가지 단계가 있다.
1. 하위 클래스가 선언한 프로퍼티의 값을 설정한다.
2. 상위 클래스의 초기화 구문(생성자)을 호출한다.
3. 상위 클래스에 의해 정의된 프로퍼티의 값을 변경한다. 메소드, getter 또는 setter를 사용하는 추가 설정 작업도 이 시점에서 수행될 수 있다.
프로퍼티를 계산할 필요는 없지만 새로운 값을 저장하기 전과 후에 실행되는 코드를 제공하려면
willSet과 didSet을 사용하면 된다.
이렇게 제공한 코드는 초기화 구문(생성자)의 외부에서 값이 변경될 때 마다 실행된다.
예를 들어 아래의 클래스는 삼각형의 측면의 길이가 항상 사각형의 측면의 길이와 같다는 것을 확인한다.
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(sizeLength: size. name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength) // prints 10.0
print(triangleAndSquare.triangle.sideLength) // prints 10.0
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength) // prints 50.0
옵셔널 값으로 동작할 때,
메서드, 프로퍼티, 그리고 서브스크립트와 같은 동작 전에 ?를 작성할 수 있다.
? 전의 값이 nil이면 ? 이후의 모든 것은 무시되고 전체 표현의 값은 nil이다.
그렇지 않다면, 옵셔널 값은 언래핑되고 ? 후에 모든 동작은 언래핑 된 값으로 동작한다.
이 경우 모드 전체 표현식의 값은 옵셔널 값이다.
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength