🍫 notion으로 보기
*본 포스트는 애플의 'A Swift Tour'를 바탕으로 작성되었으며, 공부하며 기록용으로 작성한 포스트이기 때문에 정확하지 않은 정보가 있을 수 있습니다!
** notion으로 작성한 문서를 임시로 올려둔 포스트이기 때문에 사진 링크의 오류와 문서의 형식 등으로 인해 보시기에 불편함이 있을 수 있습니다. (사진이 안나오거나 코드를 보기 불편한 점 등은 빠른 시일 내에 수정하도록 하겠습니다!)
**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."
}
var shape = Shape()
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
**var shape = Shape()
클래스의 새로운 인스턴스를 만든다. `()**`
shape.numberOfsides
를 수정
새로운 변수 var shapeDescription을 만들어서, shpae의 property shape.simpleDescription()
를 할당
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()
🌱참고하면 좋을 자료: #연산프로퍼티
저장된 단순 속성 외에도, 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개의 다른 스텝으로 초기화를 한다.
*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과 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 값을 가지고 작업을 할 때 메소드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 값 이므로 ?
을 붙여주어야 한다.