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 |