swift 기초 문법 <8>

구찌댕댕이·2022년 7월 9일
0

swift 기초 문법

목록 보기
8/12
post-thumbnail

delegate

delgeate의 뜻 : 대표자, 위임하다
클래스나 구조체가 다른타입의 인스턴스에게 책임을 전달(위임)할수 있도록 하는 디자인 패턴
위임된 기능은 프로토콜에서 정의한다
delegate가 위임된 기능을 제공
위임은 특정작업에 응답하거나, 외부에서 데이터를 가져오는데 사용
대리자, 조력자 의 개념
어떠한 일이 있을 때 delegate가 대신 전담해달라는 개념
-> delegate로 선언된(보통 자신이 만든 클래스) 객체는 자신을 임명한 객체(테이블뷰, 피커뷰 등)가 일을 도와달라고 하면 지정된 메소드를 통하여 처리해줌
델리게이트 패턴
-> 하나의 객체가 모든일을 처리하는것이 아닌 일부 처리해야할 일 중 일부기능을 다른 객체에 넘기는 것
보통 프로토콜을 사용

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    //ViewController 라는 이름의 클래스 생성 : 부모는 UIViewController, 프로토콜은 UIPickerViewDelegate(필요한 기능의 메소드만 구현), UIPickerViewDataSource(필수메소드 모두 구현해야함)
    @IBOutlet var pickerImage : UIPickerView! //인스턴스 생성
    pickerImage.delegate = self //pickerImage가 UIPickerView의 델리게이트임을 지정
    func pikcerView(_ pickerView: UIPckerView, didSelectRow row: Int, inComponent component: Int) {
        //comform
        //피커뷰가 필요할 때 호출함
        //사용자가 룰렛을 선택할때마다 피커뷰가 호출
    }
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
        //comform
        //필수 메소드
        //피커뷰가 컴포넌트의 수를 알고싶을때 호출
    }
}

UIPickerViewDelegate : 어떤 행동에 대한 반응 동작 메소드(필요한 것만 구현)

UIPickerView에 대한 프로토콜로 UIPickerView를 도와줄 조력자
UIPickerView에 어떤 일이 일어났을 때 하고 싶은 일들이 메소드 목록(선언)되어 있다
UIPickerView를 사용하는 클래스에서는 UIPickerViewDelegate를 채택(adopt) 해야 하며
채택한 클래스에서는 자신이 델리게이트라고 지정해야함. -> pickerImage.delegate = self
구현한 기능은 iOS프레임워크 내부적으로 원하는 시점에 UIPickerView가 자동으로 호출(callback)
그 시점에서 하고 싶은 기능들을 메소드 내부에 구현

UIPickerViewDataSource : 데이터를 받아 뷰를 그려주는 메소드(필수메소드는 반드시 구현)

TableView의 DataSource
필수메소드 2개

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return items.count
} //numberOfRowsInSection section : 섹션의 행(row) 갯수
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cellTypeIdentifier", for: indexPath)
    cell.textLabel!.text = "Cell text"
    return cell
} //cellForRowAt indexPath : 각 행에 들어가는 데이터
//섹션의 갯수는 default 1

TableView의 Delegate

UITableViewDelegate 프로토콜
header, footer view를 만들고 관리
행, header, footer 높이 지정
스무스한 스크롤링을 위해 높이 추정치 제공
행 선택시 하고싶은 작업
스와이프 할때 작업
테이블 내용 편집 지원

  • willSelectRowAt : 지정된 행을 선택하려 한다는 것을 delegate에게 알림
  • didSelectRowAt : 지정된 행이 이제 선택되었음을 delegate에게 알림
  • willDeselectRowAt : 지정된 행을 선택 해제하려 한다는 것을 delegate에게 알림
  • didDeselectRowAt : 지정된 행이 해제되었음을 delegate에게 알림

초기화

Designated Initializers & Convenience Initializers

  • 프로퍼티 초기화 방법 3가지
  1. 프로퍼티 선언할 때 초기값을 할당
  2. 옵셔널 타입의 프로퍼티 선언
  3. init함수로 프로퍼티 초기화
  • Designated Initializer : 모든 프로퍼티를 다 초기화시키는 생성자. 초기화되지 않은 프로퍼티가 있을 경우 클래스에 반드시 1개 이상 필요
  • Convenience Initializer : 일부 프로퍼티만 초기화. 보조 initializer. 다른 initializer를 이용하여 초기화(initializer delegation) -> self.init()

다양한 방법으로 객체를 만들 수 있도록 편의를 제공하려면 init을 overloading해야 하고 코드 중복이 발생
코드 중복 방지

class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init(age: Int, weight: Double) {
        self.age = age  // self.age 는 클래스의 프로퍼티
        self.weight = weight  // age 는 init의 매개변수
    }
    convenience init(age:Int) { // 매개변수 1개
        self.init(age:age, weight: 3.5) // init overloading을 사용했다면 weight프로퍼티에 직접 초기값 부여
    }
    convenience init() { //매개변수 0개
        self.init(age:20, weight: 60.5) // 프로퍼티 2개 다 초기값 부여
    }
}
var kim = Man(age:10, weight: 20.4)
var lee : Man = Man(age: 1)

//주의사항
class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init() {
        self.age = 1
        self.weight = 99
    }
    init(age: Int, weight: Double) {
        self.age = age
        self.weight = weight
    }
    convenience init(age:Int) { // convenience init(매개변수) {
        self.init()             //   초기화 위임 -> self.init()
        self.age = age          //   자신의 초기화 코드 -> self.age = age
    }                           // } 위의 순서가 바뀌면 에러
}
var kim = Man() // 1번 init
var lee : Man = Man(age: 1, weight: 20.5) // 2번 init
var han = Man(age:20) // 3번 init
kim.display() // 1, 99
lee.display() // 1, 20.5
han.display() // 20, 99

failabel initiaizer (실패 가능한 생성자 : init? init!)

init 다음에 ? 나 ! 하며 옵셔널 값이 리턴 됨
오류 상황에 nil 값을 리턴하는 조건문이 있음 -> return nil

class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init?(age: Int, weight: Double) { //실패 가능한 생성자
        if age <= 0 {
            return nil //오류상황에서는 nil 리턴
        } else {
            self.age = age
        }
        self.weight = weight
    }
}
var kim : Man = Man(age: 1, weight: 20.5)
kim.display() // 옵셔널 값이므로 언래핑 해줘야 한다
//1. 옵셔널 바인딩 if let kim1 = kim { kim1.display() }
//2. 인스턴스 생성과 동시에 옵셔널 바인딩 if let kim2 = Man(age:1,weight:2){ kim2.display() }
//3. 강제 언래핑 -> crash

crash 날때 발생하는 오류와 .so파일

  • .so 나 .dylib
    shared object
    shared library
    윈도우의 dll
    동적 링크 라이브러리(프로그램 실행 시 필요할 때 연결)
  • .a
    archive library
    정적 링크 라이브러리
    컴파일 시 포함됨

initializer가 상속 되는 경우

  • Designated Initializer 와 Convenience Initializer 모두 상속 되는 경우
//자식클래스에 Designated Initializer가 없는 경우
class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init(age: Int, weight: Double) {
        self.age = age
        self.weight = weight
    }
    convenience init(age:Int) {
        self.init(age:age, weight: 3.5)
    }
}
class Student : Man {
    var name : String = "kim" //자식클래스의 프로퍼티
    init(age:Int, weight:Double, name : String){ // 자식클래스의 Designated Initializer
        self.name = name  //자식클래스의 프로퍼티 초기화
        super.init(age:age, weight:weight)  //부모클래스의 init 호출
    }
    func namePlus() {
        print("\(age),\(name),\(weight)")
    }
}
var kim1 = Student(age: 20,weight: 65.2,name: "fewa")
kim1.display()
var lee1 = Student(age: 19,weight: 65.2,name: "fewa")
lee1.namePlus() //부모클래스의 Convenience Initializer 상속 불가능

//자식클래스에 초기화가 필요한 프로퍼티가 없을 경우
class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init(age: Int, weight: Double) {
        self.age = age
        self.weight = weight
    }
    convenience init(age:Int) {
        self.init(age:age, weight: 3.5)
    }
}
var kim = Man(age:10, weight:20.5)
kim.display()
var lee = Man(age:1)
lee.display()

class Student : Man {
    // 초기화가 필요한 프로퍼티가 없음
}
var kim1 = Student(age: 20,weight: 65.2) //Designated Initializer 상속
kim1.display()
var lee1 = Student(age: 19) //Convenience Initializer 상속
lee1.display()
  • Convenience Initializer 만 상속 되는 경우
    자식클래스에서 부모클래스의 Designated Initializer 를 override하는 경우
    override init(매개변수) {
    자식클래스의 프로퍼티 초기화
    부모클래스의 Designated Initializer에 위임 -> super.init()
    나머지 초기화 소스
    }
class Man {
    var age : Int
    var weight : Double
    func display() {
        print("나이 = \(age), 몸무게 = \(weight)")
    }
    init(age: Int, weight: Double) {
        self.age = age
        self.weight = weight
    }
    convenience init(age:Int) {
        self.init(age:age, weight: 3.5)
    }
}
class Student : Man {
    var name : String
    override init(age:Int, weight:Double){
        self.name = "Kim"   //자식클래스의 프로퍼티 초기화
        super.init(age:age, weight:weight)  //부모클래스의 메소드 호출
    }
}
var kim1 = Student(age: 20,weight: 65.2)
kim1.display()
var lee1 = Student(age: 19) //부모클래스의 Convenience Initializer 상속
lee1.display()

required initializer

부모클래스의 init 메소드 앞에 required 가 붙으면 자식 클래스에서 반드시 required 키워드로 재작성 해야한다

출처 : https://www.youtube.com/channel/UCM8wseo6DkA-D7yGlCrcrwA

profile
개발자를 꿈꾸는 사람 입니당

0개의 댓글