iOS 개발자/iOS Stanford Univ

[iOS 스탠포드] Chapter3

FDEE 2021. 7. 7. 17:12

Chapter 3

✔︎ 수정사항

- Computed Progerties : 굳이 메모리에 저장되지 않아도 되는 변수 -> get{}, set{} 메소드를 통해 수정 : 필요시에 해당 메소드가 실행된 값을 반환, 설정하는 구조

//before : 저장된 변수값만 의미를 지닌 상태, 어떻게 사용, 저장하느냐에 따라 에러가 발생할 가능성이 있는 상태
var indexOfOneAndOnlyFaceUpCard: Int?

//after : 사용, 저장시에 일종의 규칙을 명시하여 에러상황을 방지한 코드
var indexOfOneAndOnlyFaceUpCard: Int? {
    get {
        var foundIndex: Int?
        for index in cards.indices {
            if cards[index].isFaceUp {
                if foundIndex == nil {
                    foundIndex = index
                } else {
                    return nil
                }
            }
        }
        return foundIndex
    }
    set {
        for index in cards.indices {
            cards[index].isFaceUp = (index == newValue)
        }
    }
}

- Access Control : 기존 변수에 private, private(set)을 설정하여 보안성 강화 (open의 경우가 아닌경우 private로 기본설정을 권장)

//private : 해당 class, 또는 struct 내에서만 사용되는 변수
private var emojiChoices: [String] = ["👻", "🎃", "😈", "👽", "💀", "🤢", "🤡", "💩"]

//private(set) : 해당 class, 또는 struct 내에서만 값을 설정할 수 있는 변수
private(set) var cards: [CP3_Card] = []

- extention 을 사용하여 더러운? 코드 -> 새롭게 만든 함수를 사용하는 식으로 수정, 저장되지는 않는 구조

//before : random 인덱스값을 사용하기 위해 더러운? 한번의 작업이 필요
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)

//after : Int에 extention을 사용하여 해당메소드를 구현하여 간편하게 사용
emoji[card.identifier] = emojiChoices.remove(at: emojiChoices.count.arc4random)

extension Int {
    var arc4random: Int {
        if self > 0 {
            return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            return 0
        }
    }
}

- assert 함수를 통해 비정상적인 인자가 들어오는 경우를 체크

func chooseCard(at index: Int) {
    assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")
    //생략
}

 

✔︎ 자료구조

- stride 함수 : double값의 for문, 또는 증감폭을 임의로 지정할 경우 사용된다

//through : 해당값 포함(닫힌범위), to : 해당값 미포함(열린범위)
for i in stride(from: 0.5, through: 15.25, by: 0.3) {
    //0.5부터 15.25까지 0.3 간격으로 증가한다
}

- tupple : 값만 지정되는 간단한 구조체 형태, 메소드가 두가지값 이상 반환시에 tupple 값으로 반환시에 자주 사용된다

let x1: (String, Int, Double) = ("hello", 5, 0.85)
print(x1.0) //hello

let x2: (w: String, i: Int, v: Double) = ("hello", 5, 0.85)
print(x2.w) //hello

let (word, number, value) = x1
print(word) //hello

- Access Control

  ▪︎ internal : defalut 값으로, 해당 project 내의 모든 곳에서 사용 가능한 범위

  ▪︎ private : 해당 object(class, struct) 내에서만 사용 가능한 범위

  ▪︎ private(set) : 변수만 설정 가능하며, 해당 object(class, struct) 내에서만 설정 가능, 사용은 광범위한 범위

  ▪︎ fileprivate : 해당 source file 내의 모든 곳에서 사용가능한 범위

  ▪︎ public : 해당 framework 밖에서도 사용 가능한 범위

  ▪︎ open : 해당 framework 밖에서도 사용 가능하며 subclass 에서도 사용가능한 범위, override 또한 가능

 

- extension : 해당 object의 확장기능으로, 변수 및 메소드를 수정, 추가가 가능 (저장이 되지는 않는다)

 

- enum : 값 타입으로, 제한된 선택지의 경우 사용되며, 인자를 받아 데이터를 지닐 수 있고, switch 구문과 자주 사용된다

             : 메소드, 또는 calculated 타입의 변수를 반환할 수 있다 (not stored property)

enum FastFoodMenuItem {
    case hamburger(numberOfPatties: Int)
    case fries(size: FryOrderSize)
    case drink(String, ounces: Int)
    case cookie
}

enum FryOrderSize {
    case large
    case small
}

let monuItem: FastFoodMenuItem = FastFoodMenuItem.hamburger(patties: 2)
let otherItem: FastFoodMenuItem = .cookie //.을 통해 앞의 코드들 생략 가능
let menuItem2: FastFoodMenuItem = .fries(size: .large) //.을 통해 다른 enum의 케이스 사용가능

//switch 구문
switch menuItem {
    case .hamburger:
        print("burger")
        break
    case .fries(let size): //let 변수명 지정, 변수명은 원하는값으로 설정 가능
        print("a \(size) order of fries!") //let 변수명을 통해 데이터 사용 가능
    default:
        print("other")
}

//enum 내부 switch 구문
enum FastFoodMenuItem {
    //생략
    func isIncludeInSpecialOrder(number: Int) -> Bool {
        switch self {
            case .hamburger(let pattyCount):
                return pattyCount == number
            case .fries, .cookie: //두가지 이상의 case를 ,로 묶을 수 있다
                return true
            case .drink(_, let ounces): //사용하지 않는 데이터의 경우 _ 사용
                return ounces == 16
        }
    }
}

//enum 값 수정을 위한 메소드 : mutating 필요
enum FastFoodMenuItem {
    //생략
    mutating func switchToBeingACookie() {
        self = .cookie //값 타입의 수정이 필요할 경우 mutating을 통해 수정한다
    }
}

- optional : 내부가 일종의 enum 형식으로 값, 또는 nil을 반환하는 형태이다

  ▪︎ ? : optinal 선언

 let data: String? //optional 변수 선언

  ▪︎ ! : optional 변수의 unwrap

print(data!) //optional 변수의 unwrap

  ▪︎ ?? : optional 변수가 nil인 경우 default 값으로 설정

let data2: String = emoji[card.identifier] ?? "?"

 

- optional chaining : 두가지 이상의 optional 변수간의 관계를 표현

  let y = x?.foo()?.bar?.z //x값 존재시 -> foo()값 존재시 ->bar값 존재시 z값으로 접근

 

class

- 참조 타입

- 단일 상속

- 기능, 데이터

- heap에 저장된다 (삭제되는 경우는 ARC에 의해 자동으로 삭제된다)

※ Influencing ARC : ARC에 영향을 줄 수 있는 세가지 키워드 ※

- strong : default 값으로, heap에 계속 유지가 되는 구조 (해당 값에대한 포인터가 살아있는 한 계속 유지)

- weak : optional pointer 값으로, 해당 값이 필요없는 경우 nil 값이 된다

- unowned : 메모리 사이클 현상을 피하기 위해 참조하지 않을 경우 사용 (메모리 사이클은 heap 내부에서 서로간의 참조 형태로, 클로저를 통해 구현된다)

 

struct

- 값 타입 (복제되는 형식)

- 값이 수정되는 메소드의 경우 mutating 키워드가 필요 (수정 = 복제 + 입력)

 

enum

- 값 타입 (복제되는 형식)

- 기능 상속 : protocol 을 통해 받는다

- 값이 수정되는 메소드의 경우 mutating 키워드가 필요 (수정 = 복제 + 입력)

 

protocal

- 다음시간에 계속

 

✔︎ 새롭게 알게된 기능들

- Computed Progerties를 통해 굳이 저장될 필요 없이 계산된 값만 필요할 경우 사용된다는 점!

var count: Int {
    get() {
        return array.count
    }
    set() {
        self = newValue
    }
}

- assert 함수를 통해 인자를 체크할 수 있다는 점!

assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")

- enum에 데이터를 넣고, switch 구문에서 let을 통해 접근할 수 있다는 점!

enum FastFoodMenuItem {
    case hamburger(numberOfPatties: Int) //데이터 추가!
    case fries(size: FryOrderSize)
    case drink(String, ounces: Int)
    case cookie
}

//switch 구문
switch menuItem {
    case .hamburger:
        print("burger")
        break
    case .fries(let size):
        print("a \(size) order of fries!") //let 변수명을 통해 데이터 사용 가능
    default:
        print("other")
}

- private(set) 을 통해 내부에서만 수정 가능한 변수 설정이 가능하다는 점!

private(set) var cards: [CP3_Card] = []

- optional chaining 을 통해 여려 optional 변수간 접근이 가능하다는 점!

let y = x?.foo()?.bar?.z

 

 

✔︎ 전체 코드

- ViewController

//
//  ViewController.swift
//  Study_Stanford
//
//  Created by Kang Minsang on 2021/06/18.
//

import UIKit

class CP3_ViewController: UIViewController {
    
    private lazy var game = CP3_Concentration(numberOfPairsOfCards: numberOfPairsOfCards) //사용될 때 초기화가 진행, didSet은 사용 불가
    
    var numberOfPairsOfCards: Int {
        return (cardButtons.count+1)/2 //읽기 전용 변수
    }
    private(set) var flipCount = 0 {
        didSet { flipCountLabel.text = "Flips: \(flipCount)" }
    }
    
    private var emojiChoices: [String] = ["👻", "🎃", "😈", "👽", "💀", "🤢", "🤡", "💩"]
    
    @IBOutlet private var cardButtons: [UIButton]!
    
    @IBOutlet private var flipCountLabel: UILabel!
    
    @IBAction private func touchCard(_ sender: UIButton) {
        flipCount += 1
        if let cardNumber = cardButtons.firstIndex(of: sender) {
            game.chooseCard(at: cardNumber)
            updateViewFromModel()
        } else {
            print("chosen card was not in cardButtons")
        }
    }
    
    private func updateViewFromModel() {
        for index in cardButtons.indices { //배열 -> 셀수있는 구간
            let button = cardButtons[index]
            let card = game.cards[index]
            
            if card.isFaceUp {
                button.setTitle(emoji(for: card), for: .normal)
                button.backgroundColor = UIColor.white
            } else {
                button.setTitle("", for: .normal)
                button.backgroundColor = card.isMatched ? UIColor.clear : UIColor.orange
            }
        }
    }
    
    private var emoji : [Int:String] = [:]
    private func emoji(for card: CP3_Card) -> String {
        if(emoji[card.identifier] == nil), emojiChoices.count > 0 {
            emoji[card.identifier] = emojiChoices.remove(at: emojiChoices.count.arc4random)
        }
        
        return emoji[card.identifier] ?? "?"
    }
    
}

extension Int {
    var arc4random: Int {
        if self > 0 {
            return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            return 0
        }
    }
}

- Concentration

//
//  CP2_Concentration.swift
//  Study_Stanford
//
//  Created by Kang Minsang on 2021/06/22.
//

import Foundation

class CP3_Concentration
{
    private(set) var cards: [CP3_Card] = []
    
    private var indexOfOneAndOnlyFaceUpCard: Int? {
        get {
            var foundIndex: Int?
            for index in cards.indices {
                if cards[index].isFaceUp {
                    if foundIndex == nil {
                        foundIndex = index
                    } else {
                        return nil
                    }
                }
            }
            return foundIndex
        }
        set {
            for index in cards.indices {
                cards[index].isFaceUp = (index == newValue)
            }
        }
    }
    
    func chooseCard(at index: Int) {
        assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")
        if !cards[index].isMatched {
            if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
                // check if cards match
                if cards[matchIndex].identifier == cards[index].identifier {
                    cards[matchIndex].isMatched = true
                    cards[index].isMatched = true
                }
                cards[index].isFaceUp = true
            } else {
                indexOfOneAndOnlyFaceUpCard = index
            }
            
        }
    }
    
    init(numberOfPairsOfCards: Int) {
        assert(numberOfPairsOfCards > 0, "Concentration.init(\(numberOfPairsOfCards)): you must have at least one pair of cards")
        for _ in 1...numberOfPairsOfCards {
            let card = CP3_Card()
            cards += [card, card]
        }
        // TODO: Shuffle the cards
    }
}

//새로운 게임 시작 구현

- Card

//
//  CP2_Card.swift
//  Study_Stanford
//
//  Created by Kang Minsang on 2021/06/22.
//

import Foundation

struct CP3_Card
{
    var isFaceUp = false
    var isMatched = false
    var identifier: Int
    
    private static var identifierFactory = 0
    
    private static func getUniqueIdentifier() -> Int {
        identifierFactory += 1
        return identifierFactory
    }
    
    init() {
        self.identifier = CP3_Card.getUniqueIdentifier()
    }
}


// 구조체는 상속성이 없다 (더 간단하다)
// 구조체는 값 타입이고, 클래스는 참조 타입이다
// 값 타입 : 복사된다

//static func : 타입에 관한 정적 메소드

 

'iOS 개발자 > iOS Stanford Univ' 카테고리의 다른 글

[iOS 스탠포드] Assignment 1 : Concentration  (0) 2021.07.05
[iOS 스탠포드] Chapter2  (0) 2021.06.22
[iOS 스탠포드] Chapter1  (0) 2021.06.18