no image
[IOS/Swift] Xcode 에서 Git 연결 및 .gitignore 설정하기
gitignore 파일을 먼저 생성한 후 깃 레파지토리와 연결할 예정이다. 다음 과정을 따라해보자 1️⃣ .gitignore 파일 생성하기 command +n 을 눌러 빈 파일을 생성해준다(밑으로 스크롤 하면 있음) .gitignore 라고 이름을 설정하면 경고창이 뜰텐데 이때 그냥 Use 를 눌러주면 된다 2️⃣ 설정에 맞는 gitignore 파일 만들기 다음 사이트로 이동하여 원하는 플랫폼, 언어 등 을 선택하여 프로젝트에 알맞은 파일을 생성한다. https://www.toptal.com/developers/gitignore gitignore.io Create useful .gitignore files for your project www.toptal.com 생성하기를 누를 후, 해당 코드를 복사하여..
2023.04.04
no image
[IOS/Swift] No 스토리보드 환경 구축하기(Xcode 14)
스토리보드 없이 코드로 UI를 작성해 보고자 한다. 다음 STEP을 따라해보며 노 스토리보드 환경을 구축해보자 1️⃣ Xcode 프로젝트 생성하기 2️⃣ 스토리보드 설정 인터페이스로 Storyboard를 선택한다(SwiftUI 아님) 3️⃣ Main.Storyboard 파일 삭제하기 Delete -> Move to Trash 4️⃣ info에서 Storyboard Name 삭제하기 Scene Configuration -> Application Session Role -> Item 0 -> Storyboard Name 을 선택하여 back 키를 눌러 삭제한다. 5️⃣ 빌드 후 Main storyboard file base name 삭제하기 테스트로 빌드를 한 번 해 본 후, info 상단에 있는 Main ..
2023.04.04
[Swift] UIProgressView와 AVAudioPlayer를 활용한 타이머 구현
Udemy 강의를 듣고 구현한 것을 정리.. 1️⃣ 카운트 다운을 위한 변수 생성 / UIProgressView 변수 생성 @IBOutlet weak var progressBar: UIProgressView! ... var countdown = 60 2️⃣ 타이머 객체 생성하기 / 오디오 객체 생성하기 var timer = Timer() var player = AVAudioPlayer(); 3️⃣ 버튼 클릭 시, 타이머 실행 guard !timer.isValid else {return} 은 타이머가 이미 실행중인 경우, 중복 실행을 방지하기 위해 해당 함수를 빠져나가도록 한다. 초기 카운트 다운 변수를 해당 버튼에 맞는 타임으로 재설정해준다. @IBAction func hardnessSelected(_ ..
2023.03.12
[Swift] 서브 스크립트(Subscripts)
📌 서브 스크립트(Subscripts) 클래스. 구조체, 열거형이 collection, list, sequence의 멤버 요소에 접근할 수 있는 단축키이다. 별도의 메소드 없이 인덱스로 값을 설정하고 조회하기 위해 서브스크립트 사용 someArray[index] -> Array 인스턴스 요소에 접근 someDictionary[key] -> Dictionary 인스턴스에 접근 1️⃣ 서브 스크립트 구문 인스턴스 이름 뒤 대괄호에 하나 이상의 값을 작성하여 타입의 인스턴스를 조회 가능하다. 읽기-쓰기 혹은 읽기 전용이 될 수 있음 getter와 setter를 통해 동작 subscript(index: Int) -> Int { get { // Return an appropriate subscript value ..
2023.02.13
[Swift] 메서드(Methods)
📌 메서드(Methods) 특정 타입과 연관된 함수 기존 objective-C와는 다르게 Swift에서는 클래스 뿐만 아니라, 구조체, 열거형 모두 메서드를 정의할 수 있다. 1️⃣ 인스턴스 매서드(Instance Methods) 특정 클래스, 구조체, 또는 열거형의 인스턴스에 속하는 함수 인스턴스 프로퍼티에 접근하고 수정하는 방법 제공 class Counter { var count = 0 func increment() { count += 1 } func increment(by amount: Int) { count += amount } func reset() { count = 0 } } 위의 Counter 클래스는 3개의 인스턴스 매서드를 갖고 있다. increment() : 1씩 카운터 증가 incre..
2023.02.13
[Swift] 구조체와 클래스의 프로퍼티(Properties)
구조체와 클래스에서 프로퍼티의 저장은 각각 다르게 적용된다. 하나씩 살펴보자 📌 저장된 프로퍼티(Stored Properties) var (변수) 혹은 let(상수)를 통해 저장된 프로퍼티를 쓸 수 있다. 1️⃣ 구조체에서의 저장 프로퍼티 첫번째 요소와 길이 범위를 설정하는 프로퍼티를 갖고 있는 FixedLengthRange 구조체가 있다. FixedLengthRange 의 인스턴스는 1. firstValue 라는 저장된 프로퍼티 변수가 있으며 2. length 라는 저장된 프로퍼티 상수를 가진다. length 는 새 범위가 생성될 때 초기화되며 프로퍼티 상수 이므로 변경할 수 없다. struct FixedLengthRange { var firstValue: Int let length: Int } 생성한..
2023.02.13
no image
[Swift] 구조체와 클래스(Structures and Classes)
📌 구조체와 클래스 차이 구조체 클래스 공통점 1. 값을 저장할 수 있다. 2. 메소드를 정의할 수 있다. 3. 서브 스크립트 정의를 통해 값에 접근 가능 4. 초기화 상태 정의 가능 5. 기능적 확장 가능 6. 표준 프로토콜 준수 차이점 1. 타입을 추론하기 쉬움 2. struct 키워드로 시작 3. 값 타입(value Types) 임 1. class 키워드로 시작 2. 상속 사용 가능(한 클래스가 다른 클래스의 특성 상속 가능) 3. 타입 캐스팅을 사용하면 런타임 시에 클래스 인스턴스 확인 가능 4. 초기화 해제 구문 -> 클래스 인스턴스가 할당된 리소스 해제 가능 5. 자동 참조 카운팅을 통해 하나 이상의 인스턴스 참조를 허락함 6. 참조 타입(Reference Types) 임 단점 ) 복잡성 증가..
2023.02.08
[Swift] 열거형(Enumerations)
열거형 구문 enum 키워드 사용하여 중괄호 안에 모든 정의를 함 case 키워드를 사용하여 열거형 나열 1️⃣ 열거형 선언 enum CompassPoint { case north case south case east case west } 🔎 값 할당 및 접근 CompassPoint 이름을 통해 한번 값을 할당하면, 그 이후에는 이름을 생략해도 된다. var direction = CompassPoint.west direction = .east 2️⃣ switch 구문 사용 열거형 케이스에 대하여 switch 구문을 사용할 때, 모든 케이스에 대하여 조건이 명시되어 있어야 한다. direction = .south switch direction = { case .north: print("Lots of pla..
2023.01.16
320x100
728x90

 

gitignore 파일을 먼저 생성한 후 깃 레파지토리와 연결할 예정이다. 

다음 과정을 따라해보자 

 

 

 

 

1️⃣  .gitignore 파일 생성하기 

 

 

command +n 을 눌러 빈 파일을 생성해준다(밑으로 스크롤 하면 있음)

 

 

 

.gitignore 라고 이름을 설정하면 경고창이 뜰텐데 이때 그냥 Use 를 눌러주면 된다 

 

 

 

 

 

2️⃣ 설정에 맞는 gitignore 파일 만들기 

 

 

 

다음 사이트로 이동하여 원하는 플랫폼, 언어 등 을 선택하여 프로젝트에 알맞은 파일을 생성한다. 

https://www.toptal.com/developers/gitignore

 

gitignore.io

Create useful .gitignore files for your project

www.toptal.com

 

 

 

 

생성하기를 누를 후, 해당 코드를 복사하여 Xcode 에서 생성한 파일에 붙여넣기 한다. 

 

 

 

 

 

3️⃣ Git Repository 생성하기 

 

 

[Source Control] -> [New Git Reposotory] 를 눌러 해당 프로젝트를 Create  해 준다.

하고 나면 다음과 같이 Source Control에 기능들이 활성화 되어 있는 것을 볼 수 있다. 

 

 

 

 

4️⃣ 기존 Repository와 연결하기 

 

 

파일 아이콘 옆에 있는 두번째 탭(Show the Source Control Navigator)에 들어가 다음과 같이 Add Existing Remote 를 클릭한 후, 기존에 존재하는 git repository 주소를 복사한 뒤 넣어준다. 

 

 

 

 

 

5️⃣ push 하기 

 

 

다시 Source Control 로 가서 커밋 후 푸시 혹은 변경 내용이 없다면 바로 Push를 해주면 

원격 연결된 git repository로 연결된다. 

 

 

 

 

 

추가) .gitattributes 파일 추가하기

https://isjang98.github.io/blog/Merge-conflicts-project-pbxproj

 

[Xcode] Merge conflicts project.pbxproj

[Xcode] Merge conflicts project.pbxproj Merge conflicts project.pbxproj project.pbxproj 파일은 Xcode 구성 Bundle을 가지고 있는 중요한 파일이다. Project에 Link된 파일과 Framework, Build Setting 등을 유지하도록 하므로 .gitign

isjang98.github.io

 

 

project.pbxproj 파일로 인해 merge 할 때 Conflicts가 발생할 수 있다.

위와 같이 .gitattributes 파일을 추가해 다음 코드를 추가한다. 

 *.pbxproj binary merge=union

 

 

 

728x90
반응형
320x100
728x90

 

 

스토리보드 없이 코드로 UI를 작성해 보고자 한다. 

다음 STEP을 따라해보며 노 스토리보드 환경을 구축해보자 

 

 

 

 

 

1️⃣ Xcode 프로젝트 생성하기 

 

 

 

 

 

 

 

 

2️⃣ 스토리보드 설정 

 

 

인터페이스로 Storyboard를 선택한다(SwiftUI 아님) 

 

 

 

 

 

 

3️⃣ Main.Storyboard 파일 삭제하기

 

 

 

Delete -> Move to Trash 

 

 

 

 

 

 

4️⃣ info에서 Storyboard Name 삭제하기

 

 

 

Scene Configuration -> Application Session Role -> Item 0 -> Storyboard Name 을 선택하여 back 키를 눌러 삭제한다.

 

 

 

 

 

 

5️⃣ 빌드 후 Main storyboard file base name 삭제하기

 

 

 

테스트로 빌드를 한 번 해 본 후, info 상단에 있는 Main storyboard file base name 을 삭제해 준다. 

 

 

 

 

 

 

6️⃣ SceneDelegate.Swift 파일 수정하기 

 

 

 

 

 

다음 코드를 복사하여 위 사진과 같이 수정해 준다. 

 

guard let windowScene = (scene as? UIWindowScene) else { return }

let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
self.window = window
window.makeKeyAndVisible()

 

 

 

 

7️⃣  AppDelegate.Swift 파일 수정하기 

 

 

 

 

다음 코드를 추가한다. 

 

// 1. 클래스 안, 함수 위에 선언 
var window: UIWindow?

// 2. func 안에 다음 코드 추가 
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()

 

 

 

 

8️⃣  ViewController.Swift 파일에서 다음 코드를 추가한다. 

 

 

 

그냥 실행해보면 검은색 배경으로 뜨는 것을 볼 수 있다. 배경을 흰색으로 바꾸기 위해 다음과 같이 코드를 추가해 준다. 

 

 

 

배경 바꾸기)

self.view.backgroundColor = .white

 

 

 

 

 

 

 

 

 

참고 : 링크

728x90
반응형
320x100
728x90

 

Udemy 강의를 듣고 구현한 것을 정리..

 

 

 

 

1️⃣  카운트 다운을 위한 변수 생성 / UIProgressView 변수 생성

 

@IBOutlet weak var progressBar: UIProgressView!

...
var countdown = 60

 

 

 

 

2️⃣  타이머 객체 생성하기 / 오디오 객체 생성하기 

 

var timer = Timer()
var player = AVAudioPlayer();

 

 

 

 

3️⃣  버튼 클릭 시, 타이머 실행

 

 

guard !timer.isValid else {return} 은 타이머가 이미 실행중인 경우, 중복 실행을 방지하기 위해

해당 함수를 빠져나가도록 한다. 

 

초기 카운트 다운 변수를 해당 버튼에 맞는 타임으로 재설정해준다. 

 

@IBAction func hardnessSelected(_ sender: UIButton) {
        let hardness = sender.currentTitle!
        progressBar.progress = 0.0       // 프로그래스 바 진행 상황
        countdown = eggTimes[hardness]!  // 버튼 별 카운트 시간 
        
        
        
        guard !timer.isValid else { return }
                
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, 
        selector: #selector(timerAction), userInfo: nil, repeats: true)
        
    }

 

 

 

🔎 Timer.scheduledTimer 의 매개 변수들에 대해 더 자세히 알아보자 

 

  • timeInterval: 타이머 간격(초 단위)
  • target: 타이머가 실행될 객체(self)

  • selector: 타이머가 실행될 함수(#selector(실행함수이름))

  • userInfo: 타이머와 함께 전달할 추가 정보(nil)

  • repeats: 타이머 반복 여부(true/false)

 

 

 

 

4️⃣  타이머가 실행될 함수 구현

 

 

로직을 살펴보면 다음과 같다. 

 

timerAction() 

1) countdown 변수 값을 1씩 감소시킨다 

2) 0이 되면 타이머 종료 함수를 실행한다.

3) 진행 상황에 따른 비율로 프로그래스 바를 채운다. 

 

@objc func timerAction() {
		// 1. 시간에 따른 프로그래스 바 진행 상황 업데이트 
        progressBar.progress += 1/Float(countdown)
        
        // 2. 카운트다운 관련 
        if countdown > 0{
            print(countdown)
            countdown -= 1
        }else{
            stopTimer()
        }
    }

 

 

@objcObjective-C와의 상호 운용성을 위한 어노테이션이다. Objective-C에서는 메소드와 변수의 이름을 문자열로 전달하는 방식을 사용하기 때문에, Swift에서도 Objective-C와의 상호 운용성을 위해 @objc 어노테이션을 사용하여 Objective-C에서 호출 가능한 함수로 만들 수 있다. 

 

 

즉, @objc는 timerAction 함수를 Objective-C에서 호출 가능한 함수로 만들기 위해 사용됨

#selector 또한,  Objective-C와의 상호 운용성을 위한 문법으로, @objc 어노테이션을 사용한 함수를 선택할 수 있게 해줍니다.

 

 

 

 

 

5️⃣  타이머 종료 함수 구현( 종료 시, 종료 알림음 실행)

 

 

invalidate() 을 통해 타이머를 종료시키고, 프로그래스 바 값 또한 0으로 초기화 시킨다. 

 

func stopTimer() {
	// 1. 타이머 종료 
    timer.invalidate()  
    
    // 2. 종료 텍스트 띄우기 
    titleLabel.text = "Done!"
    
    // 3. 프로그래스 바 리셋 
    progressBar.progress = 0
	
    // 4. 오디오 실행 
    let url = Bundle.main.url(forResource: "alarm_sound", withExtension: "mp3")
    player = try! AVAudioPlayer(contentsOf: url!)
    player.play()
}

 

 

 

🔎 오디오 실행 코드를 더 자세히 알아보자

 

  1. Bundle.main.url(forResource: "alarm_sound", withExtension: "mp3") 코드는 "alarm_sound.mp3"라는 파일이 앱 번들에 있는지 확인하고 그 파일의 경로(URL)를 반환한다. (Bundle.main은 현재 앱 번들을 의미)
  2. 반환된 URL을 AVAudioPlayer 객체의 contentsOf 메서드에 전달하여 player라는 이름의 AVAudioPlayer 객체를 생성함. 이때 try! 키워드는 오류가 발생하면 무시하도록 함. 

  3. player.play() 코드는 생성된 player 객체를 재생함. 

 

 

 

 

전체 코드 

더보기
import UIKit
import AVFoundation
class ViewController: UIViewController {
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var titleLabel: UILabel!
    
    let eggTimes = ["Soft": 3, "Medium": 4, "Hard": 7]
    var countdown = 60
    var timer = Timer()
    var player = AVAudioPlayer();
    
    override func viewDidLoad() {
            super.viewDidLoad()
        progressBar.progress = 0
        }
    
    
    @objc func timerAction() {
        progressBar.progress += 1/Float(countdown)
        if countdown > 0{
            print(countdown)
            countdown -= 1
        }else{
            stopTimer()
        }
        }

        func stopTimer() {
            timer.invalidate()
            titleLabel.text = "Done!"
            progressBar.progress = 0
            
            let url = Bundle.main.url(forResource: "alarm_sound", withExtension: "mp3")
            player = try! AVAudioPlayer(contentsOf: url!)
            player.play()
        }
    
    
    @IBAction func hardnessSelected(_ sender: UIButton) {
        let hardness = sender.currentTitle!
        countdown = eggTimes[hardness]!
        
        guard !timer.isValid else { return }
                
                timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
            }
        
        }

 

 

 

 

 

728x90
반응형
320x100
728x90

 

 

📌 서브 스크립트(Subscripts)

 

 

클래스. 구조체, 열거형이 collection, list, sequence의 멤버 요소에 접근할 수 있는 단축키이다. 

별도의 메소드 없이 인덱스로 값을 설정하고 조회하기 위해 서브스크립트 사용

 

someArray[index] -> Array 인스턴스 요소에 접근

someDictionary[key] -> Dictionary 인스턴스에 접근

 

 

 

 

1️⃣ 서브 스크립트 구문

 

 

인스턴스 이름 뒤 대괄호에 하나 이상의 값을 작성하여 타입의 인스턴스를 조회 가능하다. 

읽기-쓰기 혹은 읽기 전용이 될 수 있음

 

gettersetter를 통해 동작

 

subscript(index: Int) -> Int {
    get {
        // Return an appropriate subscript value here.
    }
    set(newValue) {
        // Perform a suitable setting action here.
    }
}

 

 

정수의 n-배-테이블을 표시하기 위한 TimesTable 구조체이다. 

인덱스가 하나 증가할수록, n배 곱한 값을 반환한다. 

 

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

 

 

사용해보자. 3배 테이블을 생성하기 위한 threeTimesTable 상수에 인스턴스를 생성한다. 

6번째 인덱스에는 3 * 6 = 18 값이 들어 있다. 

 

let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"

 

 


 

 

2️⃣ 서브 스크립트 옵션

 

 

  • 여러개의 입력 파라미터 가질 수 있음
  • 입력 파라미터는 어떤 타입이든 가능
  • 또한 어떤 타입도 반환 가능 

 

 

 

✅ 타입 서브 스크립트 

 

 

타입 메서드와 동일하게 subscript 앞에 static 키워드 혹은 클래스 일 때는 class 키워드와 함께 사용 가능하다. 

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}

 

 

let mars = Planet[4]
print(mars)
//mars 출력

 

728x90
반응형

[Swift] 메서드(Methods)

얘리밍
|2023. 2. 13. 15:33
320x100
728x90

 

 

📌 메서드(Methods)

 

특정 타입과 연관된 함수

기존 objective-C와는 다르게 Swift에서는 클래스 뿐만 아니라, 구조체, 열거형 모두 메서드를 정의할 수 있다. 

 

 

 

 

1️⃣ 인스턴스 매서드(Instance Methods)

 

 

특정 클래스, 구조체, 또는 열거형의 인스턴스에 속하는 함수

인스턴스 프로퍼티에 접근하고 수정하는 방법 제공 

 

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

 

 

위의 Counter 클래스는 3개의 인스턴스 매서드를 갖고 있다. 

  • increment() : 1씩 카운터 증가
  • increment(by: Int) : 특정 정수 크기만큼 카운터 증가
  • reset() : 카운터를 0으로 재설정 

 

 

counter 프로퍼티 변수를 선언하여 Counter 클래스 인스턴스를 만들어보자😃

 

인스턴스 매서드를 호출하려면 점 구문(.)으로 접근하면 된다.

let counter = Counter()
//초기는 0

counter.increment()
// 1 증가 

counter.increment(by: 5)
// 5 증가 -> 1 + 5 결과는 6

counter.reset()
// 0으로 리셋

 


 

 

2️⃣ self 프로퍼티 

 

 

타입 인스턴스는 인스턴스 자체와 일치할 경우, self 라는 암시적 프로퍼티를 사용해 나타낼 수 있다. 

현재 인스턴스의 프로퍼티나 혹은 매서드를 참조할 때는 self를 명시적으로 작성하지 않아도 된다. 

 

func increment() {
    self.count += 1
}

 

 

🔎 그렇다면 self를 붙여야 할 때는??

 

   👉 인스턴스 매서드에서 파라미터 명프로퍼티 명일치할 때이다.

 

 

 

동일한 이름이 사용될 때, 파라미터 명이 더 우선시 되고, 프로퍼티를 참조하기 위해서는 self를 붙여 접근한다. 

 

 

아래 isToTheRightOf 인스턴스 매서드의 파라미터 명은 x 이고, 프로퍼티 중 x 또한 존재한다. 

이를 분리하기 위해 self 프로퍼티를 사용한다. 

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
}

 

 

 

예시를 보자 

 

 

somePoint 상수를 통해 Point 인스턴스를 생성한다. 

isToTheRightOf의 매서드 파라미터는 1.0으로 주어져 있다. 

self.x의 값은 4.0 파라미터 x의 값은 1.0으로 참이 되어 print 문이 출력된다. 

 

let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
    print("This point is to the right of the line where x == 1.0")
}


// This point is to the right of the line where x == 1.0 출력

 


 

 

3️⃣ 인스턴스 매서드 내에서 값 타입 수정

 

 

구조체열거형값 타입. 

하지만 값 타입 프로퍼티는 인스턴스 매서드 내에서 수정될 수 없다

 

 

 

🔎 만약 수정이 필요하다면 어떻게??

 

👉 mutating 키워드를 붙여 메서드에 대한 동작을 변경하도록 함.

mutating을 붙이지 않고 func mobeBy에서 x값 y값을 변경하려고 하면 에러가 난다. 

 

 

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

 

 

 

그럼 테스트를 통해 값을 확인해보자 

 

var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"

 

 

x값 1.0 y값 1.0으로 설정된 Point 인스턴스를 변수 somePoint에 할당하였다. 

moveBy 메소드를 통해 x 는 2.0 y 에는 3.0을 저장해주면 

somePoint.x 는 3.0 = 1.0 + 2.0 이 somePoint.y 에는 4.0 = 1.0 + 3.0 값으로 수정된다. 

 

 

 

 

하지만 변수 할당이 아닌 상수로 인스턴스를 선언하였다면😮??

 

👉 상수에 대해서는 변경 메서드를 호출할 수 없기 때문에, moveBy 메소드를 호출했을 때 에러가 난다. 

 

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)   // 에러 발생

 

 


 

 

4️⃣ 변경 메서드 내에서 self 할당 

 

 

위에서 작성한 변경 메소드 moveBy는 self 프로퍼티를 사용하여 나타낼 수 있다. 

 

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

 

 

x와 y 값 자체가 변경되는 것이 아니라, moveBy 메서드는 x와 y 값이 설정된 새로운 위치로 새로운 구조체를 생성하는 것이다.  ➡️ 쉽게 말해 변경 메소드를 호출하면 새로운 구조체를 생성하여 그 곳의 x와 y 값을 가져오는 것이라 할 수 있다. 

 

 

 

열거형에서도 self 파라미터를 설정할 수 있다. 

 

enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}

 

 

var ovenLight = TriStateSwitch.low

ovenLight.next()
// high 출력
ovenLight.next()
// off 출력

 


 

 

5️⃣ 타입 메서드 (Type Methods)

 

 

타입 자체에서 호출되는 메서드이다. 

메서드의 func 키워드 전에 static 키워드를 작성하여 나타낸다. (static으로 작성 시, override 불가) 

클래스class 키워드 사용(하위 클래스가 해당 메서드의 수퍼클래스 구현 재정의 가능)

 

 

SomeClass에서 타입 메서드 생성 

class SomeClass {
    class func someTypeMethod() {
     
    }
}

 

 

호출하는 방법 

SomeClass.someTypeMethod()

 

점 구문으로 호출되지만, 인스턴스를 호출하는 것이 아닌, 타입으로 타입 메서드를 호출한다 (타입 자체를 참조한다는 뜻)

 

 

 

⭐ 정리

 

인스턴스 메서드와 타입 메서드의 가장 큰 차이는 인스턴스를 만들고 호출하는 가 아닌가 이다. 

 

class SomeClass {

    // 인스턴스 메소드
    func InstanceMethod() -> String {
        return "Instance 입니다!"
    }
    
    // 타입 메소드
    class func someTypeMethod() -> String{
        return "Type 입니다!"
    }
}

 

 

✅ 인스턴스 메서드 호출

var some = SomeClass()
print(some.InstanceMethod())
// Instance 입니다! 출력

 

 

타입 메서드 호출

// 인스턴스를 만들지 않고 바로 호출

print(someClass.someTypeMethod())
// Type 입니다! 출력

 

 

 

 

 

인스턴스 메서드와 타입 메서드에 대해 잘 정리하신 글을 참고 바란다.

자세한 내용은 링크 참고. 

https://jiseobkim.github.io/swift/2018/10/05/swift_basic-%ED%83%80%EC%9E%85-%EB%A9%94%EC%86%8C%EB%93%9C-&-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EB%A9%94%EC%86%8C%EB%93%9C

 

728x90
반응형
320x100
728x90

 

구조체와 클래스에서 프로퍼티의 저장은 각각 다르게 적용된다. 

하나씩 살펴보자

 

 

 

📌 저장된 프로퍼티(Stored Properties)

var (변수) 혹은 let(상수)를 통해 저장된 프로퍼티를 쓸 수 있다. 

 

 

 

 

 

1️⃣ 구조체에서의 저장 프로퍼티

 

 

첫번째 요소와 길이 범위를 설정하는 프로퍼티를 갖고 있는 FixedLengthRange 구조체가 있다. 

 

FixedLengthRange 의 인스턴스는

1. firstValue 라는 저장된 프로퍼티 변수가 있으며

2. length 라는 저장된 프로퍼티 상수를 가진다.

 

 

 

length 는 새 범위가 생성될 때 초기화되며 프로퍼티 상수 이므로 변경할 수 없다.

 

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

 

 

 

 

생성한 구조체를 사용해 보자.

구조체 초기화 구문을 통해 설정한 값들을 rangeOfItems 변수에 할당한 다음, 

firstValue 프로퍼티를 6으로 변경하였다. 

 

var rangeOfItems = FixedLengthRange(firstValue: 0, length: 3)
//0,1, and 2

rangeOfItems.firstValue = 6
// 6, 7, and 8

 

 

 

 

 

만약에 상수에 구조체를 할당하면 !

구조체의 프로퍼티가 변수로 선언되어 있어도, 인스턴스의 프로퍼티를 변경할 수 없다

 

let rangeOfItems = FixedLengthRange(firstValue: 0, length: 4)
// 상수로 선언한 뒤, 인스턴스 생성 

rangeItems.firstValue = 6
// rangeItems는 상수이므로 프로퍼티 수정 불가

 

 

 

왜 그럴까🤔??

 

👉 구조체값 타입이기 때문이다. 

 

 

 

값 타입의 인스턴스는 상수로 선언되면, 모든 프로퍼티에 대해 수정이 불가능하다. 

( let notChange = FixedLengthRange()   -> notChange의 프로퍼티 수정 불가)

 

 

 

✅ 하지만 클래스는 참조 타입이라 다르게 동작한다. 

클래스는 참조 타입의 인스턴스를 상수로 선언해도, 인스턴스의 프로퍼티 변수에 대해 변경이 가능하다. 

 

 


 

 

2️⃣ 늦은 저장 프로퍼티(Lazy Stored Properties)

 

 

  • 프로퍼티 선언 전에 lazy 수정자를 붙인다.
  • lazy가 붙은 프로퍼티는 처음 사용될 때 까지, 그 초기값이 계산되지 않는다.
  • 외보 요인에 의해 초기값이 달라질 때 or 초기값이 필요할 때까지 수행되면 안되는 작업이 있을 때 사용하면 유리 

 

 

 

 

주의📢

lazy를 사용할 때는 프로퍼티를 var 키워드를 사용하여 변수로 선언해야 함

➡️ 인스턴스 초기화가 완료된 이후에도 초기값이 없을 수 있기 때문이다. (상수는 초기화가 완료되기 전에 항상 값을 갖고 있어야 함 👉 즉, lazy로 선언할 수 없음)

 

 

 

class DataImporter {

    var filename = "data.txt"
    
    
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")

 

 


 

3️⃣ 계산된 프로퍼티 (Computed Properties)

 

 

값을 실질적으로 저장하는 것이 아니라, 다른 프로퍼티와 값을 간접적으로 조회하고 설정하는 getter와 setter를 제공함 

 

 

아래 예시에서 Point와 Size는 값을 저장하는 프로퍼티들을 갖고 있지만,

Rect 구조체는 center라는 계산된 프로퍼티를 가진다. Rect이 반환하는 x와 y 값의 포인트는 명시적인 값으로

저장되는 것이 아니라, origin과 size로 계산되어 결정된다. 

 

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}

 

 

 

해당 구조체를 사용해보자

 

var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
                  
let initialSquareCenter = square.center
// 사각형 중앙의 값은 (5.0, 5.0)

 

 

square라는 변수를 통해 새로운 Rect 변수를 생성하고 원점은 (0,0)으로 너비는 (10,10)으로 설정한다

 

 

 

 

👉 그렇다면 square.center 결과값은??

 

 

center는 getter를 호출하여 현재 프로퍼티 값을 조회한 다음( (0,0) , (10,10)) 사각형의 중심 값을 계산한다. 

그리고 Point 리턴값인 (5,5)를 결과로 반환한다. 

 

 

 

 

 

 

아예 center 프로퍼티의 값을 새롭게 설정했다면 어떻게 될까

중심점을 (15.0, 15.0)으로 설정하면 center의 setter가 호출되어 origin 프로퍼티에 저장된 x값과 y값을 변경하고

변경된 중점으로 사각형을 위치시킨다. 

 

square.center = Point(x: 15.0, y: 15.0)


print("square.origin 값은 x는 \(square.origin.x)  y는 \(square.origin.y)")
//  x는 10.0   y는 10.0

 


 

4️⃣ 짧은 Setter 선언

 

 

setter 설정값 이름을 정의하지 않았다면, newValue 라는 기본 이름이 사용된다. 

 

struct AlternativeRect {
    ...
    var center: Point {
        ...
        
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

 

 


 

 

5️⃣ 짧은 Getter 선언

 

 

기존 getter에서 return이 생략되었으며, 계산 식을 x, y 안에 포함한다. 

 

struct CompactRect {
    ...
    var center: Point {
    
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        
        ...
    }
}

 


 

6️⃣ 읽기 전용 계산된 프로퍼티

 

 

setter가 없고 getter 만 있는 것읽기 전용 계산 프로퍼티(아래 Cuboid 구조체에서 volume 프로퍼티에 해당)라 한다. 

 

 

  • 항상 값을 반환함
  • 점 구문으로 접근 가능
  • 다른 값은 설정 불가 (읽기 전용)
  • get 키워드와 중괄호를 삭제하여 간편하게 선언 가능 

 

 

 

 

주의📢

구조체 안 프로퍼티는 상수로 선언하면 안된다. 값이 고정되어 있지 않기 때문에, var 변수로 선언해야 하며

인스턴스 초기화 부분에서 한 번 설정 시 값이 변경될 수 없으므로 이 때 let 키워드를 사용한다. 

 

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}

 

 

 

값 접근 방법 -> 점 구문 사용 

 

let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)

print("fourByFiveByTwo 의 부피는 \(fourByFiveByTwo.volume)")
// Prints fourByFiveByTwo 의 부피는 40.0

 


 

7️⃣ 프로퍼티 관찰자(Property Observers)

 

 

  • 프로퍼티의 값이 변경되는 지 관찰하고 응답함
  • 새로운 값이 현재 값과 같더라도 호출
  • 상속에서 하위 클래스의 프로퍼티를 재정의하여 프로퍼티 관찰자를 추가함

 

 

 

 

프로퍼티 관찰자 정의 방법

  willSet didSet
특징 - 값이 저장되기 직전에 호출
- 새로운 프로퍼티 값이 전달됨
- 새로운 값이 저장되자 마자 호출
- 예전 프로퍼티 값을 포함한 상수 파라미터가 전달됨 

 

 

 

willSet과 didSet의 예시를 살펴보자

 

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("totalSteps는 \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("\(totalSteps - oldValue) 만큼의 값이 증가")
            }
        }
    }
}

 

 

willSet은 새로운 값을 위한 newTotalSteps 파라미터 명을 사용한다. 

didSet은 totalSteps 값이 업데이트 되고 난 후에 호출된다. 

오래된 값에 대해 oldValue 기본 이름을 사용하고, 새로운 값 totalSteps와 비교한다. 

 

 

let stepCounter = StepCounter()

stepCounter.totalSteps = 200
// totalSteps는 200
// 200 만큼의 값이 증가


stepCounter.totalSteps = 360
// totalSteps는 360
// 160 만큼의 값이 증가


stepCounter.totalSteps = 896
// totalSteps는 896
// 536 만큼의 값이 증가

 


 

8️⃣ 프로퍼티 래퍼(Property Wrappers)

 

 

프로퍼티가 저장되는 방법을 관리하는 코드와 프로퍼티를 정의하는 코드 사이에 분리 계층을 추가한다.

 

@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

 

TwelveOrLess 구조체는 래핑하는 값이 항상 12와 같거나 더 작은 숫자만을 가지도록 설정된 구조체이다. 

setter를 통해 12보다 작은 값을 설정하고, 설정된 값을 getter를 통해 반환한다. 

 

 

 

이렇게 설정한 프로퍼티 래퍼는 속성으로 사용가능하며, 프로퍼티 전에 래퍼의 이름을 작성하여 적용하면 된다. 

 

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

 

 

 

height와 width 프로퍼티는 항상 12이하의 값을 가지는 지 TwelveOrLess  프로퍼티 래퍼를 통해 확인한다.

 

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"

rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints "12"

 

 

rectangle의 높이(rectangle.height)의 초기값은 number = 0 으로 초기화 되어 있기 때문에 0이다.

이때, .height을 10으로 저장하면 12보다 작기 때문에 성공적으로 저장되지만,

24를 저장하면 12보다 크기 때문에 실질적으로 12가 저장된다. 

 

 

 

 

다르게 프로퍼티 래퍼 사용하는 방법

더보기

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

 


 

 

9️⃣ 프로퍼티 래퍼 초기값 설정하기

 

 

위 프로퍼티 래퍼는 초기값 지정이 불가능하다.

 

 

초기값을 설정하기 위해서는 다음과 같이  init() 을 통해 정의할 필요가 있다. 

아래의 SmallNumber 구조체는

init(), init(wrappedValue:), init(wrappedValue:maximum:) 의 3개의 초기화를 가지고 있다. 

 

@propertyWrapper
struct SmallNumber {

    private var maximum: Int
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }


    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
    
}

 

이를 사용해보자.

 

 

1. init() 사용하기

 

기존의 프로퍼티 래퍼를 사용하는 방법과 동일하게 프로퍼티 변수 앞에 선언해주면 된다. 

 

struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int
}

var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// 0 0

 

 

 

2. init(wrappedValue:) 사용하기

 

height와 width 값을 원하는 값으로 저장해주면 된다.

height 와 width 에 1을 저장해주면 SmallNumber의 인스턴스가 SmallNumber(wrappedValue: 1) 호출로 생성된다.

이로인해 wrappedValue 값으로 1을 사용하고 기본 최대값으로 12를 사용한다. 

 

struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}

var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// 1 1

 

 

 

 

3. init(wrappedValue:maximum:) 사용하기

init(wrappedValue:maximum:)  형태에 smallNumber 래퍼를 씌워 wrappedValue와 maximum 값을 설정하면 된다. 

 

struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// 2 3

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// 5 4

 

 

 

4. 각 프로퍼티에 서로 다른 초기화 적용하기

 

 

height에는 init(wrappedValue:) 초기화를 호출하고, width에는 init(wrappedValue:maximum:)를 호출해보자

width를 래핑한 인스턴스는 SmallNumber(wrappedValue: 2, maximum: 9) 를 호출한다. 

 

struct MixedRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber(maximum: 9) var width: Int = 2
}

var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// Prints "1"

mixedRectangle.height = 20
print(mixedRectangle.height)
// Prints "12"

 

 

 

 

 

 

728x90
반응형
320x100
728x90

 

📌 구조체와 클래스 차이

  구조체 클래스
공통점 1. 저장할 수 있다.

2. 메소드를 정의할 수 있다.

3. 서브 스크립트 정의를 통해 값에 접근 가능

4. 초기화 상태 정의 가능

5. 기능적 확장 가능 

6. 표준 프로토콜 준수 
차이점 1. 타입을 추론하기 쉬움

2. struct 키워드로 시작 

3. 값 타입(value Types) 임 
1. class 키워드로 시작 

2. 상속 사용 가능(한 클래스가 다른 클래스의 특성 상속 가능)

3. 타입 캐스팅을 사용하면 런타임 시에 클래스 인스턴스 확인 가능

4. 초기화 해제 구문 -> 클래스 인스턴스가 할당된 리소스 해제 가능

5. 자동 참조 카운팅을 통해 하나 이상의 인스턴스 참조를 허락함  

6. 참조 타입(Reference Types) 임

단점 ) 복잡성 증가 

 

 

 

 

구조체와 클래스 생성 정의 

 

1. 구조체와 클래스의 이름UpperCameCase를 사용한다(첫 시작 대문자)

2. 프로퍼티lowerCameCase를 따른다. (소문자 시작) 

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

 

 


 

1️⃣ 인스턴스 생성

 

let someResolution = Resolution()
let someVideoMode = VideoMode()

 


 

2️⃣ 프로퍼티 접근

 

Resolution의 width 프로퍼티에 접근하고 싶다면, 

인스턴스 뒤에 구분자(.)를 통해 접근하면 된다. 

print("너비는 \(someResolution.width)")
// Prints 너비는  0

 

VideoMode는 Resolution 구조체를 프로퍼티로 가지고 있다

VideoMode에서 Resolution 구조체의 width 서브 프로퍼티에 접근하려면 연쇄 접근하면 된다.

someVideoMode 인스턴스 -> resolution 프로퍼티 -> width 서브 프로퍼티 

print("someVideoMode의 너비 \(someVideoMode.resolution.width)")
// Prints someVideoMode의 너비 0

 

 


 

3️⃣ 구조체 멤버 초기화

 

구조체는 멤버별 초기화 구문을 다음과 같이 갖고 있지만, 클래스 인스턴스는 멤버별 초기화를 받지 않는다

let vga = Resolution(width: 640, height: 480)

 


 

4️⃣ 구조체의 값 복사 

 

hd 상수를 선언하여 Resolution 인스턴스를 초기화하여 설정한다음, 

cinema라는 변수에 hd 값을 할당한다. 

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

 

 

hd와 cinema는 현재는 같은 값을 가지지만, 둘은 완벽히 다른 인스턴스 이다. 

 

cinema의 width를 변경해보자.

cinema의 width가 2048으로 변경되어 출력될 것이다. 

cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
 // cinema is now 2048 pixels wide

 

 

하지만 기존의 hd의 width는 변경되지 않고 1920 기존 값을 유지한다. 

다음과 같이 둘은 완벽히 다른 인스턴스이다. 

 

 

 

 

 

 

열거형에서도 같이 동작된다. 

다음과 같은 열거형이 있으며 current와 remembered 변수 상수를 통해 값 할당과 복사를 했다고 하자.

// 열거형 선언 
enum CompassPoint {
    case north, south, east, west
    mutating func turnNorth() {
        self = .north
    }
}

var current = CompassPoint.west    //프로퍼티 할당
let remembered = currentDirection  // current 복사

 

 

이 후, current만 CompassPoint의 turnNorth 함수를 호출해본다. 

current.turnNorth()

 

 

아마 값이 north로 변경 되었을 것이다.

그렇다면 remembered는? 

 

 

 

 

이제 current의 값과 remembered의 값을 확인해보자.

print("current의 방향은 \(currentDirection)")
print("remembered의 방향은 \(rememberedDirection)")
// Prints current의 방향은north
// Prints remembered의 방향은 west

remembered는 current의 복사본으로 current의 값이 변경되어도 복사본에는 영향을 주지 않기 때문에

기존 할당값인 west로 유지된다. 

 

 

 

이를 통해 구조체열거형값 타입임을 알 수 있다. 

값 타입 (value type) 은 변수 또는 상수에 할당될 때나 함수에 전달될 때 복사 되는 값인 타입

 

 

 


 

 

 

 

 

🤔 그렇다면 클래스는?

 

 

클래스참조 타입(Reference Types)

참조 타입 (reference types) 은 변수 또는 상수에 할당될 때나 함수로 전달될 때 복사되지 않음

 

 

 

위 VideMode 클래스를 다시 살펴보자 

class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

 

 

그리고 tenEighty 상수에 VideoMode 인스턴스를 할당하고 그 프로퍼티 속성 값을 다음과 같이 설정한다.

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

 

 

 

그리고 또 다른 상수 alsoTenEighty에 tenEighty를 할당해보자

그리고 나서 alsoTenEighty의 frameRate 값을 30.0으로 바꾸면 tenEighty의 frameRate 프로퍼티는 어떻게 될까??

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

 

 

 

🔎 결과)

print("tenEighty의 frameRate은 \(tenEighty.frameRate)")
// Prints tenEighty의 frameRate은 30.0

tenEighty.frameRate의 값 또한 30.0으로 변경된 것을 볼 수 있다. 

 

 

 

왜😮??

tenEighty와 alsoTenEighty는 같은 VideoMode 인스턴스를 참조하기 때문이다. 

 

 

 

클래스는 구조체와 열거형과는 다르게 값 저장이 아닌, 값 참조이기 때문 

 

 


 

 

5️⃣ 식별 연산자

 

  • 동일 인스턴스 (Identical to) (===)
  • 동일하지 않은 인스턴스 (Not identical to) (!==)
  •  
    동일 인스턴스는(3개의 등호로 표시, ===)  같음(equal to, 2개 등호, ==)과 같다는 말은 아니다. 
     
     
동일 인스턴스 (3개의 등호로 표시, ===)  래스 타입의 상수 또는 변수가 동일한 클래스 인스턴스를 참조함
같음(2개 등호, ==)  두 인스턴스 값이 동일하거나 동등함 

 

 

 

 

 

728x90
반응형
320x100
728x90

 

열거형 구문 

  • enum 키워드 사용하여 중괄호 안에 모든 정의를 함 
  • case 키워드를 사용하여 열거형 나열 

 

 

 

1️⃣ 열거형 선언

 

enum CompassPoint {
    case north
    case south
    case east
    case west
}

 

 

🔎 값 할당 및 접근 

 

CompassPoint 이름을 통해 한번 값을 할당하면, 그 이후에는 이름을 생략해도 된다. 

var direction = CompassPoint.west
direction = .east

 

 


 

 

 2️⃣ switch 구문 사용

 

열거형 케이스에 대하여 switch 구문을 사용할 때, 모든 케이스에 대하여 조건이 명시되어 있어야 한다. 

direction = .south
switch direction = {
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
}

//에러 발생

 

east와 west에 대한 케이스가 명시되어 있지 않으므로 컴파일 오류가 발생함. 

 

 

다 명시하는 것이 비효율적인 경우 default 케이스로 처리 

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
// Prints "Mostly harmless"

 

 


 

 

3️⃣ 열거형 Iterable 

 

열거형의 모든 케이스를 수집하려고 할 때, enum 뒤에 : CaseIterable을 작성한다. 

CaseIterable은 allCases 프로퍼티를 통해 모든 케이스에 대한 정보를 수집한다. 

enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let totalCount = Beverage.allCases.count
print("\(totalCount) 가능 음료")
// 3 가능 음료

 

 

🔎 반복문에서의 활용

for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice

 

 


 

4️⃣ 열거형 연관된 값 저장하기

 

UPC 바코드와 큐알 코드 바코드를 저장하는 Barcode 열거형

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

 

 

🔎 값 할당

var productBarcode = Barcode.upc(8, 83120, 45321, 3)

 

productBarcode에 qrCode 값 또한 할당할 수 있다.

productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

 

 


 

5️⃣ 원시값(Raw Values) 저장

 

모두 동일한 타입의 값으로 채우고 싶다면 다음과 같이 enum 변수 옆에 타입을 설정한다.

enum ASCIIChar: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

 

 

🔎 원시값의 암시적 할당

원시값이 저장된 열거형은 자동적으로 값이 할당된다. 

 

 

 

정수 원시값인 경우 

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

// 이 때, 첫번째 케이스 값 설정 안하면 초기값은 0임.
Planet.mercury의 값은 1
Planet.venus 는 자동으로 2가 됨

그 이후는 3,4,5 ...

 

 

 문자열 원시값 경우 

enum CompassPoint: String {
    case north, south, east, west
}

문자열은 문자 그대로의 값을 원시값으로 가진다. 

 

 

 

rawValue를 사용하여 원시값에 접근할 수도 있다.  

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

 

혹은 rawValue를 활용한 값 할당

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 에 7번째 값인 Planet.uranus 할당

 

 


 

6️⃣ 재귀 열거형 

 

열거형 케이스로 열거형 안에서 다른 인스턴스를 열거형으로 가지고 있는 것을 말한다. 

아래 예시를 보면, recursiveEnum에서 addtion의 케이스로 recursiveEnum의 인스턴스 중 number를 

열거형으로 갖고 있다. 

 

 

재귀 열거형을 사용하려면 indirect 키워드를 함께 써주면 된다. 

enum recursiveEnum {
    case number(Int)
    indirect case addition(recursiveEnum, recursiveEnum)
    indirect case multiplication(recursiveEnum, recursiveEnum)
}

 

let five = recursiveEnum.number(5)
let four = recursiveEnum.number(4)

// 재귀 열거형
let sum = recursiveEnum.addition(five, four)
let product = recursiveEnum.multiplication(sum, recursiveEnum.number(2))

 

 

전체를 모두 재귀 열거형으로 만들고 싶을 때 -> enum 시작 전indirect 작성 

indirect enum recursiveEnum {
    case number(Int)
    indirect case addition(recursiveEnum, recursiveEnum)
    indirect case multiplication(recursiveEnum, recursiveEnum)
}

 

 

이를 활용한 재귀 함수 

func evaluate(_ expression: recursiveEnum) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// Prints "18"

 

 

 

 

출처 : https://bbiguduk.gitbook.io/swift/language-guide-1/enumerations

728x90
반응형