본문 바로가기

ComputerScience/ios App(Storyboard)

ios - 10 swift 기본문법(Structure)

728x90

1 Structure

- 여러 타입의 변수를 묶어서 하나의 타입으로 동작하는 자료구조를 만들 수 있다.

- 데이터 + 메서드로 구성되어있고 이를 하나의 object라고 한다.

- value타입으로 전달 시 복사되어 값이 전달 된다.  (pass by value)

- structure생성시 stack에 할당된다.

2 Class와의 차이

- class도 structure처럼 여러 변수와 함수를 묶어서 표현하는 도구이다.

- 단 structure와 달리 reference type으로 생성시 heap에 할당된다.

- 두 타입의 차이를 아래 코드에서 살펴보자.

class PersonClass {
    var name: String
    var age: Int

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


struct PersonStruct {
    var name: String
    var age: Int

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

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

pClass1.name
pClass2.name


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

pStruct1.name
pStruct2.name

- struct는 값이 복사되기 때문에 나중에 pStruct2를 변경시켜도 pStruct1에는 변화가 없다.

- 하지만 pClass1과 pClass2는 같은 객체를 가리키고 있기 때문에 하나를 변경 시키면 둘다 바뀌는 것이다. 두 변수의 값은 {Jason, 5} 객체의 주소값이라고 생각하자.

3. struct 활용

- struct를 활용해서 내 위치에서 가장 가까운 편의점을 찾는 프로그램을 만들어보자.

import UIKit

// Given distance func
func distance(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
}


struct Location {
    let x: Int
    let y: Int
}

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

// Given stores
let store1 = Store(loc: Location(x: 3, y: 5), name: "gs")
let store2 = Store(loc: Location(x: 4, y: 6), name: "seven")
let store3 = Store(loc: Location(x: 1, y: 7), name: "cu")


// Given printClosestStore func
func printClosestStore(currentLocation: Location, stores: [Store]) {
    var closestStoreName = ""
    var closestStoreDistance = Double.infinity
    var isDeliverable = false
    
    for store in stores {
        let distanceToStore = distance(current: currentLocation, target: store.loc)
        closestStoreDistance = min(distanceToStore, closestStoreDistance)
        if closestStoreDistance == distanceToStore {
            closestStoreName = store.name
            isDeliverable = store.isDeliverable(userLoc: currentLocation)
        }
    }
    print("Closest store: \(closestStoreName) deliverable: \(isDeliverable)")
}

// Set stores and myLocation
let stores = [store1, store2, store3]
let myLocation = Location(x: 2, y: 5)


// Use printClosestStore func to print
printClosestStore(currentLocation: myLocation, stores: stores)

4 Protocol

- java의 interface와 유사하다. 바로 코드로 살펴보자.

import UIKit

// 도전 과제
// 1. 강의 이름, 강사 이름, 학생수를 가지는 Struct 만들기 (Lecture)
// 2. 강의 어레이이와 강사이름을 받아서 , 해당 강사의 강의 이름을 출력하는 함수 만들기
// 3. 강의 3개 만들고 강사이름으로 강의 찾기


// CustomStringConvertible

struct Lecture: CustomStringConvertible {
    var description: String {
        return "Title: \(name), Instructor: \(instructor)"
    }
    
    let name: String
    let instructor: String
    let numOfStudent: Int
}

func printLectureName(from instructor: String, lectures: [Lecture]) {
    var lectureName = ""

    for lecture in lectures {
        if instructor == lecture.instructor {
            lectureName = lecture.name
        }
    }
    
    // let lectureName = lectures.first { $0.instructor == instructor }?.name ?? ""
    print("아 그 강사님 강의는요: \(lectureName)")
}


let lec1 = Lecture(name: "iOS Basic", instructor: "Jason", numOfStudent: 5)
let lec2 = Lecture(name: "iOS Advanced", instructor: "Jack", numOfStudent: 5)
let lec3 = Lecture(name: "iOS Pro", instructor: "Jim", numOfStudent: 5)
let lectures = [lec1, lec2, lec3]

printLectureName(from: "Jack", lectures: lectures)

print(lec1)

- struct Lecture: CustomStringConvertible에서 CustomStringConvertible이 protocol에 해당한다.

- Lecture라는 struct를 사용하기 위해서는 반드시 CustomStringConvertible를 만족하도록 강제하는 것이다.

- CustomStringConvertible의 조건을 만족시키기 위해서 struct 안에 var description: String를 정의해 주어야 한다.

- description을 정의하고 print(lec1)을 해보면 직접 정의한대로 객체를 설명해준다.

5 Property

- struct 혹은 class가 소유하고 있는 멤버 변수를 stored property라고 한다.

struct Lecture: CustomStringConvertible {
    var description: String {
        return "Title: \(name), Instructor: \(instructor)"
    }
    
    let name: String
    let instructor: String
    let numOfStudent: Int
}

- 여기서 name, instructor, numOfStrudent가 stored property라고 한다.

- 정보를 저장하지는 않지만 접근될 때마다 정보를 가공해서 제공하는, 예시의  description같은 변수를 computed property라고 한다.

- 두 property를 활용해보자

struct Person {
    var firstName: String
    var lastName: String
    
    var fullName: String {
        get {
            return "\(firstName) \(lastName)"
        }
    }
}

var person = Person(firstName: "Jsd", lastName: "Ysw")

person.firstName = "Yoon"
person.lastName = "sw"
person.fullName

- 위 예제에서는 computed property의 값을 가져오는 것만 가능하다. 이번에는 fullName을 set할 수 있게 해보자

import UIKit

struct Person {
    var firstName: String
    var lastName: String
    
    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
            }
        }
    }
}

var person = Person(firstName: "Jsd", lastName: "Ysw")
person.fullName = "Jsd Ysw"

- 지금까지는 person이라는 객체의 인스턴스를 만들어서 프로퍼티에 값을 채웠다

- 이번에는 인스턴스를 생성하지 않고 객체의 property에 접근할 수 있는 type property를 알아보자.

- c++이나 Java에서 클래스 내에 static 멤버변수를 추가하는 것과 비슷하다.

import UIKit

struct Person {
    var firstName: String
    var lastName: String
    
    static let isAlien: Bool = false
}

Person.isAlien

6 Observation (didset, willset)

- property가 변할 때 감지할 수 있는 방법이다.

- didset을 이용하면 property의 값이 변경되었을 때 실행되는 동작을 정의할 수 있다.

- firstName이 바뀔때마다 변경내역을 출력해보자.

- willset은 값이 변경되기 전에 실행되는 블럭이다.

- "기존의 값에서 새로운 값으로 변경할 것이다"라는 문구를 출력해보자

import UIKit

struct Person {
    var firstName: String {
        willSet {
            print("willSet: \(firstName) ---> \(newValue)")
        }
        didSet {
            print("didSet: \(oldValue) ---> \(firstName)")
        }
    }
    var lastName: String
    
    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
            }
        }
    }
}

var person = Person(firstName: "Jsd", lastName: "Ysw")

person.firstName = "Yoon"
person.lastName = "sw"

7 Lazy Property

- property에 접근할 때 실행되는 동작을 정의할 수 있다.

- lazy property는 cost가 상대적으로 많이 드는 property의 경우 혹은 모든 Person instance가 접근하는 porperty가 아니고 간혹 사용되는 경우라면 사용자가 property에 접근할 때 동작하도록해서 활용 가능하다.

import UIKit

struct Person {
    lazy var isPopular: Bool = {
        if fullName == "Famous star" {
            return true
        } else {
            return false
        }
    }()
}

8 Property vs Method

struct Person {
    var firstName: String
    var lastName: String
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
    func fullName2() -> String {
        return "\(firstName) \(lastName)"
    }
}

var person = Person(firstName: "Jsd", lastName: "Ysw")

person.fullName
person.fullName2()

- 위의 코드처럼 computed property(fullName)과 method(fullName2)는 서로 같은 결과를 보장한다.

- 그럼 어떤게 더 좋은 방법일까?

- 개인적인 선호가 다를테지만 setter를 사용하는 경우는 computed property를 사용한다. setter를 사용하지 않더라도 위의 예시처럼 단순히 값을 리턴하는 가벼운 동작을 수행한다면 computed property로도 충분할 것이다.

- 반대로 setter를 사용하지 않는데 여러 연산이 필요한 경우는 method가 적절하다. 

9 Method

- function이 instance에 귀속된 경우 method라고 한다.

struct Lecture {
    var title: String
    var maxStudents: Int = 10
    var numOfRegistered: Int = 0
    
    func remainSeats() -> Int {
        let remainSeats = maxStudents - numOfRegistered
        return remainSeats
    }
    
    mutating func register() {
        // 등록된 학생수 증가시키기
        numOfRegistered += 1
    }
    
    static let target: String = "Anybody want to learn something"
    
    static func 소속학원이름() -> String {
        return "00학원"
    }
    
}

var lec = Lecture(title: "iOS Basic")

lec.remainSeats()

lec.register()
lec.register()

lec.remainSeats()

Lecture.target
Lecture.소속학원이름()

- mutating은 함수가 property를 변경하고자 하는 경우에 붙여야하는 키워드이다.

- static으로 type property를 만들었던 것 처럼 type method도 만들 수 있다. 이 메서드는 인스턴스를 생성하지 않고도 Lecture.으로 접근 가능하다. 

10 Extension

- 기존의 구조체에 기능을 확장하고 싶을때 사용한다. 

- 기존 구조체의 파일이 숨겨져 있거나 접근할 수 없을때 extension키워드로 손쉽게 확장이 가능하다.

struct Math {
    static func abs(value: Int) -> Int {
        if value > 0  {
            return value
        } else {
            return -value
        }
    }
}

Math.abs(value: -20)


// 제곱, 반값
extension Math {
    static func sqaure(value: Int) -> Int {
        return value * value
    }
    
    static func half(value: Int) -> Int {
        return value/2
    }
}

Math.sqaure(value: 5)
Math.half(value: 20)

- 이번에는 이미 존재하는 Int struct를 확장 해보자

var value: Int = 10

extension Int {
    func square() -> Int {
        return self * self
    }
    
    func half() -> Int {
        return self/2
    }
}
// 제곱, 반값
value.square()
value.half()
728x90
반응형