구조체나 클래스의 변수 → 속성(Property)
구조체나 클래스의 함수 → 메서드(Method)
struct Bird {
var name: String // name이라는 저장속성
var weight: Double // weight라는 저장속성
init(name: String, weight: Double) {
// 기본값이 없으면, 생성자를 통해 **값을 반드시 초기화해야함**
self.name = name
self.weight = weight
}
func fly() {
print("날아갑니다.")
}
}
struct Bird1 {
var name: String
lazy var weight: Double = 0.2
// 선언시에는 메모리 공간을 가지고 있지 않음
// 따라서 초기화 할때 아규먼트를 저장공간에 넣어주는 행위가 불가능
// **lazy로 선언할때는 "반드시" 기본값을 선언 해줘야함**
init(name: String) {
self.name = name
}
// weight의 경우 lazy로 선언되었기 때문에 초기화가 불가능
func fly() {
print("날아갑니다.")
}
}
var aBird1 = Bird1(name: "새")
// weight 속성 초기화 안됨
aBird1.weight
// 해당 변수에 접근하는 이 시점에 초기화됨 (메모리 공간이 생기고 숫자가 저장됨)
class AView {
var a: Int
// 1) 메모리를 많이 차지할때
lazy var view = UIImageView() // 객체를 생성하는 형태
// 2) 다른 속성을 이용해야할때(다른 저장 속성에 의존해야만 할때)
// 간단히 말해서 아래 예시처럼 **a가 있어야 b를 만들어낼 수 있는 상황
// b라는 값은 a의 값에 의존한다**
lazy var b: Int = {
return a * 10
}()
init(num: Int) {
self.a = num
}
}
클래스의 경우 힙영역 → 데이터 영역 → 코드영역
구조체의 경우 코드영역에서 → 바로 메서드 코드영역(클래스보다 빠름)
class Person {
var birth: Int = 0
var age: Int {
// **계산속성은 무조건 var로 선언을 해줘야한다**
get {
return 2021 - birth
}
// 중괄호의 열고 닫음 = 함수
// get블럭만 선언할때는 get과 {}생략 가능
var age: Int {
return 2021 - birth
}
set(age) {
// set의 input인자의 경우 타입말고 파라미터만 선언해주면된다
// newValue라는 기본 파라미터를 사용하면 생략이 가능하다
self.birth = 2021 - age
}
// newValue를 사용하면 코드가 간결해진다
set {
self.birth = 2021 - newValue
}
}
}
var p1 = Person()
// get블럭 -> 파라미터가 없으면 get블럭이라고 판단 -> **읽기**
// 계산속성에서 get블럭은 무조건 구현을 해야함
p1.age -> 개발자가 값을 "얻기(get)"위한 블럭
// set블럭 -> 파라미터가 있으면 set블럭이라고 판단 -> **쓰기**
// 계산속성에서 set블럭은 선택사항임(구현안해도 됨)
p1.age() -> 개발자가 값을 "세팅(set)"하기 위한 블럭
⭐️다른 저장 속성으로 계산해 나오는 메서드인 경우 계산속성으로 만들 수 있다⭐️
1)저장 타입 속성
class Dog {
// 저장속성 앞에 static을 붙이면 저장 타입속성이 됨
static var species: String = "Dog"
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
}
}
let dog = Dog(name: "초코", weight: 15.0)
dog.~~species~~ -> 인스턴스에 .을 찍어도 속성으로 보이지 않음
Dog.species //"Dog"
2)계산 타입 속성
class Circle1 {
// 저장 타입 속성
**// 저장 타입 속성은 반드시 기본값을 설정해놔야함**
static let pi: Double = 3.14
static var count: Int = 0
// 저장 속성
var radius: Double // 반지름
// (계산) 타입 속성(read-only)
**// 타입속성끼리는 편하게 변수 공유를 할 수 있음
// Circle1.pi라고 안하고 그냥 pi라고 해도 됨**
static var multiPi: Double {
return pi * 2
}
// 계산 속성(read-only)
**// 타입속성이 아니기 때문에 Circle1.multiPi로 접근해야함**
var getArea: Double {
return Circle1.multiPi * radius
}
// 생성자
init(radius: Double) {
self.radius = radius
}
}
let myCircle = Circle1(radius: 10.0)
myCircle.getArea
1) 저장 타입속성 → 재정의 불가능
2) 계산 타입속성 → 상위클래스에서 static이 아닌 class로 정의한 경우엔 재정의 가능
class Profile {
var name: String = "이름"
// 기본적으로 저장속성의 모양을 가지고 있음
// 추가로 중괄호를 열고닫아주고 그안에 willSet{} or didSet{}을 구현
// 일반적으로는 didSet{}만 구현한다
var statusMessage: String = "기본 상태메세지" {
// didSet은 변화된 직후에 실행됨
didSet(message) {
print("메세지가 \(statusMessage)에서 \(message)로 변경될 예정입니다.")
// print("메세지가 \(oldValue)에서 \(statusMessage)로 이미 변경되었습니다.")
// 예전값에서 (내가세팅할)새로운값으로 바뀐다 즉, 예전값은 중요도가 낮다
// 예전값을 oldValue라는 정해진 파라미터로 바꿔도 됨
}
}
// 속성감시자는 저장속성자체를 감시하는 역할이기때문에
**// 초기값없이 init으로 초기값을 생성해줘도 됨(초기값 세팅에는 감시자실행 x)**
}
구조체나 클래스나 “속성”에 있어서는 차이가 거의 없다
class Dog1 {
static var species = "Dog"
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
}
func trainning() {
print("월월 저는 \(Dog1.species)입니다.")
sit() // self가 생략된 형태라고 보면됨
self.sit() // self키워드는 명확하게 지칭하는 역할일 뿐
}
**// 구조체로 비슷한 기능을 구현할때는 조금 달라짐
// 해당 메서드는 클래스의 저장속성의 값을 바꾸는 기능을 가지고 있음**
func changeName(newName name: String) {
self.name = name
}
}
let bori1 = Dog1(name: "보리", weight: 20.0)
bori1.trainning()
bori1.changeName(newName: "말썽쟁이보리")
bori1.sit()
struct Dog2 {
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
// 클래스에서는 메서드 내에서 속성을 수정해도 상관 없었지만
// 구조체에서는 mutating키워드를 넣어야만 속성을 수정할 수 있음
mutating func changeName(newName name: String) {
// 저장속성을 수정하는 경우
**self.name = name**
}
}
var dog = Dog2(name: "흰둥이", weight: 20.0)
// 저장속성 자체에 접근을해서 값을 변경하는건 언제라도 가능
dog.name = "흰순둥이"
**// ⭐️메서드를 통해 속성을 수정하려면 mutating키워드가 반드시 필요**
dog.changeName(newName: "흰순둥이")
class Dog {
static var species = "Dog"
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
// 타입 메서드에서, 타입속성에 접근시에는 타입으로 접근하지 않아도 됨
static func letmeKnow() {
print("종은 항상 \(species)입니다.")
// Dog.species라고 써도됨
}
// ⭐️타입 메서드의 호출
Dog.letmeKnow()
// 타입 메서드의 예시
Int.random(in: 1...100) // 정수라는 클래스안에서 바로 사용할수 있는 메서드
아래와 같은 문법을 서브크립트로 구현해본다면?
var array = ["Apple", "Swift", "iOS", "Hello"]
array[0] // "Apple"
class SomeData {
var datas = ["Apple", "Swift", "iOS", "Hello"]
// 1) 함수와 동일한 형태이지만, 이름은 subscript
// 2) get/set은 계산속성에서의 형태와 비슷
// 대괄호안에 Int를 넣고 그러면 String이 반환된다는 뜻
subscript(index: Int) -> String {
get {
// 대괄호 안에 index를 넣으면 return으로 datas[index]
return datas[index]
}
// 파라미터를 넣고싶다면 set(parameter)하고 newValue대신 parameter
set {
datas[index] = newValue
}
}
}
var data = SomeData()
// data.data[0] 원래는 이렇게 구현해야하지만 subscript를 구현하면 아래형식으로도 가능
data[0] // "Apple"
data[0] = "AAA"
서브스크립트 실제 사용 예시 1
struct TimesTable {
let multiplier: Int = 3
// 인스턴스의 대괄호에 Int를 넣으면 3*index를 반환한다
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable()
print("6에 3배를 하면 숫자 \(threeTimesTable[6]) 이(가) 나옵니다.")
서브스크립트 실제 사용 예시 2
struct Matrix {
// 2차원 배열
var data = [["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"]]
// 2개의 파라미터를 받는 읽기전용 서브스크립트의 구현
subscript(row: Int, column: Int) -> String? {
if row >= 3 || column >= 3 {
// return형이 있는 함수의 경우 return을 만나면 함수 종료
return nil
}
return data[row][column]
}
}
// 2개의 파라미터를 받는 서브스크립트 구현도 가능
var mat = Matrix()
mat[0, 1]! // 대괄호 안에 파라미터 2개 필요, "2"반환
class SomeClass {
// 인스턴스의 .name에 접근할수 없어짐
private var name = "이름"
func nameChange(name: String) {
self.name = name
}
}
var s = SomeClass()
// 은닉화(private)해도 변경은 가능함
s.nameChange(name: "홍길동")
class Singleton {
// 올바른 싱글톤 패턴의 구현
static let shared = Singleton()
var userInfoID = 12345
// 생성자를 숨겨버리면 새로운 인스턴스를 절대 생성할 수 없음
private init() {}
}
// 싱글톤을 선언하는 순간 유일한 인스턴스가 생성됨(절대 새로운 인스턴스 안생김)
Singleton.shared // userInfoID = 12345
// 원래 존재하던 인스턴스를 넣는거임
let object = Singleton.shared
object.userInfoID = 12346
Singleton.shared.userInfoID // 12346
// 그렇지만 새로운 객체를 생성할 수는 있음
// private init() {}가 없다면 가능하지만 있다면 불가능(error)
let object3 = Sington()
object3.userInfoID
배열은 실제로 구조체로 구현이 되어있기 때문에 스택영역에 무조건존재하는줄알았는데 클래스내의 저장속성에 배열이저장되어있는 경우엔 힙영역에 존재