클래스, 구조체, 열거형 등에 관련된 값
struct Dog {
var name: String
let gender: String
}
var dog1 = Dog(name: "Coco", gender: "Male")
let dog2 = Dog(name: "Dodo", gender: "Female")
Dog 구조체에 선언된 name과 gender 멤버변수를 저장 프로퍼티라고 한다.
dog1의 gender 값은 변경할 수 없으며, dog2는 둘 다 변경할 수 없다.(let)
class Cat {
var name: String
let gender: String
init(name: String, gender: String) {
self.name = name
self.gender = gender
}
}
let cat = Cat(name: "Coco", gender: "Male")
cat.name = "Dodo" // 가능
cat = Cat(name: "Dodo", gender: "Female") // gender가 var로 선언되었다고 하더라도 불가능
구조체는 value 타입이고 class는 참조 타입이기 때문에 cat이 let으로 선언되었다고 하더라도 name 값을 변경할 수 있다.
let이기 때문에 cat 자체를 재할당하는 것은 안 된다.
getter, setter를 이용해서 값을 연산하고 프로퍼티에 저장하는 역할
struct Stock {
var averagePrice: Int
var quantity: Int
var purchasePrice: Int {
get {
return averagePrice * quantity
}
set(newPrice) {
averagePrice = newPrice / quantity
}
}
}
var stock = Stock(averagePrice: 2300, quantity: 3)
stock.purchasePrice // get 호출
stock.purchasePrice = 3000 // set 호출
setter 없이 getter 만 선언하면 읽기 전용 프로퍼티가 된다.
setter 를 매개변수 없이 사용하면 newValue 라는 이름으로 대신하여 사용할 수 있다.
프로퍼티 값의 변화를 관찰한다.
저장 프로퍼티에서 사용하거나 부모 클래스의 연산 프로퍼티를 오버라이딩할 경우 사용할 수 있다.
그냥 연산 프로퍼티에서는 사용할 수 없는데 그 이유는 getter를 통해 값의 변경을 알 수 있기 때문이다.
class Account {
var credit: Int = 0 {
willSet {
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet {
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
}
}
}
var account = Account()
account.credit = 1000
인스턴스 생성 없이 객체 내 프로퍼티에 접근이 가능하게 한다.
static
키워드 사용
struct SomeStructure {
static var storedTypeProperty = "Some value"
static var computedTypeProperty: Int {
return 1
}
}
SomeStructure.storedTypeProperty // Some Value
SomeStructure.computedTypeProperty // 1
SomeStructure.storedTypeProperty = "hello" // hello
class SomeClass {
var count: Int = 0
}
struct SomeStruct {
var count: Int = 0
}
var class1 = SomeClass()
class2 = class1
class3 = class1
class2.count = 2
class1.count // 2
class2.count // 2
class3.count // 2
var struct1 = SomeStruct()
struct2 = struct1
struct3 = struct1
struct2.count = 2
struct3.count = 3
struct1.count // 0
struct2.count // 2
struct3.count // 3
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
print("Speaker on")
}
}
class Bicycle: Vehicle {
var hasBasket = false
}
var bicycle = Bicycle()
bicycle.currentSpeed // 0.0
bicycle.currentSpeed = 15.0
bicycle.currentSpeed // 15.0
class Train: Vehicle {
override func makeNoise() {
super.makeNoise()
print("Choo Choo")
}
}
let train = Train()
train.makeNoise()
/*
Speaker on
Choo Choo
*/
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
let car = Car()
car.currentSpeed = 30.0
car.gear = 2
car.description
// traveling at 30.0 miles per hour in gear 2
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10) + 1
}
}
}
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
final
: final
키워드를 프로퍼티나 함수에 사용하면 오버라이딩하여 재정의할 수 없다. 클래스에 사용하면 해당 클래스를 부모 클래스로 한 서브 클래스를 생성할 수 없다.
인스턴스의 타입을 확인하거나(is) 어떤 클래스의 인스턴스를 해당 클래스 계층 구조의 슈퍼 클래스나 서브 클래스로 취급하는(as) 방법
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library = [
Movie(name: "기생충", director: "봉준호")
Song(name: "Butter", artist: "BTS")
Movie(name: "올드보이", director: "박찬욱")
Song(name: "Wonderwall", artist: "Oasis")
Song(name: "Rain", artist: "이적")
] // library는 MediaItem 배열
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
// Media library contains 2 movies and 3 songs
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
다운캐스팅하는 경우
as?
나 as!
사용
다운캐스팅이 실패할 수 있기 때문에 as?
를 사용하며, as!
를 사용하여 걍제적으로 unwrapping 했는데 잘못될 경우 에러가 발생하여 프로그램이 강제 종료될 위험이 존재
위의 예제에서는 조건문에 다운캐스팅을 사용하여 unwrapping 함.
특정 조건을 체크하고, 조건이 성립되지 않으면 메세지를 출력하게 할 수 있는 함수
디버깅 모드에서만 동작하고 주로 디버깅 중 조건의 검증을 위해 사용
var value = 0
assert(value == 0)
value = 2
assert(value == 0, "값이 0이 아닙니다.")
조건을 검사하여 그 다음에 오는 코드를 실행할지 결정하는 것
func guardTest1(value: Int) {
guard value == 0 else { return }
print("안녕하세요")
}
guardTest1(value: 2) // 아무것도 출력되지 않음
guardTest1(value: 0) // 안녕하세요가 출력됨
// guard를 사용하여 옵셔널 바인딩
func guardTest2(value: Int?) {
guard let value = value else { return }
print(value)
}
guardTest2(value: 2) // 2가 출력됨
guardTest2(value: nil) // 아무것도 출력되지 않음
특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구 사항 등의 청사진
protocol FullyNames {
var fullName: String { get set }
func printFullName()
}
struct Person: FullyNames {
var fullName: String
func printFullName() {
print(fullName)
}
}
protocol SomeProtocol {
init()
}
class SomeClass: SomeProtocol {
required init() {
}
}
클래스에서 프로토콜이 요구하는 생성자를 정의하려면 required
식별자를 써줘야 한다.(구조체에서는 써주지 않아도 되고, final
키워드를 사용한 클래스인 경우에도 써주지 않아도 된다.)
기존의 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능
기존에 존재하는 기능을 오버라이딩할 수는 없다.
extension Int {
var isEven: bool {
return self % 2 == 0
}
var isOdd: bool {
return self % 2 == 1
}
}
var number = 3
number.isEven // false
number.isOdd // true
extension String {
func convertToInt() -> Int? {
return Int(self)
}
}
var string = "0"
string.convertToInt() // 0
연관성이 있는 값을 모아 놓은 것
enum CompassPoint1 {
case north
case south
case east
case west
}
var direction1 = CompassPoint1.east
direction1 = .west
switch direction1 {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
// 원칙값 지정
enum CompassPoint2: String {
case north = "북"
case south = "남"
case east = "동"
case west = "서"
}
var direction2 = CompassPoint2.east
direction2 = .west
switch direction2 {
case .north:
print(direction2.rawValue)
case .south:
print(direction2.rawValue)
case .east:
print(direction2.rawValue)
case .west:
print(direction2.rawValue)
}
// 원칙값으로 열거형 반환
let direction3 = CompassPoint(rawValue: "남")
// 열거형은 연관값을 가질 수 있다.
enum PhoneError {
case unknown
case batteryLow(String) // String 타입 연관값을 갖는 열거형
}
let error = PhoneError.batteryLow("배터리가 곧 방전됩니다.")
// 연관값 추출
switch error {
case .batteryLow(let message):
print(message)
case .unknown:
print("알 수 없는 에러입니다.")
}
옵셔널에 속해 있는 nil
일지도 모르는 프로퍼티, 메소드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정
.
을 통해 프로퍼티나 메소드에 연속적으로 접근할 때 옵셔널이 하나라도 껴 있으면 옵셔널 체이닝이다.
struct Developer {
let name: String
}
struct Company {
let name: String
var developer: Developer?
}
var developer = Developer(name: "Park")
var company = Company(name: "Gunter", developer: developer)
print(company.developer)
print(company.developer?.name) // Optional("Park")
// 옵셔널 바인딩으로 unwrapping할 수 있다.
print(company.developer!.name) // Park(강제적 해제)