디자인
코드
ViewController.swift 파일 코드
import UIKit
var calculatorCount = 0
class ViewController: UIViewController {
@IBOutlet private weak var display: UILabel!
private var userIsInTheMiddleOfTyping: Bool = false
//MVC가 생성될 때 한번만 호출
override func viewDidLoad() {
super.viewDidLoad()
calculatorCount += 1
print("Loa ded up a new Calculator (Count = \(calculatorCount))")
//루트 대신 Z를 사용
brain.addUnaryOperation(symbol: "Z") { [ unowned me = self ] in //unoewd이기 때문에 me로 되어 있으면 힙에 아무것도 저장하지 않음
me.display.textColor = UIColor.red
return sqrt($0)
}
}
//힙에서 사라지기 직전에 호출되는 특별한 메소드
deinit {
calculatorCount -= 1
print("Calculator left the heap (Count = \(calculatorCount))")
}
@IBAction private func touchDigit(_ sender: UIButton) {
let digit = sender.titleLabel?.text //버튼의 현재 title
//버튼을 누르게 되면 기존에 있던 0이 사라지고 버튼이 입력 됨
if userIsInTheMiddleOfTyping {
let textCurrentlyInDisplay = display.text!
display.text = textCurrentlyInDisplay + digit!
}
else {
display.text = digit
}
userIsInTheMiddleOfTyping = true
}
var displyValue : Double {
get {
return Double(display.text!)!
}
set {
display.text = String(newValue)
}
}
var savedProgram: CalculatorBrain.PropertyList? //save버튼을 안눌렀을 수도 있기 때문에 Optional 타입
//계산 값을 저장
@IBAction func save() {
savedProgram = brain.program
}
//저장한 값을 불러온다.
@IBAction func restore() {
if savedProgram != nil {
brain.program = savedProgram!
displyValue = brain.result
}
}
//Model에 접근
private var brain:CalculatorBrain = CalculatorBrain()
@IBAction private func performOperation(_ sender: UIButton) {
//만약 사용자가 숫자를 입력 중이라면
if userIsInTheMiddleOfTyping {
brain.setOperand(operand: displyValue)
userIsInTheMiddleOfTyping = false
}
if let mathmaticalSymbol = sender.titleLabel?.text {
brain.performOperation(symbol: mathmaticalSymbol)
}
displyValue = brain.result
}
}
Model을 담당하는 CalculatorBrain.swift 파일 코드
//Model
import Foundation
class CalculatorBrain {
//결과를 누적해 나가기 위한 변수
private var accumulator:Double = 0.0
private var internerProgram = [Any]()
//피연산숫자
func setOperand(operand: Double) {
accumulator = operand //피연산숫자로 들어오는 값으로 accumulator를 set
internerProgram.append(operand)
}
func addUnaryOperation(symbol: String, operation: @escaping (Double) -> Double) {
operations[symbol] = Operation.UnaryOperaion(operation)
}
var operations: Dictionary<String,Operation> = [
"π" : Operation.Constant(.pi),
"e" : Operation.Constant(M_E),
"±" : Operation.UnaryOperaion({ -$0 }),
"√" : Operation.UnaryOperaion(sqrt),
"cos" : Operation.UnaryOperaion(cos),
"×" : Operation.BinaryOperation({ $0 * $1 }), //클로저 사용
"÷" : Operation.BinaryOperation({ $0 / $1 }),
"+" : Operation.BinaryOperation({ $0 + $1 }),
"-" : Operation.BinaryOperation({ $0 - $1 }),
"=" : Operation.Equals
]
enum Operation {
case Constant(Double) //상수, () 안에 자료형을 넣어서 Dictionary에서 사용
case UnaryOperaion((Double) -> Double) //단항 연산, 함수를 사용하기 위해 자료형과 반환값의 자료형을 넣어서 사용
case BinaryOperation((Double, Double) -> Double) //이항 연산, 두개의 Double형 자료를 입력 받아 Double형 결과를 반환
case Equals
}
func performOperation(symbol: String) {
internerProgram.append(symbol)
if let operation = operations[symbol] {
switch operation {
case .Constant(let associatedConstantValue):
accumulator = associatedConstantValue //값을 가져오기 위해 지역변수 선언
//Double형을 입력 받아 Double형을 반환하기 때문에 함수처럼 사용
case .UnaryOperaion(let function):
accumulator = function(accumulator)
case .BinaryOperation(let function):
excutePendingBinaryOperation()
pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
case .Equals: excutePendingBinaryOperation()
}
}
}
private func excutePendingBinaryOperation() {
if pending != nil {
accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
pending = nil
}
}
private var pending: PendingBinaryOperationInfo?
//struct(구조체)는 enum처럼 값으로 전달된다. 반면에 클래스는 참조형식으로 전달된다.
//참조형식의 경우 heap영역에 저장되었다가 전달하게 되면 값 그 자체가 아닌 메모리의 주소를 전달하게 된다.
//대기중인 이항연산 정보 - 이항연산에 필요한 값 2개를 가지고 있음
private struct PendingBinaryOperationInfo {
var binaryFunction: (Double, Double) -> Double
var firstOperand: Double
}
typealias PropertyList = AnyObject //type를 만들 수 있게 해준다.
//program이 AnyObject타입과 동시에 PropertyList이기도 함
var program: PropertyList {
get {
return internerProgram as AnyObject
}
set {
clear()
if let arrayOfOps = newValue as? [AnyObject] {
for op in arrayOfOps {
if let operand = op as? Double {
setOperand(operand: operand)
}
else if let operation = op as? String {
performOperation(symbol: operation)
}
}
}
}
}
func clear() {
accumulator = 0.0
pending = nil
internerProgram.removeAll()
}
var result: Double {
get {
return accumulator
}
}
}
실행 화면
'iOS-Study' 카테고리의 다른 글
iOS Study : 12주차 - 얼굴앱 만들기 (0) | 2023.01.24 |
---|---|
iOS Study : 11주차 - 기초 문법 공부 (0) | 2023.01.19 |
iOS Study : 10주차 - 오토레이아웃 정리(2) (0) | 2023.01.09 |
iOS Study : 9주차 - 오토레이아웃 정리(1) (0) | 2022.12.28 |
iOS Study : 8주차 - MVVM 디자인 패턴 (0) | 2022.12.26 |