A Swift Tour로 Swift 공부하기 #5. Objects and Classes

minin·2021년 2월 12일
1

A Swift Tour

목록 보기
4/8

🍫 notion으로 보기
*본 포스트는 애플의 'A Swift Tour'를 바탕으로 작성되었으며, 공부하며 기록용으로 작성한 포스트이기 때문에 정확하지 않은 정보가 있을 수 있습니다!
** notion으로 작성한 문서를 임시로 올려둔 포스트이기 때문에 사진 링크의 오류와 문서의 형식 등으로 인해 보시기에 불편함이 있을 수 있습니다. (사진이 안나오거나 코드를 보기 불편한 점 등은 빠른 시일 내에 수정하도록 하겠습니다!)

5. Objects and Classes

**class**

클래스에서의 속성property 선언은 let이나 var 선언과 똑같다. 단, 클래스의 맥락context 안에서 선언한다. 예를 들어, 메소드 혹은 함수 선언와 같은 방식으로 쓴다.

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

experiment

let colorOfBottom = "red"
func simpleDescriptionColor() -> String {
     return "A Shape with \(colorOfBottom) bottom."
}

Create an instance of a class: var shape = Shape()

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

**var shape = Shape() 클래스의 새로운 인스턴스를 만든다. `()**`

shape.numberOfsides 를 수정

새로운 변수 var shapeDescription을 만들어서, shpae의 property shape.simpleDescription()를 할당

initializer: init

**init** 초기화하기

인스턴스가 만들어질 때 클래스를 초기화해야 한다.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String  //  name property, 초기화 안됨

    init(name: String) {  //  name argument, 초기화
        self.name = name
    }

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

**self: 초기화하기 위한 인자 name argument와 클래스 속성의 name property를 구분하기 위해 self가 어떻게 사용되는지 주목하자. name property는 클래스의 새로운 인스턴스가 만들어졌을 때 함수가 호출되는 것처럼 지나간다. 모든 속성property은 할당된 값(numberOfSides) 또는 이니셜라이저(name)가 필요하다.**

**deinit: deinitializer을 생성한다. 할당을 해제하기 전에 어떤 것을 지워야 할 때 사용한다.**

**subclasses: superclass의 이름을 뒤에 :으로 분리해서 적는다. subclasses: superclass. 이것은 반드시 요구되는 것이 아니라서 superclass를 포함할 수도, 생략할 수도 있다.**

**Methods: superclass의 실행을 override하는 subclass의 메소드는 override를 통해 명시해야 한다. 만약 override 없이 갑자기 오버라이드 하게 되면, 컴파일러는 에러를 발견한다. 그리고 컴파일러는 실제로는 superclass에서 오버라이드가 일어나지 않았는데 오버라이드했다고 한 것도 감지한다.**

//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."
    }
}

class Square: NamedShape {  // NamedShape을 상속한다.
    var sideLength: Double
    //  initialize
    init(sideLength: Double, name: String) {  // arguments for init
        self.sideLength = sideLength
        super.init(name: name)  // superclass
        numberOfSides = 4  //  assign value
    }

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

    override func simpleDescription() -> String {  //  voerride
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")  // init대로 인수를 받음
test.area()
test.simpleDescription()

getter and setter

🌱참고하면 좋을 자료: #연산프로퍼티

저장된 단순 속성 외에도, properties는 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)
triangle.perimeter = 9.9
print(triangle.sideLength)

perimeter의 setter에서, 새로운 값은 암시적으로 newValue라는 이름을 가진다. set뒤에 나오는 괄호 안에 명시적인 이름을 제공해도 된다. set(newValue)

❗️EquilateralTriangle 클래스는 3개의 다른 스텝으로 초기화를 한다.

  1. 하위클래스가 선언한 속성의 값을 설정한다.
  2. 슈퍼클래스의 생성자initializer을 불러온다calling.
  3. 슈퍼클래스에 의해 정의된 속성의 값을 바꾼다. 메소드, getters, setters를 사용하는 것도 이에 해당된다.

*get set

//기본 구문
var myProperty: Int {
	get {
		return myProperty
	}
	set(newValue) {
		mProperty = newValue
	}
}

print(myProperty)
myProperty = 123

위와 같이 작성하면 Xcode에서 경고. get과 set은 해당 프로퍼티에 직접 붙어있기 때문에 위와 같이 get{}, set{}

에서 myProperty에 직접 접근하면, 재귀로 자신의 get, set을 호출하게 되기 때문이다. 따라서 실제 값을 저장 할 backing storage가 필요하다(ex. myProperty).

var _myProperty: Int  // _myProperty: 실제 값이 저장되는 변수, myProperty는 이것의 인터페이스 역할
var myProperty: Int {
	get {
		return _myProperty
	}
	set(newValue){
		_myProperty = newValue
	}
}

willSet and didSet

속성을 계산할 필요는 없지만 여전히 새로운 값을 세팅하기 전이나 후에 실행되는 코드를 제공해야할 필요가 있다면, willSet과 didSet을 이용한다. 제공하는 코드는 생성자의 밖에서 값이 변하는 언제든지 실행할 수 있다. 예를 들어, 삼각형의 옆 길이는 그것의 정사각형의 옆 길이와 항상 같다. (다음 코드에서 확인)

class TriangleAndSquare {
    var triangle: EquilateralTriangle {  //  SideLength, name
        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)
// Prints "10.0"
print(triangleAndSquare.triangle.sideLength)
// Prints "10.0"
//  newValue가 할당. square의 willSet-> triangle.length가 할당된다.
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
// Prints "50.0"

triangleAndSquare.square = Square(sideLength: 50, name: "larger square")

위의 코드에서 square의 값이 변경된다.

square의 willSet이 호출되어, triangle.sideLength의 값에 newValue.sideLength가 할당된다.

print(triangleAndSquare.triangle.sideLength)

위의 코드에서 triangle.sideLength는 변경된 값인 50이다.

*willSet didSet

프로퍼티 옵저버. 프로퍼티의 값이 변경되기 직전, 직후를 감지해서, 이 때 원하는 작업을 수행한다.

//기본 구문
var myProperty: Int = 10{
	didSet(oldValue){
		//myProperty의 값이 변경된 직후에 호출, myProperty의 변경 전 값
	}
	willSet(newValue) {
		//myProperty의 값이 변경되기 직전에 호출, newValue는 새로 변경될 값 
}

프로퍼티 옵저버를 사용하려면 프로퍼티의 값이 반드시 초기화되어 있어야 한다.

init() 안에서 값을 할당할 때에는 didSet, willSet 호출되지 않는다.초기화 된 후부터 프로퍼티 감시한다.

optional

optional 값을 가지고 작업을 할 때 메소드methods, 속성properties, 서브스크립팅subscripting과 같은 연산 전에 **?**을 쓸 수 있다. 만약 ? 앞의 값이 nil이라면, ?의 뒤에 오는 모든 것은 무시되고, 모든 표현의 값은 nil이 된다. 반대로 optional 값이 벗겨졌다면unwrapped(값이 존재한다면), ?뒤에 오는 모든 것은 벗겨진 값으로 작용한다. 두 케이스 모두에서 옵셔널 값은 옵셔널 값이다.(값이 벗겨지든 nil이든 모두 optional)

`subscripting*`: 콜렉션, 리스트, 시퀀스 등 집합의 특정 member elements에 간단하게 접근할 수 있는 문법으로, array[index], dictionary[key]의 접근 등이 그 예이다.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

optionalSquare이 nil이 아닌 것으로 벗겨져도unwrapped, optional 값 이므로 ?을 붙여주어야 한다.

profile
🍫 iOS 🍫 Swift

0개의 댓글