2주차 iOS 수업 내용 및 수업 후 과제

2025. 9. 9. 16:16iOS 기초/수업 내용 및 수업 후 과제

반응형

 

코드는 Flappy Bird 게임을 만들기 위한 GameViewController.swift 파일로, SpriteKit을 사용하여 게임을 구현하는 데 필요한 뷰 컨트롤러입니다. 각 줄에 대해 주석을 달아서 쉽게 설명할게요.

// GameViewController.swift
// FlappyBird
// Created by Nate Murray on 6/2/14.
// Copyright (c) 2014 Fullstack.io. All rights reserved.

import UIKit // iOS의 기본 UI 라이브러리를 가져옵니다.
import SpriteKit // SpriteKit은 2D 게임 개발을 위한 라이브러리입니다.

extension SKNode { // SKNode(모든 SpriteKit 객체의 기본 클래스)에 새로운 메소드를 추가하는 확장입니다.
    // unarchiveFromFile 메소드는 .sks 파일에서 씬을 로드하고 반환합니다.
    class func unarchiveFromFile(_ file : String) -> SKNode? {
        
        // .sks 파일의 경로를 가져옵니다.
        let path = Bundle.main.path(forResource: file, ofType: "sks")
        
        // .sks 파일에서 데이터를 읽어옵니다.
        let sceneData: Data?
        do {
            sceneData = try Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe)
        } catch _ {
            sceneData = nil // 오류가 발생하면 데이터는 nil로 설정합니다.
        }
        
        // 데이터에서 씬을 디코딩합니다.
        let archiver = NSKeyedUnarchiver(forReadingWith: sceneData!)
        
        // "SKScene" 클래스에 대한 정보를 설정합니다.
        archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
        
        // 씬을 GameScene 타입으로 디코딩합니다.
        let scene = archiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! GameScene
        
        archiver.finishDecoding() // 디코딩을 완료합니다.
        return scene // GameScene을 반환합니다.
    }
}

class GameViewController: UIViewController { // GameViewController는 UIViewController의 서브클래스로, 게임의 뷰를 관리합니다.

    override func viewDidLoad() {
        super.viewDidLoad()

        // GameScene을 로드하고, 성공적으로 로드되었으면 게임 뷰를 설정합니다.
        if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
            // SKView를 뷰로 설정합니다. SKView는 SpriteKit 씬을 표시하는 뷰입니다.
            let skView = self.view as! SKView
            
            // FPS(초당 프레임 수)와 노드의 개수를 표시합니다.
            skView.showsFPS = true
            skView.showsNodeCount = true
            
            // 성능을 최적화하기 위해, 부모 노드 순서를 무시하도록 설정합니다.
            skView.ignoresSiblingOrder = true
            
            // 씬의 크기 모드를 'aspectFill'로 설정하여 화면 비율에 맞게 씬이 확대되거나 축소되도록 합니다.
            scene.scaleMode = .aspectFill
            
            // SKView에 씬을 표시합니다.
            skView.presentScene(scene)
        }
    }

    // 화면 회전 기능을 활성화합니다.
    override var shouldAutorotate : Bool {
        return true
    }

    // 지원하는 화면 방향을 설정합니다.
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            // iPhone은 모든 방향을 지원하지만, 뒤집어진 방향( upside down)은 제외합니다.
            return UIInterfaceOrientationMask.allButUpsideDown
        } else {
            // iPad는 모든 방향을 지원합니다.
            return UIInterfaceOrientationMask.all
        }
    }

    // 메모리 경고를 받으면 불필요한 데이터나 이미지를 해제하여 메모리를 최적화합니다.
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // 사용 중이지 않은 캐시된 데이터나 이미지를 해제합니다.
    }
}

 

https://www.onlinegdb.com/online_swift_compiler

https://swiftfiddle.com/

 

Online Swift Compiler - online editor

OnlineGDB is online IDE with swift compiler. Quick and easy way to run swift scripts online.

www.onlinegdb.com

두가지 컴파일러를 써서 활용한다.

 

 Xcode playground 실행

 

 

스위프트에는 다양한 데이터 타입이 있습니다. 이를 통해 숫자, 문자, 배열 등 여러 가지 유형의 데이터를 다룰 수 있습니다. 각 데이터 타입을 예를 들어 설명해 드릴게요!

1. 정수 타입 (Integer Types)

정수는 소수점 없이 숫자를 나타냅니다.

  • Int: 기본적인 정수 타입. 크기는 플랫폼에 따라 다르며, 32비트나 64비트로 사용됩니다.
  • UInt: 부호 없는 정수. 음수가 아닌 정수만 사용할 수 있습니다.

예시:

 
let age: Int = 25 // 정수 타입 let population: UInt = 7000000 // 부호 없는 정수

2. 부동 소수점 타입 (Floating-Point Types)

부동 소수점 수는 소수점을 포함한 숫자입니다.

  • Float: 32비트 부동 소수점 수. 정밀도가 비교적 낮습니다.
  • Double: 64비트 부동 소수점 수. 더 높은 정밀도를 제공합니다.

예시:

 
let pi: Double = 3.14159 // Double 타입 let price: Float = 9.99 // Float 타입

3. 문자열 타입 (String Type)

문자열은 텍스트를 저장하는 타입입니다.

  • String: 문자열 타입. 문자나 텍스트를 저장합니다.

예시:

 
let greeting: String = "Hello, Swift!" // 문자열 타입

4. 불린 타입 (Boolean Type)

불린 값은 참(true) 또는 거짓(false)의 값을 가집니다.

  • Bool: 참과 거짓을 나타내는 타입입니다.

예시:

 
let isSunny: Bool = true // 참 let isRaining: Bool = false // 거짓

5. 배열 타입 (Array Type)

배열은 여러 값을 저장할 수 있는 컬렉션 타입입니다.

  • Array: 같은 타입의 값을 순서대로 저장하는 자료형입니다. 크기가 고정되어 있지 않으며, 값의 삽입과 삭제가 가능합니다.

예시:

 
let numbers: [Int] = [1, 2, 3, 4, 5] // 정수 배열 let names: [String] = ["Alice", "Bob", "Charlie"] // 문자열 배열

6. 딕셔너리 타입 (Dictionary Type)

딕셔너리는 키와 값으로 이루어진 데이터 집합입니다.

  • Dictionary: 키-값 쌍으로 데이터를 저장합니다. 각 값은 고유한 키로 참조됩니다.

예시:

 
let person: [String: String] = ["name": "John", "age": "30"] let scores: [String: Int] = ["Alice": 85, "Bob": 92]

7. 튜플 타입 (Tuple Type)

튜플은 여러 값을 하나의 복합 타입으로 묶을 수 있는 자료형입니다. 각 값은 서로 다른 타입일 수 있습니다.

예시:

 
let personInfo: (String, Int) = ("Alice", 30) // 튜플, 이름과 나이를 묶음 let point: (x: Int, y: Int) = (x: 10, y: 20) // 튜플에 레이블을 붙여서 사용

8. 옵셔널 타입 (Optional Type)

옵셔널은 값이 있을 수도, 없을 수도 있는 경우를 처리하는 타입입니다. 즉, nil 값을 가질 수 있는 타입입니다.

  • Optional: 값이 있을 수도, 없을 수도 있는 타입을 나타냅니다.

예시:

 
var name: String? = "John" // 옵셔널 문자열 name = nil // 값이 없을 수도 있음

옵셔널 값을 안전하게 다루는 방법은 옵셔널 바인딩이나 옵셔널 체이닝을 사용하는 것입니다.

옵셔널 바인딩 예시:

 
if let unwrappedName = name { print("Name is \(unwrappedName)") // 옵셔널이 아닌 값으로 사용 } else { print("Name is nil") }

9. 클래스와 구조체 타입 (Class & Struct Types)

클래스와 구조체는 복잡한 데이터를 다루기 위한 사용자 정의 타입입니다.

  • Class: 참조 타입. 객체를 생성하여 사용합니다.
  • Struct: 값 타입. 구조체의 인스턴스가 복사됩니다.

예시 (클래스):

 
class Car { var brand: String var model: String init(brand: String, model: String) { self.brand = brand self.model = model } } let myCar = Car(brand: "Toyota", model: "Corolla")

예시 (구조체):

 
struct Point { var x: Int var y: Int } let point1 = Point(x: 10, y: 20)

10. 열거형 타입 (Enum Type)

열거형은 여러 값 중 하나를 선택할 수 있게 해주는 데이터 타입입니다. 보통 상수 값을 묶어서 사용할 때 유용합니다.

예시:

 
enum Direction { case north case south case east case west } let direction = Direction.north // 북쪽을 나타냄

결론

스위프트는 다양한 데이터 타입을 제공하여, 숫자, 텍스트, 컬렉션, 객체 등 여러 형태의 데이터를 처리할 수 있습니다. 각 데이터 타입은 사용 목적에 따라 다르게 활용되며, 타입을 잘 이해하고 선택하는 것이 중요합니다.

 

변수 x는 사용되었다

두가지방법

Xcode에서는 간단한 오류는 고쳐줌

컴파일러가 초기값을 보고 알아서 추론한다 

변수명 소문자로 시작 단어가 바뀌면 대문자로 바꾼다

var myNumber = 10 //변수명:lowerCamelCase(소문자로 시작),UpperCamelCase(대문자로 시작)

 

스위프트 print()함수의 사용법을 예를 들어 설명
ChatGPT의 말:

print() 함수는 스위프트에서 값을 콘솔에 출력할 때 사용하는 함수입니다. 
주로 디버깅을 위해 변수의 값을 확인하거나, 사용자에게 정보를 제공하기 위해 사용됩니다. 
print() 함수는 콘솔에 텍스트, 변수 값 등을 출력할 수 있습니다.

다양한 사용법을 예제와 함께 설명할게요.

1. 기본 출력

print() 함수는 간단하게 값을 출력하는 데 사용됩니다. 이때 출력되는 값은 기본적으로 콘솔에 표시됩니다.

예시:
print("Hello, Swift!")  // 문자열 출력


출력:

Hello, Swift!

2. 변수 출력

변수나 상수의 값을 출력할 수도 있습니다.

예시:
let name = "Alice"
let age = 25
print(name)  // 변수 값 출력
print(age)   // 변수 값 출력


출력:

Alice
25

3. 문자열과 변수 결합

print() 함수에서 문자열과 변수를 결합하여 출력할 수 있습니다. 
문자열 안에 변수를 삽입하려면 문자열 보간법(Interpolation)을 사용합니다.

문자열 보간법 (String Interpolation)

문자열 안에서 \(변수) 형식으로 변수를 삽입할 수 있습니다.

예시:
let name = "Bob"
let age = 30
print("My name is \(name) and I am \(age) years old.")


출력:

My name is Bob and I am 30 years old.

4. 여러 값 출력

print() 함수는 여러 값을 한 번에 출력할 수 있습니다. 각 값은 기본적으로 공백으로 구분됩니다.

예시:
let fruit = "apple"
let count = 5
print(fruit, count)


출력:

apple 5

5. 출력 형식 변경: separator와 terminator

print() 함수는 출력 형식을 변경할 수 있는 두 가지 주요 파라미터인 
separator와 terminator를 제공합니다.

separator: 여러 값이 출력될 때, 각 값 사이에 어떤 문자를 넣을지 결정합니다. 기본값은 " " (공백)입니다.

terminator: 출력 후 끝에 어떤 문자를 넣을지 결정합니다. 기본값은 "\n" (새 줄)입니다.

예시 1: separator 사용
print("apple", "banana", "cherry", separator: ", ")  // 값 사이에 콤마와 공백을 추가


출력:

apple, banana, cherry

예시 2: terminator 사용
print("Hello,", terminator: " ")  // 끝에 공백을 추가
print("world!")


출력:

Hello, world!


terminator를 사용하면 기본적으로 줄바꿈을 하지 않고, 
원하는 문자를 출력 후 다른 출력을 이어서 할 수 있습니다.

6. 새 줄 없이 출력

terminator를 사용하여 출력 후 새 줄 없이 출력할 수 있습니다.

예시:
print("This is", terminator: " ")
print("on the same line.")


출력:

This is on the same line.

7. 디버깅용 출력

디버깅 시에는 변수의 값을 출력하여 상태를 확인할 수 있습니다. 
복잡한 객체나 배열 등을 출력할 때도 유용합니다.

예시:
let array = [1, 2, 3, 4]
print("Array contents:", array)  // 배열 출력


출력:

Array contents: [1, 2, 3, 4]

8. 디버깅을 위한 라벨과 함께 출력

print() 함수에서 값을 출력할 때, 해당 값의 라벨을 붙여서 출력할 수도 있습니다. 
이를 통해 어떤 값이 출력되고 있는지 구분할 수 있습니다.

예시:
let temperature = 22
let humidity = 60
print("Temperature:", temperature)
print("Humidity:", humidity)


출력:

Temperature: 22
Humidity: 60

9. 객체 출력

객체나 배열, 딕셔너리 등의 컬렉션 타입을 출력할 때도 print()를 사용할 수 있습니다. 
스위프트는 기본적으로 대부분의 타입에 대해 적절한 description을 제공하여 출력할 수 있게 합니다.

예시 1: 배열 출력
let numbers = [10, 20, 30, 40]
print("Numbers:", numbers)


출력:

Numbers: [10, 20, 30, 40]

예시 2: 딕셔너리 출력
let person = ["name": "Alice", "age": 30]
print("Person Info:", person)


출력:

Person Info: ["name": "Alice", "age": 30]

10. 옵셔널 출력

옵셔널 값을 출력할 때, 옵셔널이 nil일 경우 출력 결과가 달라집니다.

예시 1: 옵셔널 값이 있는 경우
var name: String? = "John"
print(name)  // Optional("John")

예시 2: 옵셔널 값이 없는 경우
var name: String? = nil
print(name)  // nil


옵셔널의 값을 출력할 때, 직접 값을 언래핑하거나 옵셔널 바인딩을 사용하면 더 깔끔하게 출력할 수 있습니다.

예시 3: 옵셔널 바인딩 사용
if let unwrappedName = name {
    print("Name is \(unwrappedName)")  // 옵셔널 바인딩을 사용해 안전하게 출력
} else {
    print("Name is nil")
}


출력 (name이 nil일 때):

Name is nil

요약

print() 함수는 콘솔에 정보를 출력하는 데 사용됩니다.

문자열, 변수, 배열, 딕셔너리 등 다양한 데이터를 출력할 수 있습니다.

separator와 terminator를 사용하여 출력 형식을 세밀하게 조정할 수 있습니다.

옵셔널 값은 nil을 포함할 수 있으므로 출력 시 주의가 필요합니다.

디버깅 시 변수의 값을 출력하여 프로그램의 상태를 확인하는 데 유용합니다.

print() 함수는 매우 직관적이고 유용한 디버깅 도구로, 
코드의 흐름을 추적하거나 변수의 상태를 확인할 때 자주 사용됩니다.
swift의 print()함수 사용법

    print("안녕")  // 문자열 출력
    print(123)  // 정수 출력
    
    var age = 20
    var name = "Smile"
    
    print(age)  // 변수 age 출력
    print(age, name)  // 변수 age와 name을 공백으로 구분하여 출력
    print("나이는 \(age)입니다")  // 문자열 보간법을 사용하여 age 값을 삽입
    
    print(1, 2, 3)  // 값들을 공백으로 구분하여 출력
    print(1, 2, 3, separator: "-")  // 값들 사이에 "-" 구분자를 추가
    
    print("Smile")
    print("Han")
    
    print("Hello", terminator: " ")  // 출력 후 줄바꿈 대신 공백을 추가
    print("World!")
    
    print("다음", terminator: "")  // 출력 후 줄바꿈도, 공백도 추가하지 않음
    print("입니다.")
    


출력결과

안녕
123
20
20 Smile
나이는 20입니다
1 2 3
1-2-3
Smile
Han
Hello World!
다음입니다.

자료형을 알 수 있다

 

var x = 10
print(type(of: x))
print(MemoryLayout.size(ofValue: x))

실행결과
Int
8

보통은 int형이 4바이트지만
swift에서는 8바이트이다
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

var x = 10.5
print(type(of: x))
print(MemoryLayout.size(ofValue: x))

실행결과
Double
8
var x = 10
print(x)
print("x")
print("\(x)");print("값은 \(x)입니다.")
print("Int32 Min = \(Int32.min) Int32 Max = \(Int32.max)")
print("Int Min = \(Int.min) Int Max = \(Int.max)")

실행결과
10
x
10
값은 10입니다.
Int32 Min = -2147483648 Int32 Max = 2147483647
Int Min = -9223372036854775808 Int Max = 9223372036854775807


정수 데이터 타입 : Int
 정수(소수점이 없는 수)를 저장하는 데 사용
 양수, 음수, 영(0) 값을 담을 수 있는 부호 있는(signed) 정수
 영과 양수만 담을 수 있는 부호 없는(unsigned) 정수
 8비트, 16비트, 32비트, 64비트, 128비트 정수
 Int8, Int16, Int32, Int64, Int128
 부호 없는 정수
 UInt8, UInt16, UInt32, UInt64, UInt128
 애플은 특정 크기의 데이터 타입 보다 Int 데이터 타입을 권장
 Int 데이터 타입은 해당 코드가 실행되는 플랫폼에 맞는 정수 크기를 사용
 var myAge : Int = 20 // 초깃값 20이 있으므로 : Int는 일반적으로 생략함
 32비트 부호 있는 정수 데이터 타입에 대한 최솟값과 최댓값을 출력
 \(출력하고 싶은 변수나 상수)
문자 데이터 타입 : Character
 문자, 숫자, 문장 부호, 심볼 같은 유니코드(Unicode) 문자 하나를 저장
 var 변수명: Character =
"초깃값"
 주의 : 초깃값은 작은 따옴표가 아니고 큰 따옴표
 var myChar1 : Character
 var myChar2 : Character = ":"
 var myChar3 = "X" //주의 : Character 생략불가, 생략하면 String형으로 인식
 print(type(of: myChar3))
 유니코드를 이용하여 변수에 문자 'X'를 할당
 var myChar4 = "\u{0058}"
 print(myChar4)
 myChar4 = "\u{0263B}" // 강조했음
 print(myChar4)


-------------------------------------------------
var myChar3 : Character = "X"
//주의 : Character 생략불가, 생략하면 String형으로 인식
print(type(of: myChar3))

실행결과
Character
var와 let

var x = 10
x = 20
print(x)

출력결과
20

let x = 10
x = 20
print(x)

출력결과
expression failed to parse:
error: 22.playground:2:1: error: cannot assign to value: 'x' is a 'let' constant
x = 20
^

22.playground:1:1: note: change 'let' to 'var' to make it mutable
let x = 10
^~~
var

아래 사진 참고
하지만 바뀌지 않는 값에는 let으로 쓰는게 더 좋다
예시)
let x = 10
// x = 20
print(x)

-------------------------------
let x : Int
x = 20
print(x)

출력결과
20

스위프트에서 var보다 let을 쓰는게 더 좋다는데 그 이유를 알려줘
ChatGPT의 말:

스위프트에서 var보다 let을 쓰는 것이 더 권장되는 이유는 **불변성(immutability)**을 통해 
코드의 안전성과 안정성을 높이기 위해서입니다. let을 사용하면 변수가 한 번 할당된 후 변경될 수 없기 때문에, 
예상치 못한 오류를 줄이고, 코드의 예측 가능성을 높이며, 효율성을 개선하는데 도움이 됩니다. 
var는 변수를 변경할 수 있기 때문에 코드의 흐름을 추적하기 어려워지고, 
그로 인해 버그가 발생할 가능성이 높아질 수 있습니다.

다음은 let을 사용하는 것이 좋은 몇 가지 이유를 설명합니다:

1. 불변성(Immutable)은 안전한 코드 작성에 도움이 된다

let을 사용하면 변수의 값이 고정되어 변경되지 않기 때문에, 
해당 값이 예기치 않게 변경되지 않도록 보장할 수 있습니다. 이는 예기치 않은 부작용을 줄이고, 
코드를 더 안전하게 만듭니다.

예시:
let number = 10
// number = 20  // 오류: 'let'으로 선언된 변수는 변경할 수 없습니다.


변수가 변경되지 않도록 보장하는 것은 코드의 예측 가능성을 높이고, 디버깅을 쉽게 만듭니다.

2. 코드 가독성 향상

let을 사용하면 해당 변수의 값이 변경되지 않음을 명시적으로 나타낼 수 있습니다. 
이는 코드 읽는 사람에게 그 값이 불변임을 알리므로, 
다른 사람이 코드를 읽을 때 더 명확한 의도를 전달할 수 있습니다.

예시:
let userName = "Alice"  // 이 값은 변경되지 않음을 알 수 있음


반면, var는 그 값이 언제든지 변경될 수 있기 때문에, 코드의 흐름을 더 복잡하게 만들 수 있습니다.

3. 불변 객체 사용의 이점

불변 객체는 상태를 변경할 수 없기 때문에 멀티스레드 환경에서 안전하게 사용할 수 있습니다. 
여러 스레드가 동일한 객체에 접근하더라도 상태 변경을 걱정하지 않아도 됩니다.

예시:
let user = User(name: "Alice")  // 불변 객체


불변 객체는 멀티스레딩 환경에서 레이스 컨디션(race condition)을 피하는 데 도움이 됩니다. 
반면 var로 선언된 객체는 값이 바뀔 수 있기 때문에 여러 스레드가 동시에 수정하려고 할 때 문제가 발생할 수 있습니다.

4. 성능 최적화

스위프트는 let을 사용할 때 불변성을 보장할 수 있기 때문에 더 많은 최적화를 할 수 있습니다. 
예를 들어, 컴파일러는 불변 변수에 대해 더 많은 최적화를 적용할 수 있습니다. 
이는 성능 향상에 기여할 수 있습니다.

5. 명확한 의도 전달

let을 사용하면 그 변수의 값이 변경되지 않을 것임을 명확히 알 수 있습니다. 
반면 var는 값이 변경될 가능성이 있음을 내포하고 있기 때문에, 
코드에서 그 변수를 추적하거나 변경되는 위치를 찾는 것이 어려워질 수 있습니다.

예시:
let maxScore = 100  // 이 값은 절대로 바뀌지 않음을 알 수 있음
var currentScore = 50  // currentScore는 변할 수 있다는 것을 알 수 있음

6. 불필요한 변경 방지

let을 사용하면 변수의 값이 실수로 변경되는 것을 방지할 수 있습니다. 예를 들어, 
실수로 값을 변경하려고 시도하면 컴파일러가 오류를 발생시켜 수정할 수 있도록 도와줍니다.

예시:
let radius = 10
radius = 15  // 오류: 'let'으로 선언된 변수는 변경할 수 없습니다.

7. 변수 변경이 필요하지 않은 경우 let을 사용

변수가 변할 필요가 없고, 값이 고정되어야 한다면 let을 사용해야 합니다. 
코드에서 어떤 값이 변하지 않음을 확신할 수 있는 경우 let을 사용하는 것이 더 좋습니다.

예시:
let pi = 3.14159  // 수학적으로 pi는 변경되지 않으므로 let을 사용

결론

let은 불변성을 제공하여 코드의 안정성, 예측 가능성, 가독성 등을 높여줍니다. 
let을 사용하면 코드가 더 안전하고, 의도가 명확해지며, 디버깅이 쉬워집니다. 
불변 변수를 사용하면 실수로 값이 변경되는 것을 방지할 수 있고, 성능 최적화에도 도움이 됩니다.

따라서, 값이 변경될 필요가 없다면 가능한 한 let을 사용하는 것이 좋은 습관입니다. 
var는 값을 변경할 필요가 있을 때만 사용해야 합니다.