class
뒤에 클래스의 이름을 사용하여 클래스를 생성한다.컨텍스트 context
안에 있다는 점을 제외하고는 상수
또는 변수
를 선언하는 방법과 동일하다.메서드
와 함수
선언도 동일한 방법으로 작성된다.class Shape{
var numberOfSides = 0
func simpleDescription() -> String{
return "A shape with \(numberOfSides) sides"
}
}
new
키워드 들어가지 않음프로퍼티
와 메서드
에 접근하기 위해서 점구문
을 사용한다.var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
인스턴스가 생성될 때 클래스를 설정하기 위해서 초기화 문
을 사용할 수 있다.
init
을 사용하여 생성한다.
모든 프로퍼티
는 numberOfSides
와 같이 선언 시나 name
과 같이 초기화 구문에서 값을 할당해야 한다.
객체가 할당해제
되기 전에 어떠한 정리작업이 필요하여 초기화 해제 구문 deinitializer
을 생성하려면 deinit
을 사용한다.
하위클래스 subclass
는 클래스 이름 뒤에 콜론으로 구문하여 상위 클래스 superclass
를 포함한다. 클래스가 모든 표준 루트 클래스를 하위클래스화 할 필요가 없으므로 필요에 따라서 상위클래스를 포함하거나 생략할 수 있다.
상위 클래스의 구현을 재정의하는 하위클래스의 메서드는 override
로 표시된다.
실수로 override
없이 메서드를 재정의하면 에러로 컴파일러에 의해서 감지된다.
컴파일러는 실제로 상위 클래스의 어떤 메서드도 실제로 재정의 하지 않는 override
가 있는 메서드를 감지한다.
class NamedShape{
var numberOfSides : Int = 0
var name : String
init(name : String){
self.name = name
}
func simpleDescription() -> String{
return "A shape with \(numberOfSides) sides."
}
}
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{
return 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)
// 9.3
triangle.perimeter = 9.9
print(triangle.sideLength)
// 3.30000000000000003
perimeter
의 setter에서 새로운 값은 암시적으로 newValue
라는 이름을 가진다. set
이후에 소괄호에서 명시적으로 이름을 제공해줄 수 있다.
EquilateralTriangle
클래스의 초기화 구문은 세가지 단계가 있다.
프로퍼티
의 값을 설정한다.초기화 구문
을 호출한다.메서드
, 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(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
이다. 옵셔널 값
은 언래핑
되고, ?
후에 모든 동작은 언래핑된 값
으로 동작한다. 옵셔널 값
이다.let optionalSquare : Square? = Square(sideLength : 2.5, name : "optional Square");
let sideLength = optionalSquare?.sideLength
enum
을 사용한다.열거형
과 좀 다르다. 유용하게 사용할 수 있을 것 같은enum Rank : Int{
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self{
case .ace :
return "ace"
case .jack :
return "jack"
case .queen :
return "queen"
case .king :
return "king"
default :
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
enum Month {
case mar, apr, may
case jun, jul, aug
case sep, oct, nov
case dec, jan, feb
func printMessage() {
switch self {
case .mar, .apr, .may:
print("봄!")
case .jun, .jul, .aug:
print("여름!")
...
}
}
}
기본적으로 Swift
는 0을 시작으로 매번 증가하는 원시값 raw Value
을 할당하지만 명시적으로 특정 값으로 이 동작을 변경할 수 있다.
위의 코드를 보면 Ace
는 명시적으로 1
의 값이 주어지고 나머지 원시 값은 순서대로 할당된다.
열거형의 원시타입
으로 문자열
또는 부동소수점
도 사용할 수 있다.
열거형 케이스
의 원시값
에 접근하기 위해서 rawValue
프로퍼티를 사용한다.
원시값으로부터 열거형의 인스턴스를 생성하기 위해 init?(rawValue:)
초기화 구문을 사용한다.
원시값이 일치하는 열거형 케이스나 Rank
에 일치하는 항목이 없으면 nil
을 반환한다.
if let convertedRank = Rank(rawValue : 3){
let threeDescription = convertedRank.simpleDescription()
}
enum Suit{
case spaces, hearts, diamonds, clubs
func simpleDescription() -> String{
switch self{
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
hearts
케이스를 참조하는 2가지 방법을 살펴보자.hearts
상수에 값을 할당할 때 명시적으로 타입을 지정하지 않았으므로, 열거형 케이스 Suit.hearts 전체이름으로 참조된다. self
의 값은 이미 카드(suit)로 알고 있기 때문에 열거형 케이스는 짧은 형식인 .hearts
로 참조된다. 값의 타입을 이미 알고 있다면 언제나 짧은 형식을 사용할 수 있다. 값의 타입
을 알고 있다면 언제나 짧은 형식을 사용가능하다. 열거형
이 원시값을 갖는 경우 선언의 부분으로 결정된다. 이것은 특정 열거형 케이스의 모든 인스턴스는 항상 같은 원시값을 갖는다는 의미이다. 열거형
케이스의 또다른 선택은 케이스와 연관된 값 associated value
을 가지는 것이다. 이러한 값은 인스턴스를 생성할 때 결정되고, 열거형 케이스의 각 인스턴스에 대해 다를 수 있다. 연관된 값 associated value
은 열거형 케이스 인스턴스의 저장된 프로퍼티
처럼 동작한다고 생각할 수 있다. 예를 들어 서버에서 일출과 일몰 시간에 대해 요청한다고 가정해보자.enum ServerResponse{
case result(String, String)
case failure(String)
let success = ServerResponse.result("6:00 am", "8.09 pm")
let failure = ServerResponse.failure("Out of cheese")
switch success{
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunsat).")
case let .failure(message):
print("Failure ... \(message)")
}
}
// "Sunrise is at 6:00 am and sunset is at 8:09 pm."
스위치 케이스
에 대한 값이 일치하는 부분으로 ServerResponse
값에서 일출과 일몰 시간이 어떻게 추출되는지 확인struct
를 사용한다. 구조체는 메서드와 초기화 구문을 포함하여 클래스와 동일한 동작을 많이 지원한다. 구조체
와 클래스
의 가장 큰 차이점은 구조체는 코드에서 전달될 때 항상 복사되지만 클래스는 참조로 전달된다. struct Card{
var rank : Rank
var suit : Suit
func simpleDescription() -> {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())
}
let threeOfSpades = Card(rank : .three, suit : .spades)
let threeOfSpadesDesctiption = threeOfSpades.simpleDescription()
}