iOS 개발자/swift 기초

스위프트 기초6 (structure, method, property, protocol, mutating, extension)

FDEE 2021. 2. 19. 15:35

강의 - www.fastcampus.co.kr/dev_online_iosapp

 

iOS 앱 개발 올인원 패키지 Online. | 패스트캠퍼스

차근차근 따라하다 보면 어느새 나만의 iOS 앱을 개발할 수 있게 됩니다.

www.fastcampus.co.kr

깃허브 - github.com/FreeDeveloper97/iOS-Study-FastCompus

 

FreeDeveloper97/iOS-Study-FastCompus

Contribute to FreeDeveloper97/iOS-Study-FastCompus development by creating an account on GitHub.

github.com

 

07. 스위프트 structure

 

" structure "

struct : 구조체 : 의미적으로 관계가 있는 것들을 묶어서 표현하는 방식

 

※ 구조체 사용전 구조 ※

// 문제: 가장 가까운 편의점 찾기
/*----------------------- struct 사용전 -------------------------*/

// 주어진 편의점 정보
let store1 = (x: 3, y: 5, name: "gs")
let store2 = (x: 4, y: 6, name: "seven")
let store3 = (x: 1, y: 7, name: "cu")

// 거리 구하는 함수
func distance(current: (x: Int, y: Int), target: (x: Int, y: Int)) -> Double {
    // 피타고라스..
    let distanceX = Double(target.x - current.x)
    let distanceY = Double(target.y - current.y)
    let distance = sqrt(distanceX * distanceX + distanceY * distanceY)
    return distance
}

// 가장 가까운 편의점 프린트하는 함수
func printClosestStore(currentLocation:(x: Int, y: Int), stores:[(x: Int, y: Int, name: String)]) {
    var closestStoreName = ""
    var closestStoreDistance = Double.infinity

    for store in stores {
        let distanceToStore = distance(current: currentLocation, target: (x: store.x, y: store.y))
        closestStoreDistance = min(distanceToStore, closestStoreDistance)
        if closestStoreDistance == distanceToStore {
            closestStoreName = store.name
        }
    }
    print("Closest store: \(closestStoreName)")
}

// Stores Array 세팅, 현재 내 위치 세팅
let myLocation = (x: 2, y: 2)
let stores = [store1, store2, store3]

// printClosestStore 함수이용해서 현재 가장 가까운 스토어 출력하기
printClosestStore(currentLocation: myLocation, stores: stores)

※ 구조체 사용후 구조 ※

/*----------------------- struct 사용후 -------------------------*/

// Improve Code
// - make Location struct
struct Location {
    let x: Int
    let y: Int
}
// - make Store struct
struct Store {
    let loc: Location
    let name: String
}

// 주어진 편의점 정보
let store1_after = Store(loc: Location(x: 3, y: 5), name: "gs")
let store2_after = Store(loc: Location(x: 4, y: 6), name: "seven")
let store3_after = Store(loc: Location(x: 1, y: 7), name: "cu")

// 거리 구하는 함수
func distance_after(current: Location, target: Location) -> Double {
    // 피타고라스..
    let distanceX = Double(target.x - current.x)
    let distanceY = Double(target.y - current.y)
    let distance = sqrt(distanceX * distanceX + distanceY * distanceY)
    return distance
}

// 가장 가까운 편의점 프린트하는 함수
func printClosestStore_after(currentLocation: Location, stores:[Store]) {
    var closestStoreName = ""
    var closestStoreDistance = Double.infinity

    for store in stores {
        let distanceToStore = distance_after(current: currentLocation, target: store.loc)
        closestStoreDistance = min(distanceToStore, closestStoreDistance)
        if closestStoreDistance == distanceToStore {
            closestStoreName = store.name
        }
    }
    print("Closest store: \(closestStoreName)")
}

// Stores Array 세팅, 현재 내 위치 세팅
let myLocation_after = Location(x: 2, y: 5)
let stores_after = [store1_after, store2_after, store3_after]

// printClosestStore 함수이용해서 현재 가장 가까운 스토어 출력하기
printClosestStore_after(currentLocation: myLocation_after, stores: stores_after)

- 의미있는 좌표들을 Location으로 묶었다

- 의미있는 좌표와 이름을 Store로 묶었다

 

※ 구조체 메소드 추가 구조 ※

/*----------------------- struct 메소드 추가 -------------------------*/

struct Store2 {
    let loc: Location
    let name: String
    let deliveryRange = 2.0
    
    func isDeliverable(userLoc: Location) -> Bool {
        let distanceToStore = distance_after(current: userLoc, target: loc)
        return distanceToStore <= deliveryRange
    }
}

// 가장 가까운 편의점 프린트하는 함수
func printClosestStore_after2(currentLocation: Location, stores:[Store2]) {
    var closestStoreName = ""
    var closestStoreDistance = Double.infinity
    //배송여부 추가
    var isDeliverable = false

    for store in stores {
        let distanceToStore = distance_after(current: currentLocation, target: store.loc)
        closestStoreDistance = min(distanceToStore, closestStoreDistance)
        if closestStoreDistance == distanceToStore {
            closestStoreName = store.name
            //배송여부 확인
            isDeliverable = store.isDeliverable(userLoc: currentLocation)
        }
    }
    print("Closest store: \(closestStoreName), isDeliverable: \(isDeliverable)")
}

// 주어진 편의점 정보
let store1_after2 = Store2(loc: Location(x: 3, y: 5), name: "gs")
let store2_after2 = Store2(loc: Location(x: 4, y: 6), name: "seven")
let store3_after2 = Store2(loc: Location(x: 1, y: 7), name: "cu")

// Stores Array 세팅
let stores_after2 = [store1_after2, store2_after2, store3_after2]

// printClosestStore 함수이용해서 현재 가장 가까운 스토어 출력하기
printClosestStore_after2(currentLocation: myLocation_after, stores: stores_after2)

구조체와 연관이 있는 행동은 구조체 안에 method로 추가한다

" structure vs class "

structure : value 타입 / 값이 복사되어 할당된다 / Stack에 저장된다

class : reference 타입 / 값이 참조되는 형태이다 / Heap에 저장된다

/*----------------------- struct vs class -------------------------*/
struct PersonStruct {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}
class PersonClass {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

var pStruct1 = PersonStruct(name: "Jason", age: 5)
var pStruct2 = pStruct1
pStruct2.name = "Hey"

let pClass1 = PersonClass(name: "Jason", age: 5)
let pClass2 = pClass1
pClass2.name = "Hey"

/* Struct는 값이 복사되었기 때문에 기존값에 변화가 없다 */
pStruct1.name //Jason
pStruct2.name //Hey

/* Class는 값을 참조하기 때문에 기존값과 동일하다 */
pClass1.name //Hey
pClass2.name //Hey

struct의 경우 값을 복사하기 때문에 해당 struct값만 변경되지만

class의 경우 참조하기 때문에 기존 class값 또한 변경된다

" structure - protocol "

※ protocol : 프로토콜 - 지켜야할 약속, 해야할 일들의 목록을 뜻 ※

description : Struct를 출력시 반환하는 내용을 정의

/* 도전과제 */
//1. 강의 이름, 강사 이름, 학생수를 가지는 struct 만들기 (Lecture)

/* protocol 프로토콜 사용 */
/* 지켜야할 약속, 해야할 일들의 목록 */
// CustomStringConvertible : print시 원하는 형태로 출력되게 설정
struct Lecture: CustomStringConvertible {
    var description: String {
        return "Title: \(name), Instructor: \(instructor)"
    }
    let name: String
    let instructor: String
    let studentsNumber: Int
}

let Lecture1 = Lecture(name: "iOSStudy", instructor: "FastCampus", studentsNumber: 500)
// 프로토콜 내용 출력
print(Lecture1)

" structure - parameter "

Struct를 parameter로 받는 func 예제

//2. 강의 array와 강사 이름을 받아서 해당 강사의 강의 이름을 출력하는 함수 만들기
func printLectureName(lects: [Lecture], instructor: String) {
    //1. 일반적인 for문을 통한 방법
    var result = ""
    for lecture in lects {
        if lecture.instructor == instructor {
            result = lecture.name
        }
    }
    print("--> Lecture Name : \(result)")
    
    //2. closure, optional 사용한 방법
    let lectureName = lects.first { lec in
        return lec.instructor == instructor
    }?.name ?? ""
    print("--> Lecture Name : \(lectureName)")
}

//함수 실행
//3. 강의 3개를 만들고 강사 이름으로 강의 찾기
let Lecture1 = Lecture(name: "iOSStudy", instructor: "FastCampus", studentsNumber: 500)
let Lecture2 = Lecture(name: "iOS", instructor: "FC", studentsNumber: 500)
let Lecture3 = Lecture(name: "ios", instructor: "fc", studentsNumber: 500)
let Lectures = [Lecture1, Lecture2, Lecture3]

printLectureName(lects: Lectures, instructor: "FC")

" structure - property "

※ property : 프로퍼티 - 사용되는 data 값들을 의미 (일종의 변수) ※

1. Instance property 인스턴스 프로퍼티 : struct 내의 property에 해당되는 내용

stored property

- 변수에 저장되어 있는 값들

computed property

- stored property를 사용하여 계산된 새로운 값

/* properties 프로퍼티 : data들 */
/* stored property : 변수에 저장되어 있는 값 */
/* computed property : 직접 저장하지 않고, 저장된 정보를 이용해서 가공, 혹은 계산된 값을 제공할 때 사용 */
/* type property : 생성된 instance에 상관없이 struct의 타입 자체의 속성을 정하고 싶을 때 사용 */
struct Lecture: CustomStringConvertible {
    //stored property : 값을 저장
    let name: String
    let instructor: String
    let studentsNumber: Int
    
    //computed property : 아래 정보들을 통해 계산된 값을 제공
    var description: String {
        return "Title: \(name), Instructor: \(instructor)"
    }
}

※ get, set 함수를 통해 property의 반환, 수정 연산을 지정할 수 있다 ※

struct Person {
    var firstName: String
    var lastName: String
    
    //computed property
    var fullName: String {
        get {
            return "\(firstName) \(lastName)"
        }
        set {
            if let firstName = newValue.components(separatedBy: " ").first {
                self.firstName = firstName
            }
            if let lastName = newValue.components(separatedBy: " ").last {
                self.lastName = lastName
            }
        }
    }
}

lazy property

- stored property 처럼 struct가 생성될시에 생성되지 않고 변수에 접근시에 생성되는 property

- 최적화 관점에서 사용된다

- cost가 큰 변수의 경우 필요할때만 생성되겠금 할 때 사용된다

struct Person {
    //stored property
    var firstName: String
    var lastName: String
    
    //lazy property : 인스턴스값 사용시 실행된다
    lazy var isPopular: Bool = {
        if fullName == "Jay Park" {
            return true
        } else {
            return false
        }
    }()
}

//struct 사용
var person = Person(firstName: "Jason", lastName: "Lee")
//lazy 프로퍼티 사용시에 생성
person.isPopular

2. type property 타입 프로퍼티 : struct 타입이름의 property에 해당되는 내용

type property

- static 키워드로 property를 만든다

struct Person {
    //type property
    static let isAlien: Bool = false
}

//Person 타입에 관한 프로퍼티값 사용
Person.isAlien

" structure - property observation "

observation을 통해 property가 사용되는 시점을 알 수 있다

/* observation : property가 바뀐 시점을 알 수 있다 */
struct Person {
    //stored property : observation
    var firstName: String {
        //인스턴스값 접근 이전 실행된다
        willSet {
            print("willSet: \(firstName) --> \(newValue)")
        }
        //인스턴스값 접근 이후 실행된다
        didSet {
            print("didSet: \(oldValue) --> \(firstName)")
        }
    }
    var lastName: String
}

※ willSet : newValue, 기존 property 값을 통해 새로운 값을 저장 전 수행된다

※ didSet : oldValue, 기존 property 값을 통해 새로운 값을 저장 후 수행된다

" structure - property vs method "

property로도, method로도 동일하게 수행할 수 있는 경우가 있다. 이런경우 아래와 같이 정리가 된다

간단한 경우 - property 사용 (간단하게 값을 접근, 반환하는 경우)

복잡한 경우 - method 사용 (좀더 복잡하게 값을 통해 연산처리과정 후 반환하는 경우)

/* property vs function*/
struct Person {
    var firstName: String
    var lastName: String
    
    // property
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
    //function
    func fullName_f() -> String {
        return "\(firstName) \(lastName)"
    }
}

var person = Person(firstName: "Jason", lastName: "Lee")
// property 사용
person.fullName
// method 사용
person.fullName_f()

이런 간단한 값을 반환하는 경우는 property가 더 좋은 방법이 되겠다!

" structure - method "

method : 특정 연산을 수행하는 역할 (property 값들을 통해 연산)

mutating method : stored property 값을 변경하는 메소드

static func : struct 타입에 관한 메소드 (property 값들과 무관)

struct Lecture {
    //stored property
    var title: String
    var maxStudents: Int = 10
    var numOfRegistered: Int = 0
    
    //method
    func remainSeats() -> Int {
        let remainSeats = maxStudents - numOfRegistered
        return remainSeats
    }
    
    //struct 내에서 변수를 수정하는 메소드의 경우 mutating 키워드 필요
    mutating func register() {
        //등록된 학생수 증가시키기
        numOfRegistered += 1
    }
    
    //type property
    static let target: String = "Anybody want to learn something"
    
    //type method
    static func 소속학원이름() -> String {
        return "패캠"
    }
}

//method 사용
var lec = Lecture(title: "iOS Basic")
lec.remainSeats()
lec.register()

//type property, method 사용
Lecture.target
Lecture.소속학원이름()

" structure - extention "

기존에 만들어진 struct에 추가적인 property, 또는 method가 필요할 시에 사용된다

/* extension을 활용하여 만들어진 struct에 메소드를 추가할 수 있다 */
struct Math {
    static func abs(value: Int) -> Int {
        if value > 0 {
            return value
        } else {
            return -value
        }
    }
}
Math.abs(value: -20)

위의 Math struct에 추가기능을 더할때 extention을 사용하면 된다

//만들어져있는 Math struct 에 새로운 함수 추가하기
extension Math {
    static func square(value: Int) -> Int {
        return value * value
    }
    
    static func half(value: Int) -> Int {
        return value / 2
    }
}
Math.square(value: 5)
Math.half(value: 20)

※ 애플이 기존에 만들어 놓은 struct의 경우도 역시 extension으로 기능을 추가할 수 있다 ※

//애플이 만들어 놓은 struct에 추가해서 사용할수도 있다
extension Int {
    func square() -> Int {
        return self * self
    }
    
    func half() -> Int {
        return self / 2
    }
}
var value: Int = 5
value.square()
value.half()