Swift Class와 상속

‍deprecated·2021년 1월 20일
0

Swift 시작하기

목록 보기
7/12
post-custom-banner

Class

Object = Data (Property) + Method

StructureClass
valuereference
CopyShare
Stack(빠름)Heap(느림)
struct PersonStruct{
    var firstName:String
    var lastName:String
    var fullName:String{
    	return "\(firstName) \(lastName)"
    }
    mutating func uppercaseName(){
    	firstName = firstName.uppercased()
        lastName = lastName.uppercased()
    }
}

// init은 클래스 객체 생성시 사용. 없으면 안 됨. 
// struct도 init 사용 가능 (init 굳이 안써도 memberwise initializer를 제공)

class PersonClass{
    var firstName:String
    var lastName:String
    init(firstName:String, lastName:String){
    	self.firstName = firstName
        self.lastName = lastName
    }
    var fullName:String{
    	return "\(firstName) \(lastName)"
    }
    func uppercaseName(){ // mutating X
    	firstName = firstName.uppercased()
        lastName = lastName.uppercased()
    }
}

var personStruct1 = PersonStruct(firstName:"Jason", lastName:"Lee")
var personStruct2 = personStruct1

var personClass1 = PersonClass(firstName:"Jason", lastName:"Lee")
var personClass2 = personClass1

personStruct2.firstName = "Jay"
print(personStruct1.firstName) // Jason 출력  (안 바뀜! - 깊은 복사)
print(personStruct2.firstName) // Jay 출력

personClass2.firstName = "Jay"
print(personClass1.firstName) // Jay 출력 ! (바뀜! - 얕은 복사)
print(personClass2.firstName) // Jay 출력

personClass2 = PersonClass(firstName:"Bob", lastName:"Lee")
print(personClass1.firstName)
print(personClass2.firstName) // 같은 곳을 바라보고 있었는데 다른 곳을 가리키게 됨.

personClass1 = personClass2 // 1도 2를 가리키게 됨

Structure vs Class 언제 사용할까

- Struct

  1. 두 object를 "같다, 다르다"로 비교해야 하는 경우.
    let point1 = Point(x:3, y:5)
    let point2 = Point(x:3, y:5)
    → 데이터 자체로 비교해야 하는 경우
  2. Copy 된 각 객체들이 독립적인 상태를 가져야 하는 경우.
    var myMac = Mac(owner:"Jason")
    var yourMac = myMac
    yourMac.owner = "Jay"    
    print(myMac.owner)
    print(yourMac.owner)
  3. 코드에서 object의 데이터를 여러 스레드에 걸쳐 사용할 경우.

- Class

  1. 두 Object의 인스턴스 자체가 같음을 확인해야 할 때
  2. 하나의 객체가 필요하고, 여러 대상에 의해 접근되고 변경이 필요할 경우 ex) UIApplication

요약 : 일단 Struct로 하자.

상속

중복되는 것을 적용할 Class에

class Student:Person{
    var grades:[Grade]=[]
}

let jay=Person(firstName:"Jay", lastName:"Lee")
let jason=Student(firstName:"Jason", lastName:"Kim") // initalizer 없어도 됨!

print(jay.firstName)
print(jason.firstName)

let math = Grade(letter:"B", points:0.5, credits:3)
jason.grades.append(math)

let history = Grade(letter:"A", points:10, credits:3)
jason.grades.append(history)

print(jason.grades.count)

A is B (A는 B에 포함된다.)
Student is Person.
Person : superclass(parent class)
Student : subclass(child class)

상속의 규칙

  1. 자식은 한 개의 Superclass만 상속받음.
  2. 부모는 여러 자식을 가질 수 있다.
  3. 상속의 깊이는 상관이 없다.

학생인데 운동선수

class StudentAthelete:Student{
   var minimumTrainingTime:Int = 2
   var trainingTime:Int = 0
   func train(){
       trainedTime += 1
   }
}

override 의 사용

A subclass can provide its own custom implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit from a superclass. This is known as overriding. - Swift 공식문서

운동선수인데 축구선수

class FootballPlayer:StudentAthelete{
    var footballTeam = "FC swift"
    override func train(){
        trainingTime += 2
    }
}
 
var athelete1 = StudentAthelete(firstName:"Sena", lastName:"Kim")
var athelete2 = FootballPlayer(firstName:"Henry", lastName:"Son")
 
print(athelete1.firstName)
print(athelete2.firstName)

athelete1.grades.append(math)
athelete2.grades.append(math)

athelete1.minimumTrainingTime
athelete2.minimumTrainingTime

athelete1.footballTeam // ERROR
athelete2.footballTeam

athelete1.train()
athelete2.train()

athelete1.trainingTime
athelete2.trainingTime

athelete1 = athelete2 as StudentAthelete // 가능. Uppercasing
// child class의 동일 함수를 그대로 사용하는 듯. 변수도 덮어씌워짐

athelete1.train()
athelete1.trainedTime
athelete1.footballTeam // ERROR
if let son = athelete1 as? FootballPlayer { // Downcasting
    print("->team:\(son.footballTeam)") 
    // StudentAthelete 형태인데, FootballPlayer로 다운캐스팅하여 FootballPlayer property에 접근

상속은 언제하면 좋을까

  • Simple Responsibility 단일 책임
    각 클래스는 하나의 고려사항만 있으면 된다.
  • Type Safety
    클래스 간 명확한 구분 지워줘야 할 때. 부모 혹은 다른 자식 클래스간 구분 명확.
  • Shared Base Classes
    다자녀가 있다.
    내용 자체가 다르게 구현되어야 할 때 (의대생, 예체능생, ...)
  • Extension
    확장성이 필요할 경우
  • Identity
    정체를 파악하기 위해 (체대생이야? 의대생이야?)

생성자

순서 중요!

class StudentAthelete:Student{
    var minimumTrainingTime:Int = 2
    var trainingTime:Int = 0
    var sports:[String]
    init(firstName:String, lastName:String, sports:[String]){
    	// 순서가 중요하다 !
        // 1st. 자식 클래스의 stored property 먼저
    	self.sports = sports
        // 2nd. 부모 클래스의 stored property. 여기에선 Person의 init이다.
        super.init(firstName:firstName, lastName:lastName) 
    }
}

let student1 = Student(firstName:"Jason", lastName:"Lee")
let student2 = StudentAthelete(firstName:"Jay", lastName:"Kim", sports:["football"])

2-phase Initialization (클래스 생성 시 2가지 단계)

Phase 1 : Person <- Student <- StudentAthelete (self.sports -> super.init)
Phase 2 : Person -> Student -> StudentAthelete (self.train)

Phase 2

  • Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
  • Finally, any convenience initializers in the chain have the option to customize the instance and to work with self. - 공식문서

모든 파라미터가 initializer로 안 쓰일 수 있다.

부 initializer

convenience init(name:String){ //이름만 받겠다 !
    self.init(firstName:name, lastName:"", sports:[])
}
let student3 = StudentAthelete(name:"Mike")

축소시키기

class Student:Person{
    var grade:[Grade]=[]
    // 주 init (=designated init)
    override init(firstName:String, lastName:String){ 
    	super.init(firstName:firstName, lastName:lastName)
    }
    // 부 init (=convenience init)
    convenience init(student:Student){
    	self.init(firstName:student.firstName, lastName:student.lastName)
  • DI는 자신의 부모의 DI를 호출해야 함.
  • CI는 같은 클래스의 이니셜라이저를 꼭 하나 호출해야 함.
  • CI는 궁극적으로는 DI를 호출해야 함.
profile
deprecated
post-custom-banner

0개의 댓글