벌써 Struct
순서네요.
구조체라고도 하는데 클로저와 마찬가지로 스위프트에서 중요합니다
우리만의 데이터구조를 만들게 해주고 이게 실제 프로젝트에서도 유용하거든요
오늘도 힘차게 가봅시다!
Swift에서는 2가지 방법을 제시하는데요 그 중 가장 흔한 방법이 structures
또는 그냥 struct
라고도 합니다.
struct
는 자신만의 변수, 상수, 함수를 만들 수 있으며 우리가 원하는대로 사용할 수 있슴다
Sport
라는 간단한 예시를 볼건데요 name
을 String
타입으로 저장하는 struct
입니다.
참고로 struct
안의 변수들은 properties
(프로퍼티)라고 불립니다.
그럼 Sport
는 하나의 프로퍼티를 가지는 구조체겠네요
struct Sport {
var name: String
}
생각보다 간단하죠? struct
키워드를 써주고 그 다음에 이름을 써주면 됩니다
{}
내부에는 변수이름:타입
을 작성하면 되구요
그럼이제 인스턴스를 생성해 볼까요?
var tennis = Sport(name: "Tennis")
print(tennis.name)
name
과 tennis
둘 다 변수이기 때문에 물론 변경할 수도 있어요
tennis.name = "Lawn tennis"
프로퍼티는 일반 변수처럼 기본값을 가질 수도 있고 타입추론도 가능합니다.
값이 바뀌는것도 확인해 봤습니다
앞에서 Sport
를 생성했죠?
struct Sport {
var name: String
}
name
이라는 String
타입의 프로퍼티를 가지고 있습니다.
이걸 stored property
(저장 프로퍼티)라고 합니다.
그리고 Swift에는 computed property
(연산 프로퍼티)도 존재합니다
Sport
에 저장 프로퍼티를 하나 더 추가하여 연산 프로퍼티의 작동도 확인해보죠
struct Sport {
var name: String
var isOlympicSport: Bool
var olympicStatus: String {
if isOlympicSport {
return "\(name) is an Olympic sport"
} else {
return "\(name) is not an Olympic sport"
}
}
}
isOlympicSport
라는 저장 프로퍼티와 olympicStatus
연산 프로퍼티를 추가해줬습니다.
olympicStatus
은 일반적인 String
변수처럼 보이지만 다른 프로퍼티 값에 따라서 다른 값을 반환합니다.
이걸 실제로 확인해보려면 새 인스턴스를 생성해보면 됩니다.
let chessBoxing = Sport(name: "Chessboxing", isOlympicSport: false)
print(chessBoxing.olympicStatus)
isOlympicSport가 false
값을 가지기 때문에 else 구문이 실행되네요
프로퍼티 옵저버는 아무 프로퍼티에 변화가 생기기 전이나 후에 코드가 작동하도록 합니다. Progress 라는 struct
를 통해 어떻게 작동하는지 살펴보죠
struct Progress {
var task: String
var amount: Int
}
이제 인스턴스를 생성할 수 있죠.
그 뒤에 값을 조정해보죠
var progress = Progress(task: "Loading data", amount: 0)
progress.amount = 30
progress.amount = 80
progress.amount = 100
여기서 우리가 하고자 하는것은 Swift가 amount
의 값이 변할 때마다 message를 출력하게 하는겁니다.
이걸 didSet
이라는 프로퍼티 옵저버를 통해 해보죠
amount
값이 변할때마다 작성된 코드를 출력할겁니다
struct Progress {
var task: String
var amount: Int {
didSet {
print("\(task) is now \(amount)% complete")
}
}
}
실제로 확인해보면 amount
이 값이 30, 80, 100으로 변할때마다
didSet
에 있는 print()
함수가 잘 작동하네요.
추가로 willSet
이라는 키워드로 프로퍼티가 변하기 전에 작동하도록 할 수도 있지만 잘 사용되지는 않습니다
구조체안에는 함수도 작성할 수 있슴다. 그리고 그 함수들은 필요하다면 구조체의 프로퍼티를 사용할 수도 있죠
프로퍼티 내부의 함수는 똑같이 func
키워드로 만들지만
이름은 method
라고 불립니다.
City라는 구조체로 확인해보죠
struct City {
var population: Int
func collectTaxes() -> Int {
return population * 1000
}
}
population
이라는 프로퍼티를 가지고 collectTaxes()
메소드가 있네요
이 메소드는 City내부의 population
프로퍼티의 값을 1000배 해서 반환합니다.
let london = City(population: 9_000_000)
london.collectTaxes()
메소드 또한 구조체내부에 있기 때문에 위처럼 호출해서 사용해야 합니다
숫자에 쓰인_
는 단순히 자리수를 구분하는 용도입니다
만약 구조체에 프로퍼티가 변수인데, 인스턴스가 상수로 생성되면 그 프로퍼티는 변경할 수가 없어요. 또한 모든 프로퍼티가 상수가 되어버린답니다 만들어진 방식에 상관없이요
문제는 구조체를 만들었을 때 Swift는 우리가 그것을 변수로 사용할지 상수로 사용할 지 알 수 없다는거죠. 그래서 기본 접근법으로 안전한 방식을 취합니다. 스위프트는 우리가 특별히 요구하지 않는 이상은 프로퍼티를 변경하는 메소드를 작성하게 해주지 않슴다
메소드안의 프로퍼티를 변경하고 싶다면 mutating
이라는 키워드로 마킹을 해줘야합니다
struct Person {
var name: String
mutating func makeAnonymous() {
name = "Anonymous"
}
}
Because it changes the property, Swift will only allow that method to be called on
Person
instances that are variables
프로퍼티를 변경하기 때문에 스위프트는 변수인 Person
인스턴스에서만 사용할 수 있게합니다.
제대로 번역한게 맞는지 조금 헷갈려서 원본도 첨부합니다
var person = Person(name: "Ed")
person.makeAnonymous()
'Ed'로 되어있던 name
이 makeAnonymous()
메소드에 의해서 변경되었네요.
우리가 여태 String
타입들을 많이 사용했죠? 근데 애네도 struct
랍니다
우리가 만드는 struct
들처럼 String
도 메소드와 프로퍼티를 가지고 있습니다. 이것들을 이용해서 String
을 조작할 수 있어요
테스트용으로 쓸 String
을 먼저 작성하죠
let string = "Do or do not, there is no try."
print(string.count)
count
프로퍼티를 사용해서 string
의 문자 개수를 알아낼 수 있어요
print(string.hasPrefix("Do"))
hasPrefix()
은 특정한 문자로 string
이 시작하면 '참'을 반환합니다
print(string.uppercased())
uppercased()
메소드는 String
을 대문자로 바꿔주고요
print(string.sorted())
sorted()
는 문자들을 배열로 정리합니다
string.
을 xcode에 입력하면 여러가지 다른 메소드나 프로퍼티들을 알 수 있어요
'Array' 또한 구조체입니다. 이 말은 String
과 마찬가지로 프로퍼티와 메소드를 이용해 조작할 수 있다는 말이죠
예시로 쓸 배열을 하나 선언해주고요
var toys = ["Woody"]
print(toys.count)
count
로 배열 요소의 개수를 알아낼 수도 있고
toys.append("Buzz")
append()
로 새로운 요소를 추가해 줄 수도 있죠
toys.firstIndex(of: "Buzz")
특정 요소의 인덱스를 firstIndex()
를 통해 알아낼 수도 있죠
이 경우는 '1'을 출력하겠네요.
print(toys.sorted())
String
과 마찬가지로 알파벳순서로 요소들을 정렬할 수도 있죠
toys.remove(at: 0)
마지막으로 요소를 삭제하고 싶다면 remove()
를 사용하면 됩니다.