ํฐ์คํ ๋ฆฌ ๋ทฐ
[iOS] - Stanford CS193p iOS ๊ฐ์ 2๊ฐ ์ ๋ฆฌ by Paul Hegarty
B_log 2021. 4. 5. 20:08๐ฏ MVC(Model - View - Controller)?
๐ฌ what is MVC?
๊ธฐ๋ณธ์ ์ผ๋ก ์์คํ ์์ ๋ชจ๋ ๊ฐ์ฒด๋ ์ธ๊ฐ์ง์ ์นดํ ๊ณ ๋ฆฌ๋ก ๋๋ ์ ์์ต๋๋ค.
Model: What your application is about (but not how it is displayed)
์ฑ์์ UI์ ๋ ๋ฆฝ์ ์ธ ๊ฐ์ฒด๋ค๋ก '๋ฌด์'์ ํด๋นํ๋ ์์๋ค์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, Concentration Game์์๋ ๊ฒ์ ์์ฒด๋ฅผ ํ ์ค ์๋ ๋ถ๋ถ์ ํด๋นํฉ๋๋ค. ์นด๋๊ฐ ๋งค์น๋๋์ง, ์นด๋๋ฅผ ์ ๊ฑฐํด์ผํ๋์ง, ์ธ์ ์นด๋๋ฅผ ๋ค์ง์ด์ผํ๋์ง ๊ฐ์ ๋ถ๋ถ์ ๋๋ค. (Knowledge of the game)Controller: How your Model is presented to the user (UI Logic)
์ปจํธ๋กค๋ฌ๋ ์์ ์ ์๋ ๋ชจ๋ธ์ด ์ด๋ป๊ฒ ํ๋ฉด์ ๋ํ๋๋์ง์ ๊ดํ ๊ฒ์ ๋๋ค.
View: Controller's minions
์ผ๋ฐ์ ์ธ UI ์์๋ค์ ๋๋ค. ์๋ฅผ ๋ค๋ฉด, UIButton, UIViewController, UILabel ๊ฐ์ ๊ฒ๋ค์ด ์์ต๋๋ค. Controller๊ฐ Model๊ณผ ์ฐ๋ํ์ฌ ์ฑ์ ํ์ํ ๋ฌด์ธ๊ฐ๋ฅผ ๊ฐ์ ธ์ UI์ ๋ณด์ฌ์ค ๋ ํ์ํฉ๋๋ค.
์ธ ์์์ ํต์ ์ ์๋์ ๊ฐ์ต๋๋ค.
Controller๋ Model, View์ ์ง์ ์ ์ผ๋ก ํต์ ํ ์ ์์ผ๋ฉฐ ๊ฑฐ์ ๋์์ ์์ชฝ ๋ชจ๋์ ํต์ ๊ฐ๋ฅํฉ๋๋ค. ํ์ง๋ง Model์ UI Independent ํ๋ฏ๋ก View์ ํต์ ์ด ๋ถ๊ฐํฉ๋๋ค. ํนํ View๋ ๋ฒํผ ๋ฑ UI ์์๋ค์ ์งํฉ์ธ๋ฐ UI๋ฒํผ์ด ์ด ์ฑ์ด ์ด๋ค ๊ธฐ๋ฅ์ ํ๋์ง ์๋ ๊ฒ์ ๋ง์ด๋์ง ์์ต๋๋ค.
โ๏ธ View์ Controller์ ์ปค๋ฎค๋์ผ์ด์
์ฌ๊ธฐ์ ์์ธํ ์ดํด๋ณด์์ผ ํ๋ ๊ฒ์ View๊ฐ Controller์ ํต์ ํ๋ ๋ฐฉ์์ ๋๋ค. 1๊ฐ์์ ๋ง๋ Concentration game์ ์๋ก ๋ค๋ฉด, ํ๋ฉด์์ ์นด๋ ๋ฒํผ์ ํด๋ฆญํ์ ๊ฒฝ์ฐ Controller๊ฐ ์ด๋ฅผ ์์์ผ ํ๊ธฐ ๋๋ฌธ์ ์ ๋ณด๋ฅผ ๋๊ฒจ์ค๋๋ค. ํ์ง๋ง ์ฌ๊ธฐ์ View ๊ฐ์ฒด๋ ์ผ๋ฐ์ ์ธ ๋ทฐ ๊ฐ์ฒด๋ก Controller์ ๋ํ ์ ๋ณด๊ฐ ์๋ ๋ธ๋ผ์ธ๋ ์ํ์ฌ์ผ ํฉ๋๋ค(Communication is "blind" and structured). ๋ค์ ๋งํ๋ฉด, View๊ฐ Controller์ ํต์ ํ ๋, ์๋ Controller๊ฐ ์ง์ค๋ ฅ ๊ฒ์ Controller๋ผ๋ ์ฌ์ค์ ์์ง ๋ชปํฉ๋๋ค. ๋ํ structured์ ์๋ฏธ๋ View๋ ์ผ๋ฐ์ ์ธ ๊ฐ์ฒด(generic object)๋ก ํน์ ์ปจํธ๋กค๋ฌ์ ์ด๋ป๊ฒ ๋ํ๋ฅผ ํ ์ง ๋ฏธ๋ฆฌ ์ ํด์ ธ ์์ด์ผ ํ๋ฏ๋ก ๊ตฌ์กฐํ ๋์ด์๋ค๋ ๋ป์ ๋๋ค. ์ด๋ ๊ฒ ํต์ ์ ์ํด ํ์ํ ๊ฒ์ด target-action ์ ๋๋ค.
์ผ๋ฐ ๊ฐ์ฒด(๊ฒ์์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ Controller์ ๋ค๋ฅด๊ฒ ํน์ ๋์ด์์ง ์์)์ธ View๊ฐ Controller์ ๋ํํ๊ธฐ ์ํด์๋ target์ด ์์ด์ผ ํฉ๋๋ค. ์ง์ค๋ ฅ ๊ฒ์์์ ์นด๋์์ Ctrl + ๋๋๊ทธ๋ก touchCard๋ผ๋ ๋ฉ์๋๋ฅผ ๋ง๋ค์๋๋ฐ ์ด๊ฒ์ด target์ด ๋ฉ๋๋ค. Controller๊ฐ ํด์ผํ๋ ์ผ์ ์์ ์๊ฒ ํ๊ฒ์ ๋ง๋๋ ๊ฒ์ ๋๋ค. ์ฆ, touchCard ๊ฐ์ ๋ฉ์๋๋ฅผ Controller์์ ๋ง๋ค์ด๋๋ฉด UIButton ๋ฑ์์ ํธ์ถ์ ๋ฐ์ ๋๋ง๋ค Action์ ํด๋น target์ ์ ๋ฌํ๋ ๋ฐฉ์์ ๋๋ค. ๋ฌธ์ ๋ ์ Action์ด ๋ณต์กํด์ง ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด ScrollView๋ฅผ ์๊ฐํด๋ณด๊ฒ ์ต๋๋ค. ์ฌ์ฉ์๊ฐ UI์์ ์คํฌ๋กค์ ์ฌ์ฉํ์ ๋(e.g. ๋๊น์ง ์คํฌ๋กค ํ๋์ง, ์ฌ๊ธฐ๋ฅผ ์คํฌ๋กค ํด๋ ๋๋์ง ๋ฑ) ํ์ํ ๊ฐ๋ ์ด Delegate์ ๋๋ค.
๋ธ๋ฆฌ๊ฒ์ดํธ๋ ScrollView ์์ ์๋ var์ ๋๋ค. ์ด var๋ ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด ๊ฐ์ฒด๋ ํน์ ๋ฉ์ธ์ง๋ค์ ๋ฐ์ํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ด๋ฐ ๋ฉ์ธ์ง๋ค์ will, should, did๋ก ์์ํฉ๋๋ค(I will scroll to here etc.). ์ปจํธ๋กค๋ฌ๋ ํ๋กํ ์ฝ์ ์ฌ์ฉํด์ ScrollView์๊ฒ ์์ ์ด ScrollView์ Delegate๋ผ๊ณ ์๋ ค์ค๋๋ค. ์ด๋ฅผ ํตํด, Scroll View๊ฐ ์๋ ๊ฒ์ ์ปจํธ๋กค๋ฌ๊ฐ will, should, did๋ฅผ ๊ตฌํํ๋ค๋ ์ฌ์ค ๋ฐ์ ์์ต๋๋ค. ๊ทธ ์ธ์ ๊ฒ์๊ณผ ๊ด๋ จ๋ ๋ค๋ฅธ ์ ๋ณด(e.g. ์ปจํธ๋กค๋ฌ์ ํด๋์ค)๋ ์ผ์ ์์ง ๋ชปํฉ๋๋ค.
View๋ ์๊ธฐ๊ฐ ๋ณด์ฌ์ค ๋ด์ฉ์ ๊ฐ์ง๊ณ ์์ง ๋ชปํฉ๋๋ค. ์ฆ, ํ๋ฉด์ ๋ํ๋ผ ์ ๋ณด๋ฅผ ์ธ์คํด์ค ๋ณ์๋ก ๊ฐ์ง๊ณ ์์ง ์๋ค๋ ๋ป์ ๋๋ค. iPod์์ ์์ ์ด 50,000 ๊ณก์ด ์์ ๋ ๊ทธ ๋ฆฌ์คํธ๋ฅผ ListView์์ ๊ฐ์ง๊ณ ์์ ์ด์ ๊ฐ ์์ต๋๋ค. ๊ทธ๋์ Controller์์ ์ด๋ฌํ ๋ฐ์ดํฐ ์์ค๋ฅผ ๋ฐ์์ฌ ํ์๊ฐ ์๊ณ ๊ทธ ๋ ํ์ํ ๊ฒ์ด data source๋ผ๋ ๋ธ๋ฆฌ๊ฒ์ดํธ์ ๋๋ค.
data source์ delegate๋ ๋น์ทํ์ง๋ง ์๋ก ๋ค๋ฅธ ๋ฉ์๋๋ค์ ๊ฐ์ง๊ณ ์์ ๋ฟ์ ๋๋ค. ์ด๋ ๊ฒ View๊ฐ Controller์ ํ ์ ์๋ Communication์ ๊ตฌ์กฐ์ (structured)์ด๊ณ ๋ฏธ๋ฆฌ ์ ์(predefined)๋์ด ์์ต๋๋ค.
์์ ์ด์ผ๊ธฐ๋ฅผ ์ข ํฉํ๋ฉด MVC์์ Controller์ ์ญํ ์ ๋ชจ๋ธ์ ์ ๋ณด๋ฅผ ํด์ํ๊ณ ๊ตฌ์ฑํด์ ๋ทฐ์๊ฒ ์ฃผ๋ ๊ฒ์ ๋๋ค.
โ๏ธ Model๊ณผ Controller์ ์ปค๋ฎค๋์ผ์ด์
๋ชจ๋ธ ์ญ์ Controller์ ์ง์ ์ ์ผ๋ก ๋ํํ ์ ์์ต๋๋ค. ํ์ง๋ง View์ ๋ง์ฐฌ๊ฐ์ง๋ก Model๋ Controller์ ํต์ ํด์ผํ ์ํฉ์ด ์์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์ด๋ค ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ ๊ด๋ จ๋ UI๋ค์ด ๊ทธ๊ฑธ ์ ๋ฐ์ดํธํด์ผํ ๋๊ฐ ์์ต๋๋ค. ์ด๋ ํต์ ํ๋ ๋ฐฉ๋ฒ์ ๊ต์๋์ Radio Station Model(๋ผ๋์ค ๋ฐฉ์ก๊ตญ ๋ชจ๋ธ)์ด๋ผ๊ณ ํํํ์ญ๋๋ค.
์ด๋ ๊ฒ Model์ ๋ผ๋์ค ์ก์ ๊ธฐ์ฒ๋ผ ๋ชจ๋ธ์ด ๋ณ๊ฒฝ๋์์ ๋ ์ ๋ณด๋ฅผ ์ ๋ฌํด์ผํ ๋งค์ฒด๊ฐ ์๊ณ , Controller๋ ๊ทธ ์ฃผํ์๋ฅผ Model์ ์ก์ ๊ธฐ์ ๋ง์ถฐ๋์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณ๊ฒฝ์ด ์๊ฒผ์ ๋ Controller๋ ์ด๋ก์ ํ์ดํ๋ฅผ ํตํด ๋ฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๊ฐ์ ธ์ต๋๋ค. ์ด ๋ผ๋์ค ์ก์ ๊ธฐ๋ฅผ iOS์์๋ Notification์ด๋ KVO(Key Value Observing)์ด๋ผ๊ณ ํฉ๋๋ค.
๐ฑ multiple MVCs in iPhone or iPad
์์ดํฐ์ ์ฑ ํ๋๋ ์ผ๋ฐ์ ์ผ๋ก ๋ง์ ํ๋ฉด๋ค์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๊ทธ๋์ ํ๋์ ์ฑ์ ํ๋์ MVC์์ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ๋ ๋ง์ง ์์ต๋๋ค. ์ด๋ฅผ ๋์ํํ๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
์ด๋ค Controller๊ฐ ๋ค๋ฅธ Controller์ ํต์ ํ ๋์๋ ๊ทธ ๋์์ด Controller์ง๋ง View์ฒ๋ผ ์ทจ๊ธํฉ๋๋ค. ๊ทธ๋์ ์ด๊ฒ๋ค์ ์ผ๋ฐ์ (generic)์ด๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ(reusable) ์ปดํฌ๋ํธ์ฒ๋ผ ํ๋ํฉ๋๋ค. ๋ง์ฝ ์๋์ ๊ฐ์ ๊ตฌ์กฐ๋ก ํ๋ก๊ทธ๋จ์ด ๋์ํ๋ค๋ฉด
UI์์ ๋ฌด์ธ๊ฐ๊ฐ ๋ฐ๋์์ ๋ ์ด๋ค ์ปจํธ๋กค๋ฌ๊ฐ ํ๊ณ ์๋ ๊ฒ์ธ์ง, ์ด๋ค ๋ชจ๋ธ์ด ๋ฐ์ดํฐ๋ฅผ ์ฃผ์๋์ง ๋ฑ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ฒ๊ทธ, ์ ์ง ๋ณด์ํ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค.
๐ Concentration Game์ MVC ์ ์ฉํด๋ณด๊ธฐ
MVC ์ค V, C๋ ์ด๋ฏธ ๊ตฌํ์ด ๋์๊ธฐ ๋๋ฌธ์ Model์ ๊ตฌํํ๊ธฐ ์ํด์ ์ฐ์ swiftํ์ผ์ ํ๋ ์์ฑํด์ค๋๋ค. ์ด ๋, Swiftํ์ผ๋ช ์ ๊ทธ ํ์ผ์ ํด๋์ค ์ค ๊ฐ์ฅ ๋ํ๋ ์ ์๋ ๊ฒ์ ์ด๋ฆ์ผ๋ก ํฉ๋๋ค. ์ฌ๊ธฐ์๋ Concentration์ด๋ผ๊ณ ๋ช ๋ช ํ์ต๋๋ค.
์๋ก์ด ํด๋์ค๋ฅผ ๋ง๋ค ๋์๋ ํญ์ ์ด๊ฒ์ ๊ณต๊ฐ API๊ฐ ๋ฌด์์ธ์ง ์๊ฐํด์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ API๋ Application Programming Interface๋ก ํด๋์ค ์์ ๋ชจ๋ ๋ฉ์๋์ ์ธ์คํด์ค ๋ณ์์ ๋ฆฌ์คํธ์ ๋๋ค. ๊ทธ๋ฆฌ๊ณ Public API๋ ๋ค๋ฅธ ํด๋์ค๋ค์ ์ฌ์ฉ์ ํ๋ฝํ ๋ฉ์๋์ ์ธ์คํด์ค ๋ณ์๋ค์ ๋๋ค. ๊ฒฐ๊ตญ ์ด ํด๋์ค๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ ๋๋ค.
Concentration ๊ฒ์์์ ํ์ํ ๊ณต๊ฐ API๋ ์๋์ ๊ฐ์ต๋๋ค.
import Foundation class Concentration { var cards: Array<Card> func chooseCard(at index: Int) { } }
์ฌ๊ธฐ์ cards๋ Card๋ผ๋ ์์ง ์์ฑ๋์ง ์์ ๊ฐ์ฒด์ ๋ฐฐ์ด์ด๊ณ , chooseCard๋ ์นด๋๋ฅผ ๊ณ ๋ฅด๋ ๋ฉ์๋์ธ๋ฐ Card๋ก ์ง์ ์ ๊ทผํ๋ ๊ฒ์ด ์๋๋ผ index๋ฅผ ํตํด cards์์ ๊บผ๋ด์ค๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํฉ๋๋ค.
์ด์ Card๋ฅผ ์ ์ํด ๋ณด๊ฒ ์ต๋๋ค. Card.swift๋ฅผ ์์ฑํ ํ Card๋ฅผ ๋ง๋๋๋ฐ ์ด๋ฒ์๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ตฌ์กฐ์ฒด์ ํด๋์ค์ ์ฐจ์ด๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๊ตฌ์กฐ์ฒด(struct)๋ ์์์ฑ์ด ์์ต๋๋ค.
- ๊ตฌ์กฐ์ฒด๋ ๊ฐํ์ ์ด๊ณ ํด๋์ค๋ ์ฐธ์กฐํ์ ์ ๋๋ค.
๊ฐํ์ ์ ์ธ์๋ก ๋ณด๋ด๊ฑฐ๋, ๋ฐฐ์ด์ ๋ฃ๊ฑฐ๋, ๋ค๋ฅธ ๋ณ์์ ํ ๋นํ ๋์๋ ๋ณต์ฌ๊ฐ ๋ฉ๋๋ค. ์ด๋ ๊ฒ ์๊ฐํ๋ฉด ๊ตฌ์กฐ์ฒด๋ฅผ ํตํด ํ๋ก๊ทธ๋จ์ ์ง๋ฉด ๋งค๋ฒ ๋ณต์ฌ๊ฐ ์ผ์ด๋์ ๋นํจ์จ์ ์ด๋ผ๊ณ ์๊ฐํ ์ ์์ง๋ง, Swift๋ ๊ฐํ์ ์ ์์ฑํ ๋๋ง๋ค ๋ฐ๋ก ๋ณต์ฌํ์ง ์์ต๋๋ค. ์ฐธ์กฐ์ ํํ๋ก ๊ฐ์ง๊ณ ์๋ค๊ฐ ๊ฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋ ๋ ์ค์ ๋ก ๋ณต์ฌ๋ฅผ ์งํํฉ๋๋ค. ์ด๋ฅผ "Copy on write semantics"๋ผ๊ณ ํฉ๋๋ค.
struct Card { var isFaceUp = false var isMatched = false var identifier: Int }
Card๋ ์์ ๊ฐ์ ์์๋ฅผ ํ์๋ก ํฉ๋๋ค. ์ฐ์ ๋ชจ๋ ๊ฐ์ ๋ณ๊ฒฝ ๊ฐ๋ฅํด์ผํ๋ฏ๋ก ๋ณ์(var)๋ก ์ ์ธ๋๊ณ , ์นด๋๊ฐ ๋ค์งํ์๋์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด isFaceUp, ๋ค๋ฅธ ์นด๋์ ๋งค์นญ์ด ๋๋์ง ํ์ธํ๊ธฐ ์ํด isMatched๊ฐ ํ์ํ๋ฐ ์ด ๋ ๋งค์น๊ฐ ๋๋ ์นด๋์ธ์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด ์๋ณ์๊ฐ ํ์ํฉ๋๋ค. ์ฌ์ฉํ๊ธฐ ๊ฐํธํ Intํ์ผ๋ก ์ค์ ํ์ต๋๋ค.
๊ตฌํํ ์นด๋์ ์ด๋ฏธ์ง๋ก ์ด๋ชจํฐ์ฝ์ด ๋ค์ด์์ผ๋ฏ๋ก String ํ๋กํผํฐ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํ ์ ์์ต๋๋ค. ํ์ง๋ง ์ง๊ธ ๋ง๋๋ Card๋ ๋ชจ๋ธ์ด๊ณ ๋ชจ๋ธ์ UI independentํ๋ฏ๋ก ์ด๋ชจํฐ์ฝ์ด๋ jpeg ์ด๋ฏธ์ง ๊ฐ์ ํ์ผ์ ์ฌ๊ธฐ ์กด์ฌํด์๋ ์๋ฉ๋๋ค. ๊ทธ๊ฒ๋ค์ "How do u display the cards"์ ๊ด๋ จ๋ ๊ฒ์ ๋๋ค.
์ด์ Card๊ฐ ์์ฑ๋์๊ธฐ ๋๋ฌธ์ ๋ค์ Concentration ํด๋์ค๋ก ๋์๊ฐ์ ํ์ธํฉ๋๋ค.
class Concentration { var cards = Array<Card>() func chooseCard(at index: Int) { } }
์๊น์ ๋ค๋ฅธ ์ ์ Card๋ผ๋ ํ์ ์ด ์์ฑ๋์๊ธฐ ๋๋ฌธ์ cards์ ์ ์ธ์์ ๋ฌธ์ ๊ฐ ์๊ณ , ์ด๊ธฐํ๊ฐ ์๋์๊ธฐ ๋๋ฌธ์ ํ์ ๋ช ์๊ฐ ์๋๋ผ cards๋ฅผ ์ด๊ธฐํ ํด์ค๋๋ค. ์ด ๋, Array๋ผ๋ ๋ฉ์๋๋ ํ๋ผ๋ฏธํฐ๊ฐ ์๋ init์ ๊ฐ์ง๊ณ ์์ด์ ()๋ฅผ ๋ถ์ฌ empty Array๋ฅผ ์ถ๊ฐํด์ค๋๋ค.
Array์ init์๋ ๋ค์ํ ๊ฒ๋ค์ด ์๋๋ฐ, ์๋ฅผ ๋ค์ด ์ฌ์ฉํ ์ฌ์ด์ฆ๊ฐ ์ ํด์ ธ ์๋ค๋ฉด Array์ ํฌ๊ธฐ๋ฅผ ์ ํด์ฃผ๋ init, ๋ค๋ฅธ ๋ฐฐ์ด์์ ๋ฐฐ์ด์ ๋ง๋ค์ด์ ๊ทธ ํญ๋ชฉ์ ๋ณต์ฌํด์ค๋ init ๋ฑ์ด ์์ต๋๋ค.
์ด์ View Controller์์ Model๊ณผ ํต์ ์ ์ํด ์ด๋ก์ ํ์ดํ๋ฅผ ๋ง๋๋ ์์ ์ ํด๋ณด๊ฒ ์ต๋๋ค.
class ViewController: UIViewController { var game: Concentration var flipCount = 0 { didSet { ...
game์ด๋ผ๋ Concentration ์ธ์คํด์ค๋ฅผ ์์ฑํด์ฃผ์ด ์ด์ Concentration ์๋ ์์๋ค์ ์ฌ์ฉํ ์ ์์ต๋๋ค. game์ ์์ ๊ฐ์ด ์ ์ธํด์ฃผ๋ฉด ์ญ์๋ ์ด๊ธฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค. ๊ทธ๋ผ ์ด๊ธฐํ๋ฅผ ํด๋ณด๊ฒ ์ต๋๋ค.
var game = Concentration()
Concentration ํด๋์ค๋ฅผ ์ ์ธํ ๋ init์ ์ ์ํด์ฃผ์ง ์์๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋์ผํ์ง๋ง ์ค์ ๋ก๋ ์ ์๋ํฉ๋๋ค. ์ ๊ทธ๋ด๊น์?
Swift๋ ๊ธฐ๋ณธ์ ์ผ๋ก class๋ฅผ ์ ์ธํ ๋, parameter๊ฐ ๋น์ด์๋ init์ ์๋์ผ๋ก ๊ฐ์ง๊ฒ ๋ฉ๋๋ค.
์ด ๊ฒ์์ ์์ฑํ์ ๋ ์ผ๋ง๋ ๋ง์ ์นด๋๊ฐ ์๋์ง ์๊ฐํด์ผ ํฉ๋๋ค. ๊ทธ๋์ ์๋ํ ๋ init์ด ์๋ ์ปค์คํ ํ init์ ๋ง๋ค์ด์ค ํ์๊ฐ ์์ต๋๋ค. init์์ ์นด๋ ํ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
init(numberOfPairsOfCards: Int) { let card = Card() // ์๋ฌ ๋ฐ์ }
Concentration์์๋ ์ ๋์๋ ์ด๊ธฐํ๊ฐ ์ Card์์๋ ์๋ ๊น์? ๊ทธ ์ด์ ๋ Card๊ฐ struct์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. struct ์ญ์ free initializer๋ฅผ ๊ฐ์ง๋ง class์๋ ๋ค๋ฆ ๋๋ค. struct๊ฐ ๋ฐ๋ ๊ณต์ง ์ด๋์ ๋ผ์ด์ ๋ ๋ชจ๋ ๋ณ์๋ฅผ ์ด๊ธฐํํฉ๋๋ค. ์ด๋ฏธ ์ด๊ธฐํ ๋์ด ์๋ ๊ฐ๋ ๋ค์ ์ด๊ธฐํ ํด์ค๋๋ค. ์๋๋ Xcode์ ์๋์์ฑ ๊ธฐ๋ฅ์ ์ด์ฉํ ์ฝ๋์ ๋๋ค.
init(numberOfPairsOfCards: Int) { let card = Card(isFaceUp: <#T##Bool#>, isMatched: <#T##Bool#>, identifier: <#T##Int#>) }
์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ด๋ฏธ ์ด๊ธฐํ ๋์ด์๋ isMatched ๋ฑ์ ๋ฉ์๋ ์ญ์ ์ด๊ธฐํํฉ๋๋ค. Card์ identifier๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ํด init์ ์ถ๊ฐํฉ๋๋ค.
init(identifier: Int) { identifier = identifier // ์๋ฌ ๋ฐ์ }
์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊น์? ์ฐ์ init์ ์ธ์์ธ indentifier๋ external name์ด ์กด์ฌํ์ง ์์ต๋๋ค. ๊ทธ๋์ identifier๊ฐ ์ ํํ ์ด๋ค ์๋ฏธ์ธ์ง ์ ์ ์์ต๋๋ค. ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
init(identifier i: Int) { identifier = i // ์๋ฌ ๋ฐ์ }
ํ์ง๋ง i๋ ๋งค์ฐ ์์ข์ ๋ค์ด๋ฐ์ ๋๋ค. ์๋ฌด ์๋ฏธ๋ฅผ ๋ด๊ณ ์์ง ์์ต๋๋ค. ๊ทธ๋์ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํฉ๋๋ค.
init(identifier: Int) { self.identifier = identifier }
self.identifier๋ ๊ฐ์ฒด ์์ ์ด ๊ฐ์ง๊ณ ์๋ identifier, ์ฌ๊ธฐ์๋ ํ๋กํผํฐ๋ฅผ ์๋ฏธํฉ๋๋ค.
์ด๋ฒ์ Concentration์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. Swift์ for๋ฌธ์ ์ํ์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค. sequence๋ ์ด๋๊ฐ์์ ์์ํด์ ๋ค์์ผ๋ก, ๊ทธ ๋ค์์ผ๋ก ๋์ด๊ฐ๋ฉด์ ๋ ์ ์๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. Array, String ๋ฑ์ด Sequence์ ์์์ ๋๋ค. Concentration์ init์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
init(numberOfPairsOfCards: Int) { for identifier in 1...numberOfPairsOfCards { let card = Card(identifier: identifier) let matchingCard = card cards.append(card) cards.append(matchingCard) } }
cards ๋ฐฐ์ด์ ์์ฑ๋ ์ธ์คํด์ค๋ฅผ ๋ฃ์ด์ค๋๋ค. ํ์ง๋ง ์์์ฒ๋ผ ํ ํ์๊ฐ ์์ต๋๋ค.
init(numberOfPairsOfCards: Int) { for identifier in 1...numberOfPairsOfCards { let card = Card(identifier: identifier) cards.append(card) cards.append(card) } }
matchingCard๋ผ๋ ์์๋ ํ์ํ์ง ์์ต๋๋ค. ๋ฐฐ์ด์ ๋ฃ๊ฑฐ๋ ๋บ ๋์๋ ์นด๋๋ฅผ ๋ณต์ฌํฉ๋๋ค. ์ฐธ์กฐ๊ฐ ์๋๋ผ ๋ณต์ฌ์ด๋ฏ๋ก ์์ ๋ ์ฝ๋๋ ์์ ์ด ๊ฐ์ ์ญํ ์ ํฉ๋๋ค. ๊ทธ๋์ ํ์ฌ init์ for๋ฌธ ์์๋ ์ด 3๊ฐ์ card๊ฐ ์์ฑ๋ฉ๋๋ค. 3๊ฐ์ ์นด๋๋ ์ฒ์ ์์๋ก ์ ์ธ๋ card, ์ฒซ๋ฒ์งธ cards์ append๋ ๋ ๋ณต์ฌ๋๋ card, ๋๋ฒ์งธ cards์ append๋ ๋ ๋ณต์ฌ๋๋ ์นด๋์ ๋๋ค. ์ด๋ ๊ตฌ์กฐ์ฒด๊ฐ ๊ฐํ์ ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋์ ๋๊ฐ์ ์ฝ๋๋ฅผ 2๋ฒ ์ผ๋ค๊ณ ํ๋๋ผ๋ cards์ ๋ค์ด๊ฐ ๋ card๋ ๋๊ฐ์ ๋ค๋ฅธ ์นด๋์ ๋๋ค. ํ์ง๋ง ๋ ์ค์ ๊ฐ์ ์ฝ๋๋ ๋ณด๊ธฐ ์ข์ง ์์ต๋๋ค. ๊ทธ๋์ ์๋์ ๊ฐ์ด ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
cards += [card, card]
์ด๊ฒ์ ํ์ด์ฌ์ extend์ ๊ฐ์ ์ญํ ์ด๋ฉฐ, ์ค์ ๋ก ํ์ด์ฌ์์ ๋ฆฌ์คํธ๊ฐ ๋์ํ๋ ๊ฒ๊ณผ ๊ฐ์ ํ์์ผ๋ก Array๋ฅผ extend ํด์ค๋๋ค.
์์ ์ฝ๋๋ ์จ์ ํ ์๋ํ์ง๋ง identifier = identifier๋ผ๋ ๊ตฌ๋ฌธ์ด ๋ง์์ ๊ฑธ๋ฆฝ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ์ง์ค๋ ฅ ๊ฒ์์ด ์ง์ ์นด๋์ ์๋ณ์๋ฅผ ์ง์ ํ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง ์ง์ค๋ ฅ ๊ฒ์์ ์๋ณ์๊ฐ ๋ฌด์์ธ์ง ์ ๊ฒฝ์ฐ์ง ์์ต๋๋ค. ์ ์ผํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๊ทธ๋์ identifier๋ฅผ ์ญ์ ํ๊ณ Card๊ฐ ์ง์ identifier๋ฅผ ์ค์ ํ๊ฒ ํด๋ณด๊ฒ ์ต๋๋ค.
identifier๋ Card๊ฐ ๋ช๊ฐ๊ฐ ์์ฑ๋๋ ๊ทธ๋ค์ ๊ตฌ๋ณํ ์ ์๋ ๊ตฌ๋ถ์์ฌ์ผ ํฉ๋๋ค. ๊ทธ๋์ ์๋ก์ด ์นด๋๋ฅผ ์์ฑํ ๋ ์ฌ๊ธฐ์ ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด Card์๊ฒ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค. Type Card์๋ง ์์ฒญ์ ๋ณด๋ผ ์ ์์ต๋๋ค. static์ ํตํด identifier๋ฅผ ๋ง๋ค๊ณ identifier๋ฅผ ๋ฐํํ๋ ํจ์๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
static var identifierFactory = 0 static func getUniqueIdentifier() -> Int { Card.identifierFactory += 1 return Card.identifierFactory }
์ฌ๊ธฐ์์ getUniqueIdentifier ๋ฉ์๋๋ ์ ์ ๋ฉ์๋์ด๋ฏ๋ก ์ ์ ๋ฉ์๋ ์์์๋ Card. ์์ด๋ ์ ์ ๋ณ์์ ์ ๊ทผํ ์ ์์ต๋๋ค. ๊ทธ๋์ Card.์ ์๋ตํด์ค๋ ๋ฉ๋๋ค.
static func getUniqueIdentifier() -> Int { identifierFactory += 1 return identifierFactory }
์ด ๋ณ๊ฒฝ๋ identifier๋ฅผ ์ด๊ธฐํ์ ์ฌ์ฉํ๊ธฐ ์ํด ์์ ์ด๊ธฐํ ํจ์๋ฅผ ๋ณ๊ฒฝํด๋ณด๊ฒ ์ต๋๋ค.
init() { self.identifier = Card.getUniqueIdentifier() }
์ด๋ ๊ฒ ํ๋ฉด ์๋ก์ด ์นด๋๋ฅผ ์์ฑํ ๋๋ง๋ค identifierFactory๊ฐ 1์ฉ ๋์ด๋๋ฉฐ ๊ทธ ์นด๋์ ๊ณ ์ ์ซ์๋ฅผ ๋งค๊ฒจ์ค๋๋ค.
๊ต์๋๊ป์ ์นด๋๋ฅผ ์๋ ์ฝ๋๋ฅผ ๊ณผ์ ๋ก ๋ด์ฃผ์ จ์ต๋๋ค. ์ ๋ ์๋์ ๊ฐ์ด ์์ฑํ์ต๋๋ค.
init(numberOfPairsOfCards: Int) { for _ in 1...numberOfPairsOfCards { let card = Card() cards += [card, card] } cards.shuffle() // ๋ด์ฅํจ์ }
๋ด์ฅํจ์ shuffle์ ๋ณต์ก๋๋ O(N)์ ๋๋ค. Swift์์ Shuffle์ fisher yates shuffle ์ด๋ผ๋ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํฉ๋๋ค. ์์ ํจ์๋ WWDC 2018์ ์๊ฐ๋ ๊ฒ์ผ๋ก ํด๋น ๊ฐ์๊ฐ 2017๋ ๊ณผ์ ์ด๋ผ shuffle์ด ์์ ๋ ๊ณผ์ ๋ฅผ ๋ด์ฃผ์ ๊ฒ์ผ๋ก ์ถ์ ๋ฉ๋๋ค. fisher yates๋ ์ธ๋ฑ์ค๋ฅผ 0๋ถํฐ 1์ฉ ์ฆ๊ฐ์ํค๋ฉฐ ๋ฐฐ์ด์ i ๋ฒ์งธ ์์์ ๋๋ค์ผ๋ก ๋ฝํ j ๋ฒ์งธ ์์๋ฅผ swapํ๋ ์์ผ๋ก ์งํํฉ๋๋ค.
์ด์ Controller์์ game์ ์ ์ํด์ฃผ๊ฒ ์ต๋๋ค. ์ฌ๊ธฐ์ game์ Controller๊ฐ Concentration์ ์ด์ฉํด ํ๊ฐ์ ๊ฒ์์ ์์ฑํด์ฃผ๋ ๊ฒ์ ๋๋ค.
var game = Concentration(numberOfPairsOfCards: (cardButtons.count + 1) / 2)
์ง๊ธ ๋ง๋๋ ์์์ ๊ฒฝ์ฐ ์นด๋๊ฐ 4๊ฐ์ด๋ฏ๋ก ๋จ์ํ 4๋ผ๊ณ ์ ์ ์ ์์ง๋ง ๊ทธ๋ ๊ฒ๋๋ฉด ์นด๋ ์๊ฐ ๋ฐ๋๋๋ง๋ค ๋ค์ด์์ ๋ณ๊ฒฝํด์ฃผ์ด์ผํ๊ธฐ ๋๋ฌธ์ cardButtons Array์ ํฌ๊ธฐ๋ก ์ ์ธํด์ค๋๋ค. ์ด ๋ cardButtons๊ฐ ํ์์ผ ์๋ ์์ผ๋ฏ๋ก +1 ํด์ค ํ 2๋ก ๋๋์ด์ค๋๋ค(์์ ์ด๋ฃจ๊ณ ์๊ธฐ ๋๋ฌธ์). ์ด๋ ๊ฒ ๋ง๋ฌด๋ฆฌ๊ฐ ๋๋ฉด ์ข๊ฒ ์ง๋ง ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
Cannot use instance member 'cardButtons' within property initializer; property initializers run before 'self' is available
์ค๋ฅ ๋ฉ์ธ์ง์ ์๋ฏธ๋ ํ๋กํผํฐ ์ด๊ธฐํ์์ ์ธ์คํด์ค ๋ฉค๋ฒ์ธ cardButtons๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. self๋ผ๋ ๊ฒ์ ๋ชจ๋ ์ด๊ธฐํ๊ฐ ์๋ฃ๋๊ณ ์๊ธฐ๋ ๊ฒ์ธ๋ฐ ์ด๊ธฐํ ๋จ๊ณ์์ ์ด๋ฏธ ์ธ์คํด์ค ๋ฉค๋ฒ๋ฅผ ๋ถ๋ฌ์์ ์๊ธฐ๋ ์ค๋ฅ์ ๋๋ค. Swift์์ ๋ฌด์์ ์ฌ์ฉํ๋ ์ฌ์ฉํ๊ธฐ ์ ์๋ ์์ ํ ์ด๊ธฐํ ๋์ด์ผ ํฉ๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ์ด ์์ง๋ง ์ฌ๊ธฐ์์๋ lazy๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
์์ ์ฝ๋๋ฅผ lazyํ๊ฒ ๋ค์ ์ ์ธํด์ค๋๋ค.
lazy var game = Concentration(numberOfPairsOfCards: (cardButtons.count + 1) / 2)
lazy๋ ๋๊ฐ ์ฌ์ฉํ๊ธฐ ์ ๊น์ง๋ ์ด๊ธฐํํ์ง ์๋๋ค๋ ๋ป์ ๋๋ค. ์ฆ, game์ด๋ผ๋ ํ๋กํผํฐ๋ฅผ ๋๊ตฐ๊ฐ๊ฐ ์ฌ์ฉํ๊ธฐ ์ ๊น์ง ์ด๊ธฐํํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์์๋ค์ ์ด๊ธฐํ๊ฐ ๋จผ์ ์ด๋ฃจ์ด์ง๊ณ ๋์ game์ด ํ์ํ ๋ ์ด๊ธฐํ๋๋ฏ๋ก ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋ฉ๋๋ค. ์ด game์ด ์ด๊ธฐํ๋ ๋๊น์ง ์๋ฌด๋ game ๋ณ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ข ๋ ์ฝ๊ฒ ์ค๋ช ํ๋ฉด lazy๋ ์ด ๋ณ์๊ฐ ์ด๊ธฐํ๋์๋ค๊ณ ์ณ์ค๋๋ค. ์ด lazy์๋ ์ ์ฝ ์ฌํญ์ด ์์ต๋๋ค. lazy๊ฐ ๋๋ฉด didSet์ ๊ฐ์ง ์ ์์ต๋๋ค. lazy ๋ณ์๋ Property Observer๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๐ฑ touchCard ๋ก์ง ๋ณ๊ฒฝํ๊ธฐ
์ด์ ์์ ๊ตฌํํด๋ game ์ธ์คํด์ค๋ฅผ ๋ฐํ์ผ๋ก ํ๋ฉด์์ Card๋ฅผ ๋๋ ์ ๊ฒฝ์ฐ์ ๋ก์ง์ ๋ณ๊ฒฝํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. flipCard์ ๋ช ๋ น์ game์์ Card๋ฅผ ์ ํํ๋ ๋ก์ง์ผ๋ก ๋ณ๊ฒฝํ๊ฒ ์ต๋๋ค.
@IBAction func touchCard(_ sender: UIButton) { flipCount += 1 if let cardNumber = cardButtons.firstIndex(of: sender) { // flipCard(withEmoji: emojiChoices[cardNumber], on: sender) game.chooseCard(at: cardNumber) updateViewFromModel() } else { print("Error") } }
Controller์์๋ ์ ํ๋์๋ค๊ณ ๋ง ์๋ ค์ฃผ๊ณ ์นด๋์ ์ ํ์ Model์๊ฒ ๋งก๊ธฐ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ด์ Model๋ก๋ถํฐ View๋ฅผ ์ ๋ฐ์ดํธ ํด์ผํฉ๋๋ค. ์ง๊ธ View๋ Model๊ณผ ์ฝ๊ฐ ์ฐ๊ฒฐ์ด ๋์ด์์ง ์์ต๋๋ค(little out of sync). ๊ทธ๋์ updateViewFromModel์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ ์ํด๋ณด๊ฒ ์ต๋๋ค.
func updateViewFromModel() { for index in 0..<cardButtons.count { } }
๋ชจ๋ ๋ฒํผ์ ๊ด์ฐฐํ ๋ for button in cardButtons๋ก ๊ฐ์ ธ์ฌ ์๋ ์์ต๋๋ค. cardButtons๊ฐ sequence์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํฉ๋๋ค. ํ์ง๋ง cards๋ Card ํ์ ์ Array์ด๊ณ ์ฌ๊ธฐ์ Card๋ฅผ ๊บผ๋ด์ค๋ ๊ตฌ์กฐ๋ก ์ฌ์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ index๋ก ์ ๊ทผํ๊ฒ ์ต๋๋ค. ์์ ์ฝ๋์ฒ๋ผ cardButtons.count๋ก ์์ฑํ ์ ์์ง๋ง ์๋์ ๊ฐ์ด ์์ฑํ ์๋ ์์ต๋๋ค.
func updateViewFromModel() { for index in cardButtons.indices { } }
indices(plural of index)๋ Array์ ๋ฉ์๋๋ก Array์ ๋ชจ๋ ์ธ๋ฑ์ค์ ๊ณ์ ๊ฐ๋ฅ ๋ฒ์๋ฅผ ๋ฐฐ์ด๋ก ๋ฆฌํดํด์ค๋๋ค.
Summary of indices
The indices that are valid for subscripting the collection, in ascending order.
์ด์ for๋ฌธ ์์ ์ฑ์์ฃผ๋ฉด
func updateViewFromModel() { for index in cardButtons.indices { let button = cardButtons[index] let card = game.cards[index] if card.isFaceUp { button.setTitle(emoji, for: UIControl.State.normal) button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) } else { button.setTitle("", for: UIControl.State.normal) button.backgroundColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1) } } }
๊ฐ ๋ฉ๋๋ค. cardButtons์ ์ธ๋ฑ์ค๋ฅผ ์ํํ๋ฉฐ ๋ฒํผ์ ๊ฐ์ ธ์์ button์ผ๋ก, ํด๋น ์์น์ ์นด๋๋ฅผ ๊ฐ์ ธ์์ ์นด๋๋ก ์ ์ธํด์ค๋๋ค. ์นด๋๊ฐ ์๋ฅผ ๋ณด๊ณ ์์ผ๋ฉด ๋ฒํผ์ ๊ทธ๋ฆผ๊ณผ ์ฃผํฉ์์ผ๋ก ์ค์ ํด์ฃผ๊ณ , ์นด๋๊ฐ ๋ค์งํ์๋ค๋ฉด ๊ทธ๋ฆผ์ด ์์ด ํ์์์ผ๋ก ์ค์ ํด์ค๋๋ค. ๊ธฐ์กด์ ์์ฑํด์ฃผ์๋ flipCard๋ ๋์ด์ ํ์๊ฐ ์์ผ๋ฏ๋ก ์ญ์ ํด์ค๋๋ค.
์ด์ ๋ง๋ค์ด์ฃผ์ด์ผ ํ๋ ๊ธฐ๋ฅ์ card๊ฐ ๋งค์น๋๊ณ ๋๋ฉด ๋์ด์ ๊ทธ ์นด๋๋ ์ฌ๋ผ์ ธ์ผํฉ๋๋ค. ๋ง๋ค์ด๋ isMatched๋ฅผ ์ฌ์ฉํด ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
for index in cardButtons.indices { let button = cardButtons[index] let card = game.cards[index] if card.isFaceUp { button.setTitle(emoji, for: UIControl.State.normal) button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) } else { button.setTitle("", for: UIControl.State.normal) button.backgroundColor = card.isMatched ? #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 0) : #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1) } }
์ผํญ์ฐ์ฐ์๋ฅผ ์ด์ฉํด ๊ตฌํํ์ต๋๋ค. card๊ฐ isMatched ์ํ๋ผ๋ฉด ํฌ๋ช ํ๊ฒ ๋ง๋ค์ด์ฃผ๊ณ ์๋๋ผ๋ฉด ์๋๋๋ก ์ฃผํฉ์์ ๋ณด์ฌ์ค๋๋ค.
์ฌ๊ธฐ์ setTitle์ emoji๋ ์ญ์ ํ๋ flipCard ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ์์ ๊ฐ์ ธ์จ ๊ฒ์ด๋ผ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค. ๊ทธ๋์ ์์๋ฐฉํธ์ผ๋ก ์๋ฌ๋ฅผ ์์ ๊ธฐ ์ํด ๋ฉ์๋๋ฅผ ํ๋ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
func emoji(for card: Card) -> String { return "?" } ... button.setTitle(emoji(for: card), ...
ํฐ ์๋ฏธ๋ ์์ง๋ง ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์์ฑํ์ต๋๋ค.
๐ chooseCard ๊ตฌํํ๊ธฐ (in Concentration)
ํ์ฌ card๋ cards Array์์ ์ธ๋ฑ์ค๋ก ์ ๊ทผํ์ฌ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ํด๋น ์นด๋๋ฅผ index๋ก ๊ฐ์ ธ์ isFaceUp์ ๊ฐ์ ๋ฐ๋๋ก ํด์ฃผ๋ ๋ก์ง์ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
func chooseCard(at index: Int) { if cards[index].isFaceUp { cards[index].isFaceUp = false } else { cards[index].isFaceUp = true } }
์ด์ ์๋ฎฌ๋ ์ดํฐ๋ก ์ฑ์ ์คํ์์ผ๋ณด๋ฉด ์ ์์ ์ผ๋ก ์๋ํจ์ ์ ์ ์์ต๋๋ค. ์ด์ ๋จ์ ๊ฒ์ ๋ฌผ์ํ ๋์ ์ ์ด๋ชจ์ง๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ ๋๋ค.
๐ ์นด๋์์ Emoji ๋ณด์ฌ์ฃผ๊ธฐ
์ค๋ฅ๋ฅผ ์์ ๊ธฐ ์ํด ์์๋ฐฉํธ์ผ๋ก "?" ์ถ๋ ฅํ๋ emoji ๋ฉ์๋๋ฅผ ๋ค์ ๋ณด๊ฒ ์ต๋๋ค.
var emoji = [Int:String]() func emoji(for card: Card) -> String { // ์๋ฌ ๋ฐ์ let chosenEmoji = emoji[card.identifier] return chosenEmoji }
emoji๋ผ๋ ๋์ ๋๋ฆฌ๋ฅผ ์ ์ธํด์ฃผ๊ณ card.identifier๋ก ์ด๋ชจํฐ์ฝ์ ๊ฐ์ ธ์ค๋ ์ฝ๋์ ๋๋ค. ๊ด์ฐฎ์ ๋ฏ ๋ณด์ด์ง๋ง ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค. ์์ ์ฝ๋๋ String์ ๋ฐํํ์ง๋ง ์ค์ ๋ก ๋ฐํ๋๋ ๊ฐ์ String์ด ์๋ String?(optional String)์ ๋๋ค. Dictionary๋ key๋ก item์ ์ ๊ทผํ์ฌ value๋ฅผ ๊ฐ์ ธ์ค๋ ๊ตฌ์กฐ์ธ๋ฐ key๊ฐ ์์ ์๋ ์๊ธฐ ๋๋ฌธ์ ๋ฐํ๊ฐ์ ํญ์ optional๋ก ๋์ต๋๋ค. ๊ทธ๋์ ์์ ์ฝ๋๋ฅผ ์์ ํ๋ฉด
func emoji(for card: Card) -> String { if emoji[card.identifier] != nil { return emoji[card.identifier]! } else { return "?" } }
๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. if let์ ์ฌ์ฉํ ์ ์์ง๋ง ์กฐ๊ธ ๋ค๋ฅธ ํํ์ ์จ๋ณด๊ธฐ ์ํด ์์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์์ต๋๋ค. ์์ ์ฝ๋๋ ์๋์ ๊ฐ์ด 1์ค๋ก ์์ฑ ๊ฐ๋ฅํฉ๋๋ค.
return emoji[card.identifier] ?? "?"
์์ ๋ ์ฝ๋๋ ์ ํํ ๊ฐ์ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๐ ๋์ ๋๋ฆฌ์ emoji ์ถ๊ฐํ๊ธฐ
์ด์ ๋์ ๋๋ฆฌ์ emoji๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค. emoji๋ฅผ ์์ฒญ๋ฐ์์ ๋ emojiChoices์ ์๋ emoji ์ค์์ ๋์ ๋๋ฆฌ์ ์ด๋ชจํฐ์ฝ์ด ํ์ํ ๋์๋ง ๋๋คํ๊ฒ ๋ฃ์ด์ฃผ๋ ๋ก์ง์ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค. ์ฌ๊ธฐ์ ๋๋คํ ๊ตฌํ์ ์ํด ์๋ก์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค. arc4random_uniform๋ pseudo random generator์ ๋๋ค. 0๋ถํฐ upper_bound๊น์ง์ ์ซ์ ์ฌ์ด์์ ์์์ ์ซ์๋ฅผ ์์ฑํด์ค๋๋ค. ๋ง์ง๋ง ์ํ์ ํฌํจํ์ง ์์ต๋๋ค.
func emoji(for card: Card) -> String { if emoji[card.identifier] == nil, emojiChoices.count > 0 { let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count))) emoji[card.identifier] = emojiChoices.remove(at: randomIndex) } return emoji[card.identifier] ?? "?" }
์ฐ์ arc4random ๋ฉ์๋๋ ํ๋ผ๋ฏธํฐ๋ก Int๊ฐ ์๋ UInt32 ํ์ ์ ๋ฐ์ต๋๋ค. emojiChoices.count๋ ๋ฐํ๊ฐ์ผ๋ก Int๊ฐ์ ์ฃผ๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค. ๋คํํ UInt32๋ struct์ด๊ณ initialzer์ Int๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ ๊ตฌํ์ด ๋์ด์์ด์ ์์ ๊ฐ์ด ๊ตฌํํฉ๋๋ค. ํ์ง๋ง ๋๋ค์ผ๋ก ์์ฑ๋ ๊ฐ ์ญ์ UInt32์ด๊ณ ์ฐ๋ฆฌ๊ฐ ํ์ํ randomIndex๋ Array์ index๋ก ์ฌ์ฉํด์ผํ๋ฏ๋ก Int๊ฐ ํ์ํฉ๋๋ค. Int ์ญ์ initializer์ UInt32๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ ๊ตฌํ์ด ๋์ด์์ด ๋ง์ฐฌ๊ฐ์ง๋ก Int๋ฅผ ์์ ๋ถ์ฌ ํ๋ณํ์ด ๊ฐ๋ฅํฉ๋๋ค.
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)
๋ง์ฝ emoji[card.identifier] = emojiChoices[randomIndex]๋ก ๊ตฌํํ๋ค๋ฉด ๋ฌด์์ด ๋ฌธ์ ์ผ๊น์? ์ด๋ฏธ ์ฌ์ฉ๋ ์ด๋ชจ์ง๊ฐ ๊ณ์ํด์ ๋์ฌ ์ ์์ต๋๋ค. ๊ทธ๋์ ๊บผ๋ผ๋๋ง๋ค ๊บผ๋ด์ง ์ด๋ชจ์ง๋ ํ๋ณด๊ตฐ์์ ์ ์ธ๋๋ ๋ก์ง์ด ํ์ํฉ๋๋ค. remove ๋ฉ์๋๋ ํด๋น ์ธ๋ฑ์ค์ ์์๋ฅผ ์ ๊ฑฐํ๋ ๋์์ ๊ทธ ๊ฐ์ ๋ฐํํ๋ฏ๋ก ์์ ๊ฐ์ด ์ ์๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
if emoji[card.identifier] == nil, emojiChoices.count > 0 {
์ด์ ๋ฌธ์ ๋ ๋์ด์ ๊บผ๋ด์ฌ ์ ์๋ ํ๋ณด๊ตฐ์ด ์์ ๋ ์ ๋๋ค. arc4random ํจ์๋ ์์์ ์ธ๊ธํ๋๋ก 0์์ upperbound-1๊น์ง์ ์ซ์ ์ค์ ๋๋คํ๊ฒ ํ๋๋ฅผ ๊บผ๋ด์ค๋ Unsigned Int ๊ฐ์ ๋๋ค. ๊ทธ๋์ 0 ์ดํ์ ๊ฐ์ ๋์ฌ ์ ์์ต๋๋ค. upperbound๋ ์์์ฌ์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ ์กฐ๊ฑด์ ์ค ๋ if ๋ฌธ์ ๋๊ฐ ์์ฑํด๋ ๋์ง๋ง ์์์ฒ๋ผ ์ฌํํ๊ฒ comma(,)๋ก ๋ถ๋ฆฌ ๊ฐ๋ฅํฉ๋๋ค. comma๋ and ๋ก์ง์ ์ญํ ์ ์ํํฉ๋๋ค.
๐น Concentration game ๋ก์ง ์์ฑํ๊ธฐ
2๊ฐ์ ๋ง์ง๋ง ๋ก์ง์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. ๋ง์ง๋ง ๋ก์ง์ ๊ฒ์์ ๊ด๋ จ๋ ๊ฒ์ผ๋ก Model์์๋ง ์๋ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ Concentration์ chooseCard์ ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
var indexOfOneAndOnlyFaceUpCard: Int? func chooseCard(at index: Int) { if !cards[index].isMatched { if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index { if cards[matchIndex].identifier == cards[index].identifier { cards[matchIndex].isMatched = true cards[index].isMatched = true } cards[index].isFaceUp = true indexOfOneAndOnlyFaceUpCard = nil } else { // either no cards or 2 cards are face up for flipDownIndex in cards.indices { cards[flipDownIndex].isFaceUp = false } cards[index].isFaceUp = true indexOfOneAndOnlyFaceUpCard = index } } }
indexOfOneAndOnlyFaceUpCard๋ ์ ์ผํ๊ฒ ๋ค์งํ ์นด๋์ ์ธ๋ฑ์ค์ธ์ง ์ฌ๋ถ๋ฅผ ์๋ ค์ฃผ๋ ํ๋กํผํฐ์ ๋๋ค. ๋ก์ง์ ์ดํด๋ณด๋ฉด ์ฐ์ cards[index].isMatched์ธ else ๊ตฌ๋ฌธ์ ๋ก์ง์ ๋ณด๊ฒ ์ต๋๋ค.
if !cards[index].isMatched { if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index { if cards[matchIndex].identifier == cards[index].identifier { cards[matchIndex].isMatched = true cards[index].isMatched = true } cards[index].isFaceUp = true indexOfOneAndOnlyFaceUpCard = nil
๋ชจ๋ ์กฐ๊ฑด์ ๋งค์น๊ฐ ๋์ง ์์ ๊ฒฝ์ฐ๋ง์ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ๋ฉ์๋ ์ ์ฒด๋ฅผ if ! ์ผ๋ก ๊ฐ์ธ๊ฒ๋ฉ๋๋ค. ๋งค์น๋์ง ์์๋ค์ ์๋ฏธ๋ ์ด๋ฏธ ์ง์ด ์์ฑ๋ ์นด๋๊ฐ ์๋ ๋์ ์กฐ๊ฑด์ ๋๋ค. ๊ทธ ์์ if let์ ์กฐ๊ฑด๋ฌธ์ ์ด๋ฏธ ํ์ฅ์ ์นด๋๊ฐ ๋ค์งํ ์๊ณ , ์ด๋ฏธ ๋ค์งํ์๋ ์นด๋๋ฅผ ํด๋ฆญํ ๊ฒ์ด ์๋ ์๋ก์ด ์นด๋๋ฅผ ํด๋ฆญํ์ ๋ ์๋ํฉ๋๋ค.
์ด๋ฏธ ๋ค์งํ์๋ ํ์ฅ์ ์นด๋๊ฐ ์๋กญ๊ฒ ๊ณ ๋ฅธ ์นด๋์ ๊ฐ์ ์ด๋ชจ์ง(์๋ณ์)๋ผ๋ฉด ๋ ์นด๋์ isMatched๋ true๋ก ๋ฐ๊พธ์ด์ฃผ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ณ ๋ฅธ ์นด๋๋ฅผ ๋ค์ง์ด์ค ํ ๋ค์งํ ์นด๋๋ฅผ ์ด๊ธฐํ ํด์ค๋๋ค.
else { // either no cards or 2 cards are face up for flipDownIndex in cards.indices { cards[flipDownIndex].isFaceUp = false } cards[index].isFaceUp = true indexOfOneAndOnlyFaceUpCard = index }
์ด ์ํฉ์ ์นด๋๊ฐ ๋ชจ๋ ๋ค์งํ์๋ ์ํ์์ ์ฒ์์ผ๋ก ์นด๋ ํ๋๋ฅผ ์คํํ๋ ์ํฉ์ด๊ฑฐ๋ ์ด๋ฏธ 2์ฅ์ด ์คํ๋์ด ์๋ ์ํ์์ ๋ค์ ์ก์ ์ผ๋ก ์นด๋๋ฅผ ๋๋ฅธ ์ํฉ์ ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ๋ชจ๋ ์นด๋๋ฅผ ๋ค์ ๋ค์ง์ด์ฃผ๊ณ ๊ณ ๋ฅธ ์นด๋๋ง ๋ค์ ๋ค์ง์ด์ฃผ๋ ๋ก์ง์ด ๋ฉ๋๋ค.
Reference: Stanford CS193p by Paul Hegarty
'iOS ์ฑ๊ฐ๋ฐ > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS] - Table View & Collection View (ํ ์ด๋ธ ๋ทฐ, ์ปฌ๋ ์ ๋ทฐ) (0) | 2021.04.13 |
---|---|
[iOS] - Stanford CS193p iOS ๊ฐ์ 3๊ฐ ์ ๋ฆฌ by Paul Hegarty (0) | 2021.04.08 |
[iOS] - Control Event(์ปจํธ๋กค ์ด๋ฒคํธ) (0) | 2021.04.06 |
[iOS] - Stanford CS193p iOS ๊ฐ์ 1๊ฐ ์ ๋ฆฌ by Paul Hegarty (0) | 2021.04.02 |
[iOS] - Anatomy of a Constraint in Auto Layout (0) | 2021.03.31 |
- Total
- Today
- Yesterday
- ๋ฆฌ๋ชจ์ปจ#์์ ํ์#BOJ#Python
- API#lazy#
- NumberofDiscIntersections#Codility#Sort#Python
- filter#isalnum#lower
- ๋ฐฐ์ดํฉ์น๊ธฐ#๋ถํ ์ ๋ณต#BOJ#Python
- Triangle#Sorting#Codility#Python
- ์ํธ์ฝ๋#dp#BOJ#Python
- ๋ฐฑ์ค ์๊ณ ๋ฆฌ์ฆ#BackTracking
- N์ผ๋ก ํํ#DP#Programmers#Python
- ๋ฐ๋ณต์์ด#๋ฐฑ์ค์๊ณ ๋ฆฌ์ฆ#Python
- ์ข ์ด์๋ฅด๊ธฐ#๋ถํ ์ ๋ณต#BOJ#Python
- ์ฟผ๋ํธ๋ฆฌ#BOJ#๋ถํ ์ ๋ณต#Python
- ์ฌ์๊ฐ์#๋ฐฑ์ค์๊ณ ๋ฆฌ์ฆ#Python
- ๋๋ฌด์๋ฅด๊ธฐ#BOJ#์ด๋ถํ์#Python
- Swift#Tuples#Range
- Brackets#Stacks and Queues#Codility#Python
- ๊ณต์ ๊ธฐ ์ค์น#BOJ#์ด๋ถํ์#Python
- ํ ๋งํ #๋ฐฑ์ค์๊ณ ๋ฆฌ์ฆ#Python
- ๋ณ๋ ๋์ดํธ#BOJ#ํ์๋ฒ#Python
- Distinct#Codility#Python
- ๋์ ์๋ฅด๊ธฐ#์ด๋ถํ์#BOJ#Python
- ํ์ด์ฌ์๊ณ ๋ฆฌ์ฆ์ธํฐ๋ทฐ#4์ฅ
- ์์ด์ฌ์ดํด#BOJ#Python
- PassingCars#Codility#Python
- django#slicing
- django
- ํ ํ๋ก์ ํธ#๋ฐฑ์ค์๊ณ ๋ฆฌ์ฆ#Python
- ํฐํ๋น์น#๋ฆฌ์ฝ#xbox#controller
- ๋ฏธ๋ก ํ์#๋ฐฑ์ค์๊ณ ๋ฆฌ์ฆ#Python
- ๋ ์ง ๊ณ์ฐ#BOJ#์์ ํ์#Python
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |