본문 바로가기

# 02/Swift

[Swift] 기초 입문 Study

반응형
  • 몇가지 스위프트 언어의 특징 (공식 소개 링크)
    • Modern
      • 최근 개발언어에 대해 연구해서 좋은것만 넣어서 업데이트 하고 있음
      • 코드를 깔끔하게 작성할수 있게 설계함
      • 개발자들이 실수 덜 할수 있게 고도화를 지속적으로 하고 있음
      • 메모리 관리도 자동으로 됨
    • Safety
      • 스위프트는 안전하지 않은 형태의 클래스를 제거함
      • 문법과 시스템을 통해서, 발생할수 있는 위험성을 제거함
    • Fast
      • 스위프트는 최초부터 빠르게 설계되었음
      • C, Objective-C 등과 호환도 되지만, 기본적으로 더 빠른 속도로 기존 언어들을 대체하려고 나옴
func printNames(_ names: String...) {
    for name in names {
        print("name is \\(name)")
    }
}

printNames("James", "Roy", "Jake")
// name is James
// name is Roy
// name is Jake
enum DivideError: Error {
    case cannotZero
}

func divide(dividend: Int, divisor: Int) throws -> Int {
    if divisor == 0 {
        throw DivideError.cannotZero
    }
    
    return Int(dividend / divisor)
}

do {
    let result = try divide(dividend: 80, divisor: 6)
    // let result = try divide(dividend: 80, divisor: 0)
    print(result)
} catch {
    print(error.localizedDescription)
}
func makeTriple(num: inout Int) {
    num *= 3
}

var num = 8
makeTriple(num: &num)

print(num) // 24

 

  • 클로져도 사실 함수처럼 “어떤 태스크를 수행하기 위한 코드 블록” 임

💡 struct vs. class 차이점과 공통점

 

✅ 공통점

  • 프로퍼티(속성)를 정의해서 값을 저장할수 있음
  • 메소드(행동)를 정의해서 기능을 제공할수 있음
  • 생성자를 정의해서 초기상태를 세팅할수 있음
  • 확장을 이용해서, 기본 구현외 추가 기능을 더할수 있음
  • 프로토콜을 구현해서 특정 기능을 제공할수 있음

✅ 차이점

  • 클래스는 상속을 시킬수 있음
  • 클래스는 레퍼런스 카운팅을 통해 클래스 인스턴스에 대한 하나 이상의 참조를 허용
  • 클래스는 deinitializer 호출시, 이미 할당된 리소스에서 해제 할수 있음

Struct

struct Movie {
		// stored property
    var name: String
    var director: String

		// computed property    
    var description: String {
        return "\\(name) is \\(director)'s best movie ever"
    }
}

var movie = Movie(name: "인셉션", director: "놀란")
print(movie.description)
struct Task {
    var name: String
    var progress: Int {
        didSet {
            print("Current Progress : \\(progress) %")
        }
    }
    
    
    var isDone: Bool {
        return progress == 100
    }
}

var task = Task(name: "Very Important Task", progress: 0)

task.progress = 30 // Current Progress : 30 %
task.progress = 50 // Current Progress : 50 %
task.progress = 90 // Current Progress : 90 %

method

  • 객체는 행동(기능)을 가지고 있고, 그것을 우리는 method 라고 부름
    • 객체가 가지고 있는 함수라고 생각하면 됨
  • struct 에서 method 가 stored property 값을 변경하는 경우, mutating 키워드를 메소드 앞에 붙여주어야함
struct Student {
    let name: String
    var major: String
    var knowledge: Double
    
    func didFinalTest() -> Int {
        let howMuchIdontKnow = (1 - knowledge) * 100
        let score = 100 - Int(howMuchIdontKnow)
        return score
    }
    
    mutating func didStudy() {
        if knowledge >= 1 {
            knowledge = 1
        } else {
            knowledge += 0.1
        }
    }
}

initializer (생성자)

  • 해당 struct 를 만들수 있게 해주는 특별한 타입의 메소드
    • init 키워드를 이용해서 생성함
  • struct 는 meberwise initializer 가 기본으로 따라옴
// -------------------------------------------------//
// initializer(생성자)
struct iPhone {
    var model: String

    init() {
        model = "iPhone 13"
    }
}

let iPhone13 = iPhone()
// init() 정의 안해줬으면 에러 안났을 텐데 생성자를 정의해줘서 해당 값 에러남.
//let iPhone14 = iPhone(model: "iPhone 14") 

// -------------------------------------------------//
// 현재 객체 지칭 - self
struct iPhone2 {
    var model: String

    init(model: String = "iPhone 13") {
        self.model = model
    }
}

let iPhone2_13 = iPhone2()
let iPhone2_14 = iPhone2(model: "iPhone 14")

// -------------------------------------------------//
// Lazy properties
struct Transactions {
    init() {
        print("Loading self history.. ")
    }
}

struct SecondHandItem {
    var name: String
//    var history: Transactions = Transactions()
    lazy var history: Transactions = Transactions()
    
    init(name: String) {
        self.name = name
    }
}

Access Control

  • 접근 제어 - internal defau
    • 외부 접근 가능 > public > internal > fileprivate > private > 내부 접근 가능
// -------------------------------------------------//
// Access Control
//- 외부 접근 가능 >  `public`  > `internal` (default) > `fileprivate`  > `private` > 내부 접근 가능
struct UserAccount {
    private var id: String
    private var bill: Int
    var name: String
    
    init(id: String, bill: Int, name: String) {
        self.id = id
        self.bill = bill
        self.name = name
    }
    
    func billDescription() -> String {
        return "\\(id)'s billing amount : \\(bill) name : \\(name)"
    }
}

var user01 = UserAccount(id: "1234", bill: 400, name: "Mike")

//user01.bill = 100 - 에러남 접근 안됨.
//user01.id = "1123" - 에러남 접근 안됨.
user01.name = "joy" // 접근 가능.

let billDescription01 = user01.billDescription()

Class

  • class 는 struct 와 다르게 memberwise initializer 제공되지 않음
class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) { // 안해주면 에러남!!!
        self.name = name
        self.breed = breed
    }
}
  • class 와 struct 차이중 하나는 class 는 상속을 제공함
class Collie: Dog {
    init(name: String) { // 이거 안해주면 부모클래스 생성자 따라감.
        super.init(name: name, breed: "Collie")
    }
}

let milky = Collie(name: "milky")
milky.name // milkiy
milky.breed // Collie

copying object

  • struct 와 class 의 차이중 하나가 instance 복사할때임
    • struct 와 같은 value type 은 복사하면, 새로운 객체를 만듬
      • → 따라서, 새로 복사된 객체의 프로퍼티 변형했다고 원래 객체가 영향 받지 않음
    • class 와 같은 reference type 은 복사하면, 새로운 객체를 만들지 않음 (같은 객체를 포인팅하고 있음)
      • → 따라서, 새로 복사된 객체의 프로퍼티 변형하면, 원래 객체의 프로퍼티 영향 받아서 변형 되어 있음
class StudentClass {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

struct StudentStruct {
    var name: String
}

var studentClass1 = StudentClass(name: "Jacob")
var studentClass2 = studentClass1

studentClass2.name = "Jay"
studentClass1.name // Jay
studentClass2.name // Jay

var studentStruct1 = StudentStruct(name: "Jacob")
var studentStruct2 = studentStruct1

studentStruct2.name = "Jay"
studentStruct1.name // Jacob
studentStruct2.name // Jay

deinitializer

  • struct 와 class 의 차이중 하나가 class 는 deinitializer 가 있음
  • 해당 인스턴스가 메모리에서 해제될때(없어질때) 호출됨
class Human {
    var name: String
    
    init(name: String) {
        self.name = name
        print("initialize instance")
    }
    
    deinit {
        print("deintilize instance: \\(name)")
    }
    
    func printName() {
        print("my name: \\(self.name)")
    }
}

func createJohn() {
    let john = Human(name: "John")
    john.printName()
}

createJohn() // 함수가 끝나면 함수내에 변수가 메모리 해제되어 deintilize 호출됨.
// initialize instance
// my name: John
// deintilize instance: John

let john = Human(name: "John")
john.printName()
// initialize instance
// my name: John

mutability

  • class 는 struct 와 다르게 reference type 이여서 상수로 선언하더라도, 프로퍼티가 var 로 선언해 놓았으면 변경 가능함
    • 이 때문에, class 는 method 앞에 mutating 키워드를 쓸 필요 없음
    • 프로퍼티를 변경가능하지 않게 하려면, let 으로 선언해 놓으면 됨
class Human {
    var name: String
    
    init(name: String) {
        self.name = name
        print("initialize instance")
    }
    
    deinit {
        print("deintilize instance: \\(name)")
    }
    
    func printName() {
        print("my name: \\(self.name)")
    }
    
    func updateName(to name: String) {
        self.name = name
    }

}

let sean = Human(name: "Sean")
sean.name // Sean
sean.name = "Joy" // sean이 let 이지만 name 이 var 이기 때문에 변경 가능.
sean.name // Joy
sean.updateName(to: "Son")
sean.name // Son

 

 

https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes

 

Choosing Between Structures and Classes | Apple Developer Documentation

Decide how to store data and model behavior.

developer.apple.com

 

Struct vs Class

  • Use structures by default. :
    • 기본적으로 잘 모르겠으면 struct를 쓴다.
  • Use classes when you need Objective-C interoperability.
    • Objective-C 상호 운용성이 필요할 때 class 를 쓴다.
  • Use classes when you need to control the identity of the data you’re modeling.
    • 모델링 중인 데이터의 identity를 제어해야 하는 경우 class 를 쓴다.
  • Use structures along with protocols to adopt behavior by sharing implementations.
    • 프로토콜을 채택할 때 struct를 쓴다.

Protocol

  • protocol 은 어떤 약속(역할)을 정해 놓은 것 어떤 약속(역할)을 정해 놓은 것이지 아직 구현된것은 아님

한가지 객체에 대해서만 buy() 메소드를 정의할 때

struct Book {
    var name: String
}

func buy(_ book: Book) {
    print("I'm buying \\(book.name)")
}

let harrypotter = Book(name: "Harry Potter")
buy(harrypotter)

**Purchaseable** 역할에 buy() 메소드를 정의할때

protocol Purchaseable {
    var name: String { get set }
}

func buy(_ item: Purchaseable) {
    print("I'm buying \\(item.name)")
}

struct Book: Purchaseable {
    var name: String
    var author: String
}

struct Movie: Purchaseable {
    var name: String
    var actors: [String]
}

struct Car: Purchaseable {
    var name: String
    var manufacturer: String
}

struct Coffee: Purchaseable {
    var name: String
    var strength: Int
}

let harryPotter = Book(name: "HarryPotter", author: "J.K. Rowling")

let topgun = Movie(name: "Top Gun", actors: ["Tom cruise"])

let modelX = Car(name: "modelX", manufacturer: "Tesla")

let americano = Coffee(name: "ahah", strength: 5)

buy(harryPotter)
buy(topgun)
buy(modelX)
buy(americano)

Protocol 상속

  • protocol 은 상속 받을수 있음
  • 클래스와 다르게 여러개 상속이 가능함
    • 여러개 상속이 되다 보니, protocol 을 더 작고 명확하게 나눌수 있음
    • 재사용성도 늘고 테스트 가능성도 더 높아짐
protocol Payable {
    func calculateWages() -> Int
}

protocol Trainable {
    func train()
}

protocol HasVacation {
    func takeVacation(days: Int)
}

protocol Employee: Payable, Trainable, HasVacation { }

struct DeveloperEmployee: Employee {
    var name: String
    
    func calculateWages() -> Int {
        return 10_000_000
    }
    
    func train() {
        print("study hard")
    }
    
    func takeVacation(days: Int) {
        print("take \\(days) days off")
    }
}

let choi = DeveloperEmployee(name: "Choi")

choi.calculateWages()
choi.takeVacation(days: 3)
choi.train()

Protocol Extension

  • protocol 은 어떤 역할에 대한 정의를 설명해줌
    • 다만, 실제 구현은 제공하지 않음
  • extension 은 기능에 대한 구현까지 제공함
    • 다만, 한가지 타입에만 적용됨
  • protocol extension 은 위에 있는 두가지 단점을 보완해 줄수 있음
    • protocol 에 기본 구현을 제공해줄수 있음
    • protocol 을 채택하는 여러 타입에 기능을 제공해줄수 있음
extension Collection {
    func summarize() {
        print("There are \\(count) members")
    }
}

let stringArray = ["aa", "bb", "cc"]
let numSet = Set([1, 2, 3, 4, 5])

stringArray.summarize()
// There are 3 members

numSet.summarize()
// There are 5 members

Protocol Oriented Programming

  • protocol 은 어떤 역할에 대한 정의를 제공
  • extension 은 어떤 타입에 구현을 제공함
  • protocol extension 은 어떤 역할에 대한 기본 구현 제공
  • 위 3가지 덕분에 POP(protocol oriented programming)이 가능함
protocol Payable {
    func calculateWages() -> Int
}

protocol Trainable {
    func train()
}

protocol HasVacation {
    func takeVacation(days: Int)
}

extension Payable {
    func calculateWages() -> Int {
        return 10_000_000
    }
}

extension Trainable {
    func train() {
        print("study hard")
    }
}

extension HasVacation {
    func takeVacation(days: Int) {
        print("take \\(days) days off")
    }
}

protocol Employee: Payable, Trainable, HasVacation { }

struct DeveloperEmployee: Employee {
    var name: String
}

let choi = DeveloperEmployee(name: "Jason")

choi.calculateWages()
choi.takeVacation(days: 3)
choi.train()

struct DesignerEmployee: Employee {
    var name: String
}

let jane = DesignerEmployee(name: "jane")
jane.calculateWages()
jane.takeVacation(days: 5)
jane.train()

Typecasting

  • swift 에서는 부모 타입에서 자식 타입으로 변형가능한지 확인해볼때 as? 키워드를 사용함

 

 

 


https://github.com/jiyeonjoy/SwiftStudy

 

GitHub - jiyeonjoy/SwiftStudy

Contribute to jiyeonjoy/SwiftStudy development by creating an account on GitHub.

github.com

 

반응형

'# 02 > Swift' 카테고리의 다른 글

[Swift] RxSwift  (0) 2023.01.11
[Swift] DispatchQueue / Serial / Concurrent  (0) 2023.01.11
[Swift] DFS/BFS  (0) 2022.12.18
[Swift] MaxHeap  (0) 2022.12.12
[Swift] MinHeap  (0) 2022.12.12