강의 - www.fastcampus.co.kr/dev_online_iosapp
깃허브 - github.com/FreeDeveloper97/iOS-Study-FastCompus
08. 스위프트 class
" class "
reference 타입을 저장하는 용도로 사용된다
저장영역 : Heap 영역에 저장된다 Stack에 비해 (속도가 느리다)
시스템에서 동적으로 할당이 가능하다.
다만, 자동으로 데이터를 제거하지 않는다.
class를 저장하는 변수는 stack에서 저장된다
" class vs struct 차이점 "
" class vs struct 구조 "
struct와 class의 구조는 거의 비슷하다
1. struct 구조
-> struct의 경우 property를 수정하는 method의 경우 mutating 키워드가 필요하다
//struct 구조
struct PersonStruct {
//properties
var firstName: String
var lastName: String
var fullName: String {
return "\(firstName) \(lastName)"
}
//method
mutating func uppercaseName() {
//String - uppercased() : 대문자로 변환하는 메소드
firstName = firstName.uppercased()
lastName = lastName.uppercased()
}
//struct의 경우 init이 없어도 된다는 특징이 있다
init(firstN: String, lastN: String) {
self.firstName = firstN
self.lastName = lastN
}
}
2. class 구조
-> class의 경우 init 함수가 필수이다
//class 구조
class PersonClass {
//properties
var firstName: String
var lastName: String
var fullName: String {
return "\(firstName) \(lastName)"
}
//class의 경우 mutating을 사용하지 않는다
func uppercaseName() {
//String - uppercased() : 대문자로 변환하는 메소드
firstName = firstName.uppercased()
lastName = lastName.uppercased()
}
//class의 경우 init이 필수로 있어야 한다
init(firstN: String, lastN: String) {
self.firstName = firstN
self.lastName = lastN
}
}
" class vs struct 값 비교 "
//struct vs class 값 비교
var personStruct1 = PersonStruct(firstN: "Jason", lastN: "Lee")
var personStruct2 = personStruct1
var personClass1 = PersonClass(firstN: "Jason", lastN: "Lee")
var personClass2 = personClass1
personStruct2.firstName = "Jay"
personStruct1.fullName //Jason Lee
personStruct2.fullName //Jay Lee
personClass2.firstName = "Jay"
personClass1.fullName //Jay Lee
personClass2.fullName //Jay Lee
//동적으로 새로운 클래스 생성 후 비교
personClass2 = PersonClass(firstN: "Bob", lastN: "Lee")
personClass1.fullName //Jay Lee
personClass2.fullName //Bob Lee
//참조값을 동일하게 설정
personClass1 = personClass2
personClass1.fullName //Bob Lee
personClass2.fullName //Bob Lee
class의 경우 참조값이기 때문에 수정하면 같이 수정되는 모습을 볼 수 있다
struct의 경우 독립적인 값이기 때문에 수정하면 따로 수정되는 모습을 볼 수 있다
" struct를 사용하는 경우 "
1. 두 object가 "같음" 또는 "다름"을 비교하는 경우
let point1 = Point(x: 3, y: 5)
let point2 = Point(x: 3, y: 5)
print(point1 == point2)
2. copy된 각 객체들이 독립적인 상태를 가져야 하는 경우
var myMac = Mac(owner: "Jason")
var yourMac = myMac
yourMac.owner = "Jay"
myMac.owner
yourMac.owner
3. 코드에서 object의 데이터를 여러 스레드 걸쳐 사용할 경우 사용된다
-> 각 스레드에서 유니크한 struct 값을 사용하기 때문에 꼬일 위험이 없어 안전하다
" class를 사용하는 경우 "
1. 두 object의 인스턴스 자체가 같음을 확인해야 할 때 사용된다
-> 독립적인 값의 상반되는 개념으로 동일대상인지 여부를 확인해야 할 때 사용된다
2. 하나의 객체가 필요하고, 여러 대상에 의해 접근되고 변경이 필요한 경우 사용된다
" class - property로 struct를 지니는 경우 "
1. struct
//성적 struct
struct Grade {
var letter: Character
var points: Double
var credits: Double
}
2. class 내의 struct를 넣을 수 있다
//학생 class
class Student {
var grades: [Grade] = [] //struct를 넣을 수 있다
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func printMyName() {
print("My name is \(firstName) \(lastName)")
}
}
" class - 상속 "
super class(부모)의 속성 + 추가적인 property, method를 추가 -> child class(자식) 클래스가 된다
-> 동일속성은 부모를 통해 사용함으로써 코드 중복을 줄일 수 있다
//사람 class
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func printMyName() {
print("My name is \(firstName) \(lastName)")
}
}
1. 독립적인 class의 경우 구조
//학생 class
class Student {
var grades: [Grade] = []
//중복되는 property가 보인다
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func printMyName() {
print("My name is \(firstName) \(lastName)")
}
}
2. 상속을 통해 class를 만든 경우 구조
-> class 클래스명 : 부모클래스명 { } 구조를 통해 상속이 가능하다
-> 중복된 property, method 들은 부모의 속성을 사용하면 된다
//학생 class
class Student_after: Person {
var grades: [Grade] = []
//중복되는 property들은 상속받았기 때문에 그대로 사용이 가능하다!
}
3. 부모의 property, method 사용예시
//상속받았기 때문에 property, method를 그대로 사용할 수 있다
//상속받은 class는 부모의 init을 사용할 수 있다
let jay = Person(firstName: "Jay", lastName: "Lee")
let jason = Student(firstName: "Jason", lastName: "Lee")
jay.firstName
jason.firstName
jay.printMyName()
jason.printMyName()
" class - 상속 of 상속 "
상속은 무한으로 할 수 있다
1. Person
//Person
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func printMyName() {
print("My name is \(firstName) \(lastName)")
}
}
2. Person -> Student
//Person -> Student
class Student: Person {
var grades: [Grade] = []
//중복되는 property들은 상속받았기 때문에 그대로 사용이 가능하다!
}
3. Person -> Student -> StudentAthlete
//Person -> Student -> StudentAthlete
class StudentAthlete: Student {
var minimumTraningTime: Int = 2
var trainedTime: Int = 0
func train() {
trainedTime += 1
}
}
4. Person -> Student -> StudentAthlete -> FootballPlayer
//Person -> Student -> StudentAthlete -> FootballPlayer
class FootballPlayer: StudentAthlete {
var footballTeam = "FC Swift"
//같은 메소드를 자식에서 수정할 때 override를 사용한다
override func train() {
trainedTime += 2
}
}
" class - 상속 - override "
부모의 method를 그대로 사용하지 않고 수정해야 하는 경우
-> override를 통해 재정의 할 수 있다
1. super class : train() method
class StudentAthlete: Student {
var minimumTraningTime: Int = 2
var trainedTime: Int = 0
func train() {
trainedTime += 1
}
}
2. child class : train() method : override (재정의)
class FootballPlayer: StudentAthlete {
var footballTeam = "FC Swift"
//같은 메소드를 자식에서 수정할 때 override를 사용한다
override func train() {
trainedTime += 2
}
}
3. override 결과 확인
var athelete1 = StudentAthlete(firstName: "Yuna", lastName: "Kim")
var athelete2 = FootballPlayer(firstName: "Heung", lastName: "Son")
//override 확인
athelete1.train()
athelete2.train()
athelete1.trainedTime //1
athelete2.trainedTime //2
" class - upperCast "
※ super class (부모) <- child class (자식)이 가능하다! ※
-> 부모 class의 그릇에 해당되는 자식 class 값들을 담을 수 있기 때문이다
-> 자식 class에 추가적으로 정의된 property, method는 사용할 수 없게 된다 (데이터 누락 발생)
-> override 된 method가 있는 경우 재정의된 method가 동작된다!
var athelete1 = StudentAthlete(firstName: "Yuna", lastName: "Kim")
var athelete2 = FootballPlayer(firstName: "Heung", lastName: "Son")
//upperCast를 통해 부모 <- 자식 저장이 가능하다
athelete1 = athelete2 as StudentAthlete
//다만 부모의 method 대신 자식의 override된 method가 작동된다
athelete1.train() //+2가 된다
athelete1.trainedTime //1이 아닌 2가 된다
" class - downCast "
super class (부모) -> child class (자식)으로 변경하는 경우
※ child class에 해당되는 property값이 없는경우 에러가 발생하므로 optional, binding을 사용하여야 한다 ※
//downCast를 통해 FootBall로 바꿀 수 있다
if let son = athelete1 as? FootballPlayer {
print("--> team: \(son.footballTeam)")
}
" class - 상속 - init "
1. 별다른 명시가 없는 경우 부모class의 init을 사용할 수 있다 (init)
var athelete1 = StudentAthlete(firstName: "Yuna", lastName: "Kim")
var athelete2 = FootballPlayer(firstName: "Heung", lastName: "Son")
2. 명시적으로 부모class의 init을 사용할수도 있다 (super.init)
struct Grade {
var letter: Character
var points: Double
var credits: Double
}
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func printMyName() {
print("My name is \(firstName) \(lastName)")
}
}
class Student: Person {
var grades: [Grade] = []
override init(firstName: String, lastName: String) {
super.init(firstName: firstName, lastName: lastName)
}
}
3. 새로운 property를 초기화해야할 경우 새로운 init을 작성할 수 있다 (self.init)
※ 상속된 class에서 init을 할 경우 부모 class의 super.init 까지 완료된 후에 부모 class의 property, method 사용이 가능하다 ※
// 학생인데 운동선수
class StudentAthlete: Student {
var minimumTrainingTime: Int = 2
var trainedTime: Int = 0
var sports: [String] //새로운 property
//새로운 property를 초기화하기 위한 새로운 init이 필요하다
init(firstName: String, lastName: String, sports: [String]) {
//자식의 init 실행 -> 부모의 init 실행
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
//super.init를 완료한 후에 method를 사용이 가능하다
self.train()
}
}
4. 추가적인 init을 만들 수 있다 (conveience init)
※ 단, 꼭 self.init이 있어야만 사용이 가능하다 ※
class StudentAthlete: Student {
var minimumTrainingTime: Int = 2
var trainedTime: Int = 0
var sports: [String]
init(firstName: String, lastName: String, sports: [String]) {
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
}
//두번째의 init
convenience init(name: String) {
self.init(firstName: name, lastName: "", sports: [])
}
}
" class - designated init vs convenience init "
designated init : 일반적인 init()
convenience init : 추가적인 init()
1. designated init은 super의 designated init을 호출해야 한다
2. convenience init은 같은 class 내의 init을 꼭 호출해야 한다
3. 결국 convenience init은 designated init을 호출해야 한다
" class - 상속을 사용하는 경우 "
1. single responsibility (단일책임)
-> 한 클래스는 하나의 책임을 지니면 된다. 여러개의 책임을 지닐 필요가 없다!
-> 즉 최대한 한 기능위주로 작성되어야 한다
2. type safety (타입이 분명해야 할 때)
3. shared base classes (다자녀가 있다)
4. extensibility (확장성이 필요한 경우)
5. identity (정체를 파악하기 위해서)
'iOS 개발자 > swift 기초' 카테고리의 다른 글
스위프트 기초6 (structure, method, property, protocol, mutating, extension) (0) | 2021.02.19 |
---|---|
스위프트 기초5 (collection, closure, first class type) (0) | 2021.02.15 |
스위프트 기초4 (collection, array, dictionary, set) (0) | 2021.02.15 |
스위프트 기초3 (function, parameter, overload, optional, binding, coalescing) (0) | 2021.02.13 |
스위프트 기초2 (while, repeat, for, switch) (0) | 2021.02.09 |