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()
}