<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>백점영점</title>
        <link>https://velog.io/</link>
        <description>일단 배운내용은 적어두기</description>
        <lastBuildDate>Fri, 04 Nov 2022 08:37:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>백점영점</title>
            <url>https://images.velog.io/images/high_sky8320/profile/2f5150d0-68e2-4a2b-82e0-05d38fe97748/KakaoTalk_Image_2022-03-18-15-41-58_002.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 백점영점. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/high_sky8320" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SwiftUI - State, Binding, EnvironmentObject, Published]]></title>
            <link>https://velog.io/@high_sky8320/SwiftUI-State-Binding-EnvironmentObject</link>
            <guid>https://velog.io/@high_sky8320/SwiftUI-State-Binding-EnvironmentObject</guid>
            <pubDate>Fri, 04 Nov 2022 08:37:09 GMT</pubDate>
            <description><![CDATA[<p>간략하게 정의만 작성 ( 추후 예제도 추가 예정 )
직접 사용해보면서 알게 된 점도 적어둘 예정입니다.</p>
<h3 id="state">@State</h3>
<p>@State는 상태를 나타내며, 이 상태가 변경되면 연결된 뷰를 다시 그린다.
따라서 변경된 값을 화면에 보여주어야 할 때 사용하며, 제일 많이 사용된다.</p>
<p>공식 사이트에도 잘 설명되어 있다.</p>
<blockquote>
<p><a href="https://developer.apple.com/documentation/swiftui/state/">https://developer.apple.com/documentation/swiftui/state/</a></p>
</blockquote>
<p>@State는 이렇게 작성하면 된다. private는 상황에 맞게.. 필수는 아니다!
예시로 간단하게 버튼을 탭했을 때 count가 올라가는 코드를 작성하면..
이렇게 작성할 수 있다. </p>
<pre><code class="language-swift">struct ContentView: View {
    @State private var clickEvent = 1

    var body: some View {
        HStack {
            Button(action: {
                clickEvent += 1
            }, label: {
                Text(&quot;-&quot;)
            })
            .foregroundColor(.gray)
            .frame(width: 30, height: 20, alignment: .center)
            .overlay(
                RoundedRectangle(cornerRadius: 0)
                    .stroke(.gray, lineWidth: 1)
            )

            Text(clickEvent.description)
             .foregroundColor(.black)
             .frame(width: 50, height: 30, alignment: .center)
             .overlay(
                 RoundedRectangle(cornerRadius: 0)
                     .stroke(.black, lineWidth: 1)
             )
             .padding()          
         }.padding(.trailing, 20)
    }
}</code></pre>
<p>버튼을 누를때마다 1을 추가하고 해당 값을 텍스트로 출력하는 간단한 예제이다.
( 바인딩해서 하는 예제는 추후 바인딩 작성할 때 추가하도록 하겠습니다 )</p>
<p>..만약 @State를 제외하면.. 뷰 내부에서 demodata의 값을 변경하는 코드가 있다면 Cannot use mutating member on immutable value: &#39;self&#39; is immutable 에러가 나게 될 것이다. </p>
<p>SwiftUI의 경우 뷰가 구조체로 이루어져있는데, 구조체의 경우 메소드 안의 값을 수정할 수 없기 때문에 값을 변경하게 된다면 에러가 나게된다.
때문에 @State를 이용하여 상태(값)이/가 변경되면 연결된 뷰를 다시 그리는 작업을 한다.
그럼 값을 수정할 수 있다. 위 예제처럼 말이다. </p>
<h3 id="binding">@Binding</h3>
<p>@Binding은 상위뷰의 @State를 하위뷰에 공유해야 할 때 하위뷰에서 사용한다.
사용할 때 $ 키워드를 사용한다.</p>
<blockquote>
<p>전달&quot;받을거면&quot; @Binding, 전달&quot;해줄거면&quot; @State</p>
</blockquote>
<h3 id="environmentobject">EnvironmentObject</h3>
<p>@EnvironmentObject는 하위뷰에 모두 공유해야 할 때 사용한다.
공식문서에 따르면 하위뷰에 ObservableObject을 제공한다고..하는데 잘 모르겠다.</p>
<p>검색해보니 사용자 인터페이스 밖에 있으며 앱 내의 SwiftUI 뷰 구조체의 하위 뷰에만 필요한 데이터는 Observable 오브젝트를 이용하고, 사용자 인터페이스 밖에 있으며 여러 뷰에서 접근해야 하는 데이터는 Environment 오브젝트를 활용한다고한다.</p>
<p>음.. 상위뷰에 데이터가 여러개면 Binding이 아니고 Environment를 사용한다는 걸까?
( 해당 부분은 더 공부하고 추가하겠습니다 )</p>
<h3 id="published">Published</h3>
<pre><code class="language-swift">@propertyWrapper struct Published&lt;Value&gt;</code></pre>
<p>@ObservableObject 프로토콜을 준수하는 것은 SwiftUI의 뷰들이 변경사항에 대해 관찰할 수 있다는 의미이다.</p>
<pre><code class="language-swift">class Box: ObservableObject {
    var item = [String]()
}</code></pre>
<p>하지만 유일한 프로퍼티가 @Published로 표시되지 않았기 때문에, 변경사항 알림이 전송되지 않을 것이다. 해서 이 클래스는 배열에 항목들을 자유롭게 추가할 수 있지만 뷰들은 업데이트 하지않고있다.</p>
<p>item에 무언가 추가되거나 제거될때마다 변경사항 알림을 보내고자하는 경우, 다음과 같이 @Published로 표시한다.</p>
<pre><code class="language-swift">class Box: ObservableObject {
    @Published var item = [String]()
}</code></pre>
<p>@Published property wrapper는 실제로 items에 willSet property observer를 추가하므로, 모든 변경사항은 자동으로 관찰자(observers)에게 전송된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI - 상단탭바 구현하기]]></title>
            <link>https://velog.io/@high_sky8320/SwiftUI-%EC%83%81%EB%8B%A8%ED%83%AD%EB%B0%94-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@high_sky8320/SwiftUI-%EC%83%81%EB%8B%A8%ED%83%AD%EB%B0%94-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 04 Nov 2022 08:10:20 GMT</pubDate>
            <description><![CDATA[<p>그림처럼 쇼핑몰앱의 상단탭바를 구현하고 싶었다..
UIKit에서는 그냥 Tabman 라이브러리를 사용하여 구현했기에 SwiftUI에서는 어떻게 구현할지 고민되었다. ( 코드로 구현해보고 싶었다. )</p>
<p>일단 <strong>변경된 값을 화면에 보여줘야 하므로</strong> @State를 사용해야 한다는건 알았는데 이후를 몰랐다.. 
<del>( 질문하기 전에 바보같이 Segmented Control이 있다는걸 잊고있었다. )</del></p>
<p>아무튼 상황과 이미지(코드,완성예시)를 적어서 질문을 했는데 두가지 답변이 왔다.</p>
<blockquote>
<ol>
<li>그냥 간단하게 Picker를 사용해서 구현해보세요.</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>@namespace 사용하시면 그림처럼 만들 수 있어요.</li>
</ol>
</blockquote>
<p>해서 둘 다 구현해보기로 했다!</p>
<h3 id="들어가기-전에">들어가기 전에..</h3>
<p>만약 위의 두가지 방법을 듣고 구현할 수 있다면 뒤로가기를 하시면 됩니다..ㅎㅎ</p>
<p>해당 코드가 정답은 아닙니다. 명심해주세요ㅠㅠ..
저는 이렇게 했지만 더 깔끔하게 구현할 수도 있습니다.. 저도 배우는 입장이니까요.</p>
<p>피드백 환영합니다. 코드 리뷰 완전 환영합니다.</p>
<h3 id="1-picker-사용하여-구현하기">1. Picker 사용하여 구현하기</h3>
<p>먼저, 첫번째로 답변을 받은 Picker로 상단탭바를 구현해보기로 했다.</p>
<p>제일먼저 Picker에 들어갈 항목을 만들었다.</p>
<blockquote>
<p>CaseIterable ( -&gt; case들을 배열처럼 순회할 수 있도록 하는 프로토콜 )
<a href="https://0urtrees.tistory.com/197">https://0urtrees.tistory.com/197</a></p>
</blockquote>
<pre><code class="language-swift">enum tapInfo : String, CaseIterable {
    case info = &quot;정보&quot;
    case size = &quot;사이즈&quot;
    case review = &quot;리뷰&quot;
    case call = &quot;문의&quot;
}</code></pre>
<p>배열로 구현해도 상관은 없다. 
하지만 ForEach에서 tapInfo.indices로 순회해야한다. 참고해주세요!</p>
<p>그 다음 ForEach를 사용하여 열거형에 있는 정보,사이즈,리뷰,문의가 모두 뜨도록 해주었다.</p>
<pre><code class="language-swift">Picker(&quot;Flavor&quot;, selection: $selectedPicker) {
    ForEach(tapInfo.allCases, id: \.self) {
        Text($0.rawValue)
    }
}
.pickerStyle(.segmented)
.padding()</code></pre>
<p><img src="https://velog.velcdn.com/images/high_sky8320/post/a73934e3-c883-4401-9172-f421c98d2136/image.png" alt=""></p>
<p>이후 뷰에 VStack을 주어 상단에는 Picker를 두고 하단에는 뷰를 두어 상단탭바처럼 보이게 했다.</p>
<pre><code class="language-swift">struct InfoView: View {

    @State private var selectedPicker: tapInfo = .info

    var body: some View {
        VStack {
            Picker(&quot;Pick&quot;, selection: $selectedPicker) {
                ForEach(tapInfo.allCases, id: \.self) {
                    Text($0.rawValue)
                }
            }
            .pickerStyle(.segmented)
            .padding()

            testView(tests: selectedPicker)
        }
    }
}</code></pre>
<p>여기서 testView는 Picker를 클릭했을때 그 Picker에 맞는 뷰를 띄워주어야한다.</p>
<pre><code class="language-swift">var tests : tapInfo</code></pre>
<p>해서 해당 코드를 추가하고 switch-case를 사용하여 맞는 뷰를 띄워주게끔 했다.</p>
<pre><code class="language-swift">switch tests {
    case .info:
        //Write UI here.
    case .size:
        //Write UI here.
    case .review:
        //Write UI here.
    case .call:
        //Write UI here.
}</code></pre>
<p>그럼 이렇게 상단탭바가 완성된다.
<img src="https://velog.velcdn.com/images/high_sky8320/post/69f4f8fc-52e7-4bed-a360-c174bfac1356/image.png" height="50px" width="200px"></p></p>
<p>전체코드
( 참고만 해주세요 )</p>
<pre><code class="language-swift">import SwiftUI

enum tapInfo : String, CaseIterable {
    case info = &quot;정보&quot;
    case size = &quot;사이즈&quot;
    case review = &quot;리뷰&quot;
    case call = &quot;문의&quot;
}

struct InfoView: View {

    @State private var selectedPicker: tapInfo = .info

    var body: some View {
        VStack {
            Picker(&quot;Pick&quot;, selection: $selectedPicker) {
                ForEach(tapInfo.allCases, id: \.self) {
                    Text($0.rawValue)
                }
            }
            .pickerStyle(.segmented)
            .padding()

            testView(tests: selectedPicker)
        }
    }
}

struct testView : View {

    var tests : tapInfo

    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            switch tests {
            case .info:
                ForEach(0..&lt;5) { _ in
                    Text(&quot;블랙컬러&quot;)
                        .padding()
                    Image(&quot;shoes&quot;)
                        .resizable()
                        .frame(maxWidth: 350, minHeight: 500)
                }
            case .size:
                Text(&quot;사이즈 참고해주세요&quot;)
                    .font(.system(size: 15, weight: .bold, design: .monospaced))
                    .frame(width: 300, height: 20, alignment: .center)
                Text(&quot;발폭 넓으신분 -&gt; 한사이즈 up!&quot;)
                    .padding()
            case .review:
                ScrollView(.horizontal, showsIndicators: false) {
                    ForEach(0..&lt;10) { _ in
                        LazyHStack {
                            ForEach(0..&lt;2) { _ in
                                NavigationLink(destination: ReviewView()){
                                    VStack(spacing: 5) {
                                        Image(&quot;shoes&quot;)
                                            .resizable()
                                            .frame(width: 160, height: 200, alignment: .center)
                                        Text(&quot;실착용 솔직 한달 후기 입니다&quot;)
                                            .font(.system(size: 15, weight: .bold, design: .monospaced))
                                            .frame(width: 160, height: 20, alignment: .leading)
                                            .foregroundColor(.black)
                                        Text(&quot;Sky Blue&quot;)
                                            .font(.system(size: 13, weight: .medium, design: .monospaced))
                                            .frame(width: 160, height: 20, alignment: .leading)
                                            .foregroundColor(.black)
                                        Text(&quot;평발인데 너무편해요 공간도 넉넉해서 걸을때 불편하지 않아요 최고입니다 ㅋㅋ 재구매의사 100%&quot;)
                                            .font(.system(size: 13, weight: .medium, design: .default))
                                            .frame(width: 160, height: 50, alignment: .leading)
                                            .foregroundColor(.black)
                                    }
                                    .padding(15)
                                }
                            }
                        }
                    }
                }
            case .call:
                VStack {
                    Text(&quot;별도의 커뮤니티를 운영하지 않습니다.&quot;)
                    Text(&quot;자세한 문의는 여기로 부탁드립니다&quot;)
                    Text(&quot;02-xxx-xxxx&quot;)
                        .padding()
                }.padding()
            }
        }
    }
}</code></pre>
<h3 id="2-namespace-사용하기">2. @namespace 사용하기</h3>
<p> 먼저 완성된 상단탭바는 이렇다.</p>
<p> <img src="https://velog.velcdn.com/images/high_sky8320/post/f356288e-46a1-4a71-91cc-ad71b3588326/image.png" alt=""></p>
<p>처음 namespace를 접했을때는 이해가 안갔는데 질문을 하고 답변을 받으니.. 이해가갔다.
시작하기 전에 앞서 사용했던 열거형과 @State는 그대로 사용한다.
( 어짜피 상단탭바에 들어갈 내용도 같고, 각각 맞는 뷰를 띄워줘야 하기 때문 )</p>
<p>대신 우리는 Picker와 다르게 namespace를 사용하여 만들 것이기 때문에
@Namespace를 추가해준다.</p>
<pre><code class="language-swift">enum tapInfo : String, CaseIterable {
    case info = &quot;정보&quot;
    case size = &quot;사이즈&quot;
    case review = &quot;리뷰&quot;
    case call = &quot;문의&quot;
}

struct InfoView: View {

    @State private var selectedPicker: tapInfo = .info
    @Namespace private var animation

    var body: some View {
        //MARK : Code here.
    }
}</code></pre>
<p>그 다음 사진과 같은 뷰를 만드려면 VStack으로 tapInfo에 있는 문자열을 정렬해주고
HStack으로 tapInfo의 문자열과 언더바를 정렬해야한다.
tapInfo에 있는 문자열을 정렬하는 것은 Picker때와 같이 ForEach를 돌려준다.</p>
<p>여기서 자연스러운 제스처를 위해 matchedGeometryEffect도 사용해준다.
( 구현해봤는데 정확하진 않지만 잘 작동되었기에.. 들어가는 id는 뭘하든 상관 없는 것 같다. )</p>
<pre><code class="language-swift">@ViewBuilder
private func animate() -&gt; some View {
    HStack {
        ForEach(tapInfo.allCases, id: \.self) { item in
            VStack {
                Text(item.rawValue)
                    .font(.title3)
                    .frame(maxWidth: .infinity/4, minHeight: 50)
                    .foregroundColor(selectedPicker == item ? .black : .gray)

                if selectedPicker == item {
                    Capsule()
                        .foregroundColor(.black)
                        .frame(height: 3)
                        .matchedGeometryEffect(id: &quot;info&quot;, in: animation)
                }

            }
            .onTapGesture {
                withAnimation(.easeInOut) {
                    self.selectedPicker = item
                }
            }
        }
    }
}</code></pre>
<p>그럼 사진처럼 탭바가 완성될 것이다.
필자는 ViewBuilder로 따로 빼서 구현했지만 그냥 구조체뷰에 만들어도 상관없다.
그냥 개인차이.. </p>
<p>뷰빌더를 간략하게나마 알고 싶다면 해당 포스트를 참고!</p>
<blockquote>
<p><a href="https://velog.io/@budlebee/SwiftUI-ViewBuilder">https://velog.io/@budlebee/SwiftUI-ViewBuilder</a></p>
</blockquote>
<p>그다음 만들었던 탭바와 뷰를 VStack으로 정렬시키면 완성이다.</p>
<pre><code class="language-swift">struct InfoView: View {

    @State private var selectedPicker: tapInfo = .info
    @Namespace private var animation

    var body: some View {
        VStack {
            animate()
            testView(tests: selectedPicker)
        }
    }
}</code></pre>
<p>완성 화면
<img src="https://velog.velcdn.com/images/high_sky8320/post/044bd7cd-dc32-430f-b736-382fc6be8218/image.png" height="50px" width="200px"></p></p>
<p>전체코드 
( 참고만 해주세요 )</p>
<pre><code class="language-swift">//InfoView.swift
enum tapInfo : String, CaseIterable {
    case info = &quot;정보&quot;
    case size = &quot;사이즈&quot;
    case review = &quot;리뷰&quot;
    case call = &quot;문의&quot;
}

struct InfoView: View {

    @State private var selectedPicker: tapInfo = .info
    @Namespace private var animation

    var body: some View {
        VStack {
            animate()
            testView(tests: selectedPicker)
        }
    }

    @ViewBuilder
    private func animate() -&gt; some View {
        HStack {
            ForEach(tapInfo.allCases, id: \.self) { item in
                VStack {
                    Text(item.rawValue)
                        .font(.title3)
                        .frame(maxWidth: .infinity/4, minHeight: 50)
                        .foregroundColor(selectedPicker == item ? .black : .gray)

                    if selectedPicker == item {
                        Capsule()
                            .foregroundColor(.black)
                            .frame(height: 3)
                            .matchedGeometryEffect(id: &quot;info&quot;, in: animation)
                    }

                }
                .onTapGesture {
                    withAnimation(.easeInOut) {
                        self.selectedPicker = item
                    }
                }
            }
        }
    }
}

struct testView : View {

    var tests : tapInfo

    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            switch tests {
            case .info:
                ForEach(0..&lt;5) { _ in
                    Text(&quot;블랙컬러&quot;)
                        .padding()
                    Image(&quot;shoes&quot;)
                        .resizable()
                        .frame(maxWidth: 350, minHeight: 500)
                }
            case .size:
                Text(&quot;사이즈 참고해주세요&quot;)
                    .font(.system(size: 15, weight: .bold, design: .monospaced))
                    .frame(width: 300, height: 20, alignment: .center)
                Text(&quot;발폭 넓으신분 -&gt; 한사이즈 up!&quot;)
                    .padding()
            case .review:
                ScrollView(.horizontal, showsIndicators: false) {
                    ForEach(0..&lt;10) { _ in
                        LazyHStack {
                            ForEach(0..&lt;2) { _ in
                                NavigationLink(destination: ReviewView()){
                                    VStack(spacing: 5) {
                                        Image(&quot;shoes&quot;)
                                            .resizable()
                                            .frame(width: 160, height: 200, alignment: .center)
                                        Text(&quot;실착용 솔직 한달 후기 입니다&quot;)
                                            .font(.system(size: 15, weight: .bold, design: .monospaced))
                                            .frame(width: 160, height: 20, alignment: .leading)
                                            .foregroundColor(.black)
                                        Text(&quot;Sky Blue&quot;)
                                            .font(.system(size: 13, weight: .medium, design: .monospaced))
                                            .frame(width: 160, height: 20, alignment: .leading)
                                            .foregroundColor(.black)
                                        Text(&quot;평발인데 너무편해요 공간도 넉넉해서 걸을때 불편하지 않아요 최고입니다 ㅋㅋ 재구매의사 100%&quot;)
                                            .font(.system(size: 13, weight: .medium, design: .default))
                                            .frame(width: 160, height: 50, alignment: .leading)
                                            .foregroundColor(.black)
                                    }
                                    .padding(15)
                                }
                            }
                        }
                    }
                }
            case .call:
                VStack {
                    Text(&quot;별도의 커뮤니티를 운영하지 않습니다.&quot;)
                    Text(&quot;자세한 문의는 여기로 부탁드립니다&quot;)
                    Text(&quot;02-xxx-xxxx&quot;)
                        .padding()
                }.padding()
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-12 타입 캐스팅]]></title>
            <link>https://velog.io/@high_sky8320/swift-12-%ED%83%80%EC%9E%85-%EC%BA%90%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@high_sky8320/swift-12-%ED%83%80%EC%9E%85-%EC%BA%90%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Wed, 12 Oct 2022 04:35:22 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 타입캐스팅 입니다.</p>
<p>본래 순서라면.. 초기화를 다하고 하려했지만
초기화의 기초는 익혔기 때문에 다음 개념을 먼저 공부하고자 타입캐스팅을 작성하게 되었습니다.</p>
<pre><code class="language-swift">class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}</code></pre>
<p>먼저 <code>MediaItem</code> 클래스와 그 클래스를 상속한 <code>Movie</code>와 <code>Song</code> 클래스가 있다.</p>
<pre><code class="language-swift">let library : [MediaItem] = [ 
    Movie(name: &quot;Casablanca&quot;, director: &quot;Michael Curtiz&quot;),
    Song(name: &quot;Blue Suede Shoes&quot;, artist: &quot;Elvis Presley&quot;),
    Movie(name: &quot;Citizen Kane&quot;, director: &quot;Orson Welles&quot;),
    Song(name: &quot;The One And Only&quot;, artist: &quot;Chesney Hawkes&quot;),
    Song(name: &quot;Never Gonna Give You Up&quot;, artist: &quot;Rick Astley&quot;)
]
// the type of &quot;library&quot; is inferred to be [MediaItem]</code></pre>
<p><code>library</code> 는 <code>Movie</code> 와 <code>Song</code>의 슈퍼 클래스가 <code>MediaItem</code>으로 동일하기에, 
<code>MediaItem</code>이란 클래스로 둘 다 업캐스팅한 것이다.</p>
<p>기본 타입으로 작업을 하려면 <strong>타입을 확인</strong>하거나 <strong>다른 타입으로 다운캐스트</strong> 해야한다.</p>
<h3 id="타입-확인-is">타입 확인 is</h3>
<p>인스턴스가 특정 하위 클래스 타입인지 확인하기 위해 타입 검사 연산자(<code>is</code>)를 사용한다.</p>
<p>리턴타입은 <code>Bool</code>이다.</p>
<pre><code class="language-swift">var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}

print(&quot;Media library contains \(movieCount) movies and \(songCount) songs&quot;)
// Prints &quot;Media library contains 2 movies and 3 songs&quot;</code></pre>
<p><code>library</code>에 저장된 <code>Movie</code>와 <code>Song</code>이 몇개인지 루프와 조건을 돌려 이를 출력한다.</p>
<h3 id="업캐스팅-as">업캐스팅 as</h3>
<p>업캐스팅은 서브 클래스의 인스턴스를 슈퍼 클래스의 타입으로 참조하는 것을 말한다.</p>
<p>해당 개념을 설명하기 위해 예제를 들자면,</p>
<pre><code class="language-swift">let theory = Movie.init(name: &quot;cats&quot;, director: &quot;Risk&quot;) as MediaItem
print(&quot;Movie : \(theory.name), dir : \(theory.director)&quot;)</code></pre>
<p>해당 <code>theory</code>를 프린트하면 <strong>Value of type &#39;MediaItem&#39; has no member &#39;director&#39;</strong> 에러가 뜬다.</p>
<p>위 코드는 <code>Movie</code>인스턴스를 생성하지만, 이를 <code>as</code>를 사용하여 <code>MediaItem</code>으로 업캐스팅해서 <code>theory</code> 
상수에 저장하겠다는 의미이기 때문이다. </p>
<p>좀 더 풀어서 설명하자면, <code>theory</code>는 <code>Movie</code>란 서브클래스를 <code>MediaItem</code>이란 슈퍼클래스로 업캐스팅을 했기 때문에, <code>theory</code>는 <code>MidiaItem</code>에 있는 멤버밖에 접근을 하지 못하게 된 것이다.</p>
<h3 id="다운캐스팅-as-as">다운캐스팅 as?, as!</h3>
<p>다운캐스팅은 슈퍼 클래스 인스턴스를 서브 클래스의 타입으로 참조한다. 
( 보통은 업캐스팅된 변수를 다시 서브클래스로 참조하고자 다운캐스팅을 쓴다고 함. )</p>
<p>다운 캐스트가 성공할지 확신이 없을 때 조건부 형식의 타입 캐스트 연산자 (<code>as?</code>)를 사용하고,
다운 캐스트가 항상 성공할 것이라는 확신이 있을 때만 강제 형식의 타입 캐스트 연산자 (<code>as!</code>)를 사용한다.</p>
<pre><code class="language-swift">for item in library {
    if let movie = item as? Movie {
        print(&quot;Movie: \(movie.name), dir. \(movie.director)&quot;)
    } else if let song = item as? Song {
        print(&quot;Song: \(song.name), by \(song.artist)&quot;)
    }
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley</code></pre>
<p>예시의 경우 <code>Movie</code>와 <code>Song</code> 두 가지 선택지가 있어, 각 항목에 사용할 실제 클래스를 미리 알지 못하므로,
<code>as?</code>를 사용하는 것이 적절하다.</p>
<h3 id="any--anyobject-타입캐스팅">Any &amp; AnyObject 타입캐스팅</h3>
<p><code>Any</code> 는 <strong>모든 타입</strong>의 인스턴스를 나타낼 수 있고, 
<code>AnyObject</code> 는 모든 <strong>클래스 타입</strong>의 인스턴스를 나타낼 수 있다.</p>
<pre><code class="language-swift">var things: [Any] = []

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append(&quot;hello&quot;)
things.append((3.0, 5.0))
things.append(Movie(name: &quot;Ghostbusters&quot;, director: &quot;Ivan Reitman&quot;))
things.append({ (name: String) -&gt; String in &quot;Hello, \(name)&quot; })</code></pre>
<p>as는 업캐스팅 말고도 패턴 매칭에도 사용되는데, 
switch 문을 활용해서, 해당 타입일 경우(캐스팅에 성공한 경우) case문이 실행될 수 있게 할 수 있다.</p>
<pre><code class="language-swift">for thing in things {
    switch thing {
    case 0 as Int:
        print(&quot;zero as an Int&quot;)
    case 0 as Double:
        print(&quot;zero as a Double&quot;)
    case let someInt as Int:
        print(&quot;an integer value of \(someInt)&quot;)
    case let someDouble as Double where someDouble &gt; 0:
        print(&quot;a positive double value of \(someDouble)&quot;)
    case is Double:
        print(&quot;some other double value that I don&#39;t want to print&quot;)
    case let someString as String:
        print(&quot;a string value of \&quot;\(someString)\&quot;&quot;)
    case let (x, y) as (Double, Double):
        print(&quot;an (x, y) point at \(x), \(y)&quot;)
    case let movie as Movie:
        print(&quot;a movie called \(movie.name), dir. \(movie.director)&quot;)
    case let stringConverter as (String) -&gt; String:
        print(stringConverter(&quot;Michael&quot;))
    default:
        print(&quot;something else&quot;)
    }
}

// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of &quot;hello&quot;
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael</code></pre>
<h3 id="any--anyobject">Any &amp; AnyObject</h3>
<p>Any와 AnyObject는 런타임 시점에 타입이 결정되기 때문에 컴파일 시점에는 해당 타입을 알 수 없다.
때문에 해당 타입의 멤버에 접근할 수 없다. 접근하려면 다운캐스팅을 사용하여야 한다.</p>
<p>제공하는 동작과 기능이 명시적으로 필요한 경우에만 Any 와 AnyObject 를 사용해야하고, 
코드에서 작업할 것으로 예상되는 타입에 대해 구체적으로 지정하는 것이 좋다.
( Swift가 타입에 민감한 언어라 그런 것도 있긴함. )</p>
<pre><code class="language-swift">if let movie = item as? Movie {
    movie.append(&quot;credit_&quot;)
}</code></pre>
<p>뭐.. 이런식으로..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-11 초기화]]></title>
            <link>https://velog.io/@high_sky8320/swift-11-%EC%B4%88%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@high_sky8320/swift-11-%EC%B4%88%EA%B8%B0%ED%99%94</guid>
            <pubDate>Thu, 06 Oct 2022 07:17:54 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 초기화 입니다.</p>
<p>초기화는 내용도 많고 난이도도 높은 것 같습니다..
일단 기초적인 내용은 다 추가하였습니다.
심화적인 부분? org 기준 후반부분은 추가가 필요합니다.</p>
<h3 id="정의">정의</h3>
<p>초기화 (Initialization)는 인스턴스의 클래스, 구조체, 또는 열거형을 사용하기 위해 준비하는 단계이다. </p>
<p>이 단계에는 인스턴스에 각 저장된 프로퍼티에 <strong>초기값을 설정</strong>하고, 새로운 인스턴스가 사용할 준비가 되기 전에 다른 설정이나 초기화를 수행하는 것을 포함한다.</p>
<h3 id="초기화-선언">초기화 선언</h3>
<pre><code class="language-swift">init() {
    // perform some initialization here
}</code></pre>
<p><strong>구조체 &amp; 클래스의 경우</strong> </p>
<ol>
<li>선언과 동시에 값을 주어 초기화를 하거나 </li>
<li>?(옵셔널)를 주어 초기화를 하거나</li>
<li>init(생성자)를 주어 초기화를 하는 방법이있다.</li>
</ol>
<p>해당 예시에서는 3번 방법을 사용하였다. ( 구조체 )</p>
<pre><code class="language-swift">struct Fahrenheit {
    var temperature: Double
    init() { 
        temperature = 32.0
    }
}

var f = Fahrenheit()
print(&quot;The default temperature is \(f.temperature)° Fahrenheit&quot;)
// Prints &quot;The default temperature is 32.0° Fahrenheit&quot;</code></pre>
<blockquote>
<p> 프로퍼티가 항상 같은 초기값을 갖는다면 초기화 구문 내에서 값을 설정하기보다 <strong>기본값을 제공하라</strong>
이것은 초기화 구문을 더 짧고, 명확하게 하고 기본값으로 부터 프로퍼티의 타입을 유추할 수 있다.</p>
</blockquote>
<p>초기화는 모든 프로퍼티가 값을 가져야만 초기화에 성공해서 인스턴스를 생성한다.</p>
<p>그런데 구조체는 프로퍼티의 초기화를 따로 지정하지 않을 경우, 초기화되지 않은 프로퍼티를 초기화할 수 있는 init 함수를 자동으로 제공하기 때문에 이렇게 할 수 있다. ( memberwise initializers )</p>
<pre><code class="language-swift">struct Fahrenheit {
    var temperature: Double
}

var f = Fahrenheit(temperature: 32.0)
print(&quot;The default temperature is \(f.temperature)° Fahrenheit&quot;)
// Prints &quot;The default temperature is 32.0° Fahrenheit&quot;</code></pre>
<p>하지만 클래스의 경우 memberwise initializers를 제공하지 않아서.. 초기값 지정을 구조체와는 좀 다르게 해줘야 한다.</p>
<p>크게 두가지로 나뉘는데, </p>
<p><code>Designated Initializers</code> (지정생성자) : 클래스의 모든 프로퍼티를 초기화 하는 생성자</p>
<p>지정 생성자의 경우 옵셔널 변수로 선언 되지 않았거나, 기본 값을 갖지 않는 프로퍼티가 있을 경우엔 생성자를 추가하여 초기값을 지정한다. 또한 상속받은 서브 클래스에서 지정 생성자를 작성할 경우, 반드시 슈퍼 클래스의 Initializers를 호출해주어야 한다.</p>
<pre><code class="language-swift">class Fahrenheit {
    let temperature : String

    init(temperature : String) {
        self.temperature = temperature
    }
}

class Temp : Fahrenheit {
    var feel : String

    init(feel : String) {
        self.feel = feel
        super.init(temperature : feel)
    }
}</code></pre>
<p><code>Convenience Initializers</code> (편의 생성자) : Designated Initializers를  도와주는 역할</p>
<p>Convenience Initializers는 보조 생성자라, 굳이 모든 프로퍼티를 초기화 할 필요가 없다.
허나, 만약 초기화가 안 된 프로퍼티가 있는 경우 지정 생성자가 모든 프로퍼티를 초기화 시켜서
최종적으로 같은 클래스 내에 있는 지정 생성자를 호출 시키기 때문에, 
<strong>아무리 보조 생정자를 호출해도 어짜피 마지막에 지정 생성자를 호출</strong>하기에 신경쓰지 않아도된다.</p>
<pre><code class="language-swift">class Fahrenheit {
    let temperature : String
    let feels : String

    init(temperature : String, feels : String) {
        self.temperature = temperature
        self.feels = feels
    }

    convenience init(temperature : String) {
        self.init(temperature: temperature, feels: &quot;cold&quot;)
    }
}</code></pre>
<p>예시로 설명하자면, Convenience 생성자가 먼저 실행되고 그 다음 최종적으로 Designated 생성자가 실행되는 순서인 것이다. 
그렇기 때문에 꼭 Designated 생성자가 생성되어 있어야 에러가 나지 않으니 주의!!</p>
<p>( 내용이 좀 많이 어렵다.. Convenience랑 Designated 이해하는데 시간 오래걸림.. )</p>
<h3 id="클래스의-2단계-초기화--클래스의-초기화-프로세스-">클래스의 2단계 초기화 ( 클래스의 초기화 프로세스 )</h3>
<p>2단계 초기화는 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 예기치 않게 다른 값을 설정하는 것을 막는다. 때문에 초기화를 보다 안전하게 진행하기 위해 2단계 초기화를 사용하는 것이다.</p>
<p>예시로 1단계와 2단계를 해설해보겠다.</p>
<pre><code class="language-swift">class Human {
    var name: String

    init(name: String) {
        self.name = name

        print(name)
    }
}

class Developer: Human {
    var language: String

    init(language: String) {
        self.language = language
        super.init(name: &quot;nothing&quot;)

        print(language)
    }
}

class Dtzero : Developer {
    var nickName: String

    init(nickName: String) {
        self.nickName = nickName
        super.init(language: &quot;swift&quot;)

        print(nickName)
    }
}</code></pre>
<p><strong>1단계 초기화</strong> </p>
<p> 위의 예시에서 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르게 되면</p>
<pre><code class="language-swift">Dtzero(nickName: &quot;well&quot;)</code></pre>
<p>Dtzero → Developer → Human 순서로 자신의 프로퍼티를 초기화하고 
<code>super.init</code>을 통해 부모 클래스까지 초기화 시키면 1단계 초기화는 끝이난다.</p>
<p><strong>2단계 초기화</strong></p>
<p>모든 프로퍼티가 기본 값을 가지게 되어 초기화가 완료 되었다면,
가장 마지막으로 호출됐던 제일 상위 클래스의 남은 작업부터 차례로 아래로 실행된다. ( 스택 )
해서 위의 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르면 이렇게 출력 된다. </p>
<pre><code class="language-swift">/*
nothing
swift
well
*/</code></pre>
<p><code>nothing</code>이 Human 클래스의 name이고, <code>swift</code>가 Developer 클래스의 language, <code>well</code>이 Dtzero 클래스의 nickName이다. ( 2차 초기화 과정에서 값들이 덮어 씌워진 것 )</p>
<h3 id="상속의-재정의">상속의 재정의</h3>
<p><code>Convenience</code>는 상속은 되지만 재정의가 불가능
자식 클래스에서 재정의 초기화 할 때 반드시 <code>super.init</code> 필수!</p>
<p>부모 클래스의 모든 지정 초기화를 상속받거나, 
부모 클래스의 모든 지정 초기화를 오버라이딩 해서 구현 했다면 
이땐 부모의 모든 편의 초기화 메서드가 상속된다.</p>
<h3 id="필수-초기화-구문">필수 초기화 구문</h3>
<p>클래스 초기화 구문 앞에 <code>required</code>를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함을 나타낸다. 추가로 지정된 필수 초기화 구문을 재정의 할 때 <code>override</code>를 작성하지 않는다.</p>
<pre><code class="language-swift">class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-10 상속]]></title>
            <link>https://velog.io/@high_sky8320/swift-10-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@high_sky8320/swift-10-%EC%83%81%EC%86%8D</guid>
            <pubDate>Wed, 05 Oct 2022 08:22:46 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 상속 입니다.</p>
<h3 id="정의">정의</h3>
<p>클래스는 다른 클래스에서 메서드, 프로퍼티, 그리고 다른 특성을 <code>상속 (inherit)</code>할 수 있다. 
클래스가 다른 클래스로 부터 상속될 때 상속하는 클래스를 <code>하위 클래스 (subclass)</code>라고 하고 
상속된 클래스를 <code>상위 클래스 (superclass)</code>라고 한다.</p>
<p>간단히 말하면 클래스를 상속받는 클래스는 <code>자식 클래스</code>고, 그 상속된 클래스는 <code>부모 클래스</code>이다.</p>
<blockquote>
<p>자식클래스 = 서브클래스 
부모클래스 = 슈퍼클래스</p>
</blockquote>
<p>공식에 이렇게 나와있으니 당연한거지만 구조체는 상속이 불가능하다.
상속도 단일 상속만 가능.. ( 후에 배울 프로토콜처럼 : 뒤에 여러개를 놓을 수 없다. )</p>
<h3 id="기본-클래스">기본 클래스</h3>
<p>앞서 클래스와 구조체에서 설명한 적이 있으니 간단하게 형태만..</p>
<pre><code class="language-swift">class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return &quot;traveling at \(currentSpeed) miles per hour&quot;
    }
    func makeNoise() {
        // do nothing - an arbitrary vehicle doesn&#39;t necessarily make a noise
    }
}

let someVehicle = Vehicle() //-&gt; 초기화 구문 으로 Vehicle 의 새로운 인스턴스를 생성

print(&quot;Vehicle: \(someVehicle.description)&quot;)
// Vehicle: traveling at 0.0 miles per hour</code></pre>
<h3 id="서브클래스-슈퍼클래스">서브클래스, 슈퍼클래스</h3>
<p>하위 클래스(Subclassing)는 기존 클래스를 기반으로 새로운 클래스를 만드는 작업이다. 
하위 클래스는 기존 클래스의 특성을 상속하므로 수정할 수 있으며, 새로운 특성도 추가할 수 있다.</p>
<p>먼저 위의 <code>Vehicle</code> 클래스를 상속한 <code>Bicycle</code> 클래스를 선언한다.</p>
<p>여기서 <code>Vehicle</code>은 <strong>슈퍼클래스</strong>, <code>Bicycle</code>은 <strong>서브클래스</strong>라고 부른다.</p>
<pre><code class="language-swift">class Bicycle: Vehicle {
    var hasBasket = false
}</code></pre>
<p>새로운 <code>Bicycle</code>클래스는 위에 있는 <code>Vehicle</code>의 모든 특성을 자동적으로 얻는다. 
추가로 이 자전거는 현재 바구니를 가지고 있지 않다. ( 새로운 저장 프로퍼티 <code>hasBasket</code> )</p>
<p>인스턴스가 생성된 후에 특정 <code>Bicycle</code>인스턴스의 <code>hasBasket</code>프로퍼티를 <code>true</code>로 설정할 수 있다.
또한 상속된 프로퍼티를 수정할 수 있고 조회할 수도 있다. ( currentSpeed와 description 프로퍼티 )</p>
<pre><code class="language-swift">let bicycle = Bicycle()
bicycle.hasBasket = true

bicycle.currentSpeed = 15.0
print(&quot;Bicycle: \(bicycle.description)&quot;)
// Bicycle: traveling at 15.0 miles per hour</code></pre>
<p>+) 하위 클래스는 그 자체로 하위 클래스화 될 수 있다. 그 클래스는 기존 클래스와 하위 클래스 프로퍼티를 모두 상속하게 되며, 또한 그 클래스에 새로운 프로퍼티를 추가할 수도 있다. 상속된 프로퍼티, 새로운 프로퍼티 모두 수정이 가능하고 조회도 가능하다. </p>
<h3 id="상속-재정의--오버라이딩-">상속 재정의 ( 오버라이딩 )</h3>
<p>부모 클래스에서 상속받은 것을 자식 클래스에서 재정의할 수 있는 것을 말한다.</p>
<p>오버라이딩은 인스턴스 메소드, 타입 메소드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 모두에 대해 
가능하며, 상속될 특성을 재정의 하려면 앞에 <code>override</code> 키워드를 붙여준다.</p>
<p>위의 정의를 간단하게 말하자면, 
내가 기존에 <code>클래스A</code>를 선언했는데 <code>클래스B</code>에 <code>클래스A</code>를 상속한 상황이고, 이후에 <code>클래스A</code>에 선언한 프로퍼티의 값들을 <code>클래스B</code>에서 변경하고 싶을 때 상속 재정의를 사용한다.</p>
<h4 id="메서드-오버라이딩">메서드 오버라이딩</h4>
<p>예제를 통해 상속 재정의에 대해 더 자세히 보자면</p>
<pre><code class="language-swift">class Human {
    func comment() {
        print(&quot;my name is dtzero&quot;)
    }
}</code></pre>
<p>이렇게 comment라는 메서드를 선언했는데</p>
<pre><code class="language-swift">class Me : Human {
    func comment() {
        print(&quot;my name is dotzer0&quot;)
    }
}</code></pre>
<p>이렇게 Human을 상속한 Me 클래스에서 값을 변경하려 한다면.. 이렇게 에러가 나게 된다.</p>
<blockquote>
<p>Overriding declaration requires an &#39;override&#39; keyword</p>
</blockquote>
<p>해당 에러를 고치기 위해 override를 사용하면 내 슈퍼클래스인 Human의 comment 함수를 
내 입맛에 맞춰서 재정의해서 쓰겠다는 의미가 된다.</p>
<p>그럼 출력할 때 변경된 값이 출력된다. </p>
<p>만약 변경 전과 변경 이후의 메세지 모두 출력하고 싶다면,
<code>super</code> 접두어를 사용하여 메서드, 프로퍼티, 또는 서브 스크립트의 상위 클래스 버전을 접근한다.</p>
<pre><code class="language-swift">class Me : Human {
    override func comment() {
        super.comment()
        print(&quot;my name is dotzer0&quot;)
    }
}</code></pre>
<p>그리고 해당 값을 출력하면.. 변경 전과 후의 값이 모두 출력된다.</p>
<pre><code class="language-swift">let prints: Me = .init()
prints.comment()

/*
my name is dtzero
my name is dotzer0
*/</code></pre>
<h4 id="프로퍼티-오버라이딩">프로퍼티 오버라이딩</h4>
<p>프로퍼티에 고유한 사용자 정의 <code>getter</code>와 <code>setter</code>를 제공하거나 기본 프로퍼티 값이 변경될 때 재정의한 프로퍼티가 관찰할 수 있도록 프로퍼티 관찰자를 추가 하기위해 상속된 인스턴스 또는 타입 프로퍼티를 재정의할 수 있다.
주의할 점은 프로퍼티 오버라이딩을 할 때 프로퍼티의 이름과 타입을 반드시 명시해야 한다는 것이다.</p>
<p>먼저 앞서 배웠던 프로퍼티에는 저장 / 연산 프로퍼티가 있었다.
저장 프로퍼티의 경우, 재정의를 할 때 <code>getter</code>와 <code>setter</code>를 모두 구현해줘야 한다.
<code>getter</code> 하나만 구현하는 것도 불가능하고 구현하지 않고 재정의도 불가능하다.</p>
<pre><code class="language-swift">class Human {
    var name : String = &quot;dtzer0&quot;
}

class Me : Human {
    var me : String = &quot;dotzero&quot;

    override var name: String {
        get {
            return self.me
        }
        set {
            self.me = name
        }
    }
}</code></pre>
<p>출력해보면 변경된 값이 알맞게 들어가있는걸 확인할 수 있다.</p>
<pre><code class="language-swift">var test1 : Human = .init()
print(test1.name)

var test2 : Me = .init()
print(test2.me)

/*
dtzer0
dotzero
*/</code></pre>
<p>연산 프로퍼티의 경우, 부모 클래스의 연산 프로퍼티가 <code>getter</code>로만 구현된 경우 서브 클래스에서 <code>getter</code>만 구현하거나 <code>setter</code> 를 추가로 구현하는 오버라이딩은 가능하지만,
만약 부모 클래스에서 둘 다 구현했다면 서브 클래스에서 둘 다 구현해줘야한다.</p>
<p>해당 사항말고 나머지는 저장 프로퍼티랑 같다.</p>
<h3 id="재정의-방지">재정의 방지</h3>
<p><code>final</code>을 통해 실수로 메서드, 프로퍼티, 또는 서브 스크립트를 재정의 하는 것을 방지할 수 있다.
final을 class 앞에 붙이게 되면 해당 클래스는 아무도 상속할 수 없게 된다.</p>
<p>상속을 시도할 경우, final 클래스를 선언한걸 상속했다고 에러가 난다.</p>
<pre><code class="language-swift">final class Human {
    var name: String?
    var age: Int?
}

class Me : Human {
    //Inheritance from a final class &#39;Human&#39;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-9 메서드]]></title>
            <link>https://velog.io/@high_sky8320/swift-9-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@high_sky8320/swift-9-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Wed, 05 Oct 2022 02:21:19 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 메서드 입니다.</p>
<p>swift.org에 프로퍼티 다음인 카테고리입니다.
프로퍼티 개념과 연계되므로 프로퍼티를 먼저 배우고 아는걸 추천합니다.
( 그래야 이해가 빨리 되더라구요 )</p>
<p>메서드의 개념을 요약해보자면 이렇다.</p>
<blockquote>
<p> <strong>클래스, 구조체, 열거형 속에 포함되어 있는 &quot;함수&quot;</strong></p>
</blockquote>
<p>어? 설마 그건가? 하면 맞을 수도 있습니다.
아마 개발 하실 때 많이 보셨을 수도 있음!!</p>
<h3 id="인스턴스-메서드">인스턴스 메서드</h3>
<pre><code class="language-swift">class Music {
    func kPop() {
        print(&quot;awesome&quot;)
    }
}</code></pre>
<p>아무런 수식어 없이 func으로 시작하는 메서드는 모두 인스턴스 메서드라고 한다.</p>
<p>해당 예제에서는 kPop 함수가 인스턴스 메서드인 것이다.</p>
<pre><code class="language-swift">let listen : Music = .init()
listen.kPop()</code></pre>
<p>호출은 이렇게 인스턴스를 생성한 다음 .을 이용하여 호출한다. </p>
<p>( 저장 프로퍼티때의 호출이랑 같은 방식! )</p>
<p>인스턴스 메서드는 인스턴스를 생성 하지 않으면 해당 메서드에 접근하여 값을 호출하는 것이 불가능하다.</p>
<h3 id="타입-메서드">타입 메서드</h3>
<p>메서드의 <code>func</code>키워드 전에 <code>static</code>키워드를 작성하여 타입 메서드를 나타낸다. </p>
<p>클래스는 대신 <code>class</code>키워드를 사용하여 하위 클래스가 해당 메서드의 수퍼 클래스 구현을 재정의 할 수 있다.</p>
<pre><code class="language-swift">class Music {
    static func kPop() {
        print(&quot;awesome&quot;)
    }

    class func pop() {
        print(&quot;perfect&quot;)
    }
}</code></pre>
<p>따라서 kPop(), pop() 모두 타입메서드이다.</p>
<pre><code class="language-swift">Music.kPop() //awesome
Music.pop() //perfect</code></pre>
<p>인스턴스 메서드와 다르게 타입메서드는 Music이라는 타입에만 연관되어 있으므로 이렇게 호출한다.</p>
<p>따라서 타입 메서드는 해당 메서드가 속해 있는 타입만 알면 호출이 가능하다.</p>
<h4 id="-한-class에-저장-프로퍼티와-저장-타입-프로퍼티가-같이-있을-경우-각-메서드의-멤버-접근-범위">+) 한 class에 저장 프로퍼티와 저장 타입 프로퍼티가 같이 있을 경우 각 메서드의 멤버 접근 범위?</h4>
<p>해당 개념은 위의 인스턴스 메서드와 타입 메서드의 호출방식을 이해했다면 이해하기 쉽다.</p>
<p>먼저 저장 프로퍼티와 저장 타입 프로퍼티를 생성한다.</p>
<pre><code class="language-swift">class Music {
    let kPop : String = &quot;Awesome&quot;
    static let pops : String = &quot;Perfect&quot;
}</code></pre>
<p>1️⃣ <code>타입 메서드</code>에서는 타입 멤버만 사용 가능하고, 같은 타입 멤버는 타입 이름 없이 접근 가능</p>
<pre><code class="language-swift">class Music {
    let kPop : String = &quot;Awesome&quot;
    static let pops : String = &quot;Perfect&quot;

    static func Player() {
        print(kPop) //Instance member &#39;kPop&#39; cannot be used on type &#39;Music&#39;
        print(pops)
    }
}</code></pre>
<p>저장 프로퍼티인 kPop은 인스턴스 멤버이기 때문에 호출할 때 인스턴스를 선언하고 호출해야 한다.
하지만 예시에서는 그렇게 하지 않고 저장 타입 프로퍼티처럼 타입 이름 없이 접근을 해버려서 에러가 난 것.</p>
<pre><code class="language-swift">class Music {
    let kPop : String = &quot;Awesome&quot;
    static let pops : String = &quot;Perfect&quot;

    static func Player() {
        let asd : Music = .init()
        print(asd.kPop)
        print(pops)
    }
}

print(Music.Player())
/*
Awesome
Perfect
()
*/</code></pre>
<p>→ 인스턴스를 선언하고 호출하면 에러가 사라지긴 하나, 이럴거면 2번 방법 사용하는게.. 더 나을것 같다.</p>
<p>결론적으로 타입 메서드는 &quot;인스턴스를 선언할 필요 없는&quot; 메서드 이기 때문에, 자기랑 똑같이 &quot;인스턴스를 생성할 필요 없는&quot; 타입 프로퍼티의 경우, &quot;같은 타입에 한해 타입 이름 없이도 접근&quot;이 가능하지만, 인스턴스가 생성 되어야만 저장 공간을 갖는 인스턴스 멤버(프로퍼티, 메서드)엔 접근할 수 없는 것이다.</p>
<p>2️⃣ <code>인스턴스 메서드</code>에서는 인스턴스 멤버를 사용할 수 있고, 타입 멤버도 타입만 알면 접근 가능</p>
<pre><code class="language-swift">class Music {
    let kPop : String = &quot;Awesome&quot;
    static let pops : String = &quot;Perfect&quot;

    func Player() {
        print(kPop)
        print(Music.pops)
    }
}</code></pre>
<p>인스턴스 메서드에선 인스턴스 생성이 되었기 때문에 kPop에 그냥 접근이 가능하다.</p>
<p>인스턴스와 상관 없는 타입 멤버(프로퍼티&amp;메서드)에 접근할 경우, 기존의 접근 방식과 마찬가지로 타입 이름을 통해서 접근할 수 있다.</p>
<h3 id="static과-class">static과 class</h3>
<p>위의 타입메서드 정의에서 나온걸 조금 더 알아보자.</p>
<p>선언할 때 언제 static을 사용하고 언제 class를 사용할까?
바로 메소드 오버라이딩을 허용하지 않을 때 / 할 때 각각 사용한다.</p>
<p><code>static</code>의 경우는 메소드 오버라이딩을 허용하지 않을 때 사용한다.</p>
<pre><code class="language-swift">class Music {
    static func kPop() {
        print(&quot;awesome&quot;)
    }
}

class MusicPlayer : Music {
    override static func kPop() { //Cannot override static method
    }
}</code></pre>
<p>static으로 메서드를 선언했을 때, 해당 메소드를 Subclass에서 오버라이드를 시도하면 
예시처럼 에러가 나게 된다.</p>
<p><code>class</code>의 경우에는 메소드 오버라이딩을 허용할 때 사용한다.</p>
<pre><code class="language-swift">class Music {
    class func pop() {
        print(&quot;perfect&quot;)
    }
}

class MusicPlayer : Music {
    override class func pop() {

    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[iOS - 싱글톤]]></title>
            <link>https://velog.io/@high_sky8320/iOS-%EC%8B%B1%EA%B8%80%ED%86%A4</link>
            <guid>https://velog.io/@high_sky8320/iOS-%EC%8B%B1%EA%B8%80%ED%86%A4</guid>
            <pubDate>Tue, 04 Oct 2022 08:36:23 GMT</pubDate>
            <description><![CDATA[<p>프로퍼티를 정리하며 싱글톤 이야기가 나왔기에.. 
머릿속을 정리하고자 겸사겸사 정리하게 되었습니다.</p>
<h3 id="여는장">여는장</h3>
<p>먼저 싱글톤의 정의입니다.</p>
<blockquote>
<p>The singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance.</p>
</blockquote>
<p>위를 번역하자면 싱글톤 패턴은 클래스의 인스턴스화를 하나의 &quot;단일&quot; 인스턴스로 제한하는 소프트웨어 디자인 패턴이다 라고 합니다.</p>
<p>간단하게 이야기 하자면 <strong>특정 용도로 객체를 &quot;하나&quot;만 생성하여, 공용으로 사용하고 싶을 때 사용하는 디자인 패턴</strong>을 말합니다.</p>
<p>막상 이렇게 말하면 잘 모르겠지만 iOS 개발을 하다보면 몇번 마주쳤을 것입니다.
예시를 들자면 URLSession.shared가 있습니다.</p>
<pre><code class="language-swift">let dataTask = URLSession.shared.dataTask(with: URLRequest(url: url!)) { _, _, _ in 
    //code in here.
}</code></pre>
<p>이외에도 iOS에서 몇몇 기능들에서 싱글톤을 사용하고 있습니다.</p>
<h3 id="직접-만들기">직접 만들기</h3>
<p>본론으로 들어가서.. 싱글톤을 우리가 직접 만들 수도 있습니다.</p>
<pre><code class="language-swift">class Samples {
    static let shared = Samples()

    private init() {}
}</code></pre>
<pre><code class="language-swift">class Samples {
    static let shared = Samples()

    var id: Int?
    var password: String?
    var name: String?

    private init() {}
}</code></pre>
<p>static 을 이용해 전역적으로 사용할 수 있도록 만들고,
혹시라도 Init을 호출해 인스턴스를 또 생생하는 것을 막기 위해,
init() 함수 접근 제어자를 private로 지정해줍니다.</p>
<p>이렇게 싱글톤이 만들어졌습니다.
해당 클래스는 위의 URLSession과 마찬가지로 shared란 프로퍼티를 통해 접근합니다.</p>
<pre><code class="language-swift">let test = Samples.shared
test.id = 0
test.password = &quot;qwertz04@&quot;
test.name = &quot;zero&quot;</code></pre>
<h3 id="그럼-언제-쓰는가">그럼 언제 쓰는가</h3>
<p>싱글톤 패턴은 DBCP(DataBase Connection Pool)처럼 
공통된 객체를 여러개 생성해서 사용해야하는 상황에서 많이 사용합니다.</p>
<p>싱글톤 패턴은 장점이 많은 만큼 단점도 비례합니다.
먼저 장점은 이렇습니다.</p>
<h4 id="메모리를-단-한번만-사용한다">메모리를 단 한번만 사용한다.</h4>
<p>싱글톤을 이용하면 메모리를 단 한번만 사용하기 때문에 메모리 관리가 편합니다.</p>
<h4 id="객체-접근-시간이-줄어든다">객체 접근 시간이 줄어든다.</h4>
<p>싱글톤을 한 번 만들어두기만 하면 다시 메모리를 할당하고 초기화(init)하는 과정이 줄어드니 
매번 객체를 만들 때보다는 빠릅니다.</p>
<h4 id="전역-범위에서-상태-데이터-전달이-쉬워진다">전역 범위에서 상태, 데이터 전달이 쉬워진다.</h4>
<p>static이라서 어디서든 접근할 수 있기 때문에 어떤 상태를 표시하거나, 데이터를 공유하기에 적절합니다. 
단 하나의 객체만 있고, 잘 신경써서 동시성 문제가 생기지 않도록 짜기만 한다면 걱정 없이 상태나 데이터를 전달할 수 있다.</p>
<p>장점만 봤을때는 &quot;와 그럼 싱글톤 완전 좋네 무조건 써야겠다&quot; 싶지만 꼭 그런것도 아닙니다.
단점이 존재하기 때문입니다.</p>
<h4 id="테스트가-힘들다">테스트가 힘들다.</h4>
<p>위의 싱글톤 예시처럼 init 을 private 로 설정해 유일성을 보장하는 경우가 많습니다.
의도는 좋지만 이렇게 생성을 제한해버리면 테스트용 Mock 객체를 만들어내기 어려워집니다.
이렇게 되면 관련된 로직을 테스트하기 어려워지고, 테스트 코드가 중요한 경우에는 싱글톤이 오히려 곤란한 존재가 될 수 있습니다.</p>
<h4 id="의존성">의존성</h4>
<p>싱글톤의 특성이 ‘여기저기’서, ‘쉽게’ 접근할 수 있는 터라, 
Singleton Instance가 너무 많은 일을 하거나, 많은 데이터를 공유시킬 경우 다른 클래스의 Instance 간의 결합도가 높아져  &quot;개방=폐쇄&quot; 원칙을 위배하게 됩니다. </p>
<h3 id="닫는장">닫는장</h3>
<p>따라서 싱글톤 객체를 무조건적으로 쓰지 말고 쓰기 이전에 많이 고민을 해보아야 합니다.
고민했을때 사용하는 것이 좋다고 생각하면 최대한 동시성에 주의하여 설계하도록 해야합니다.
정말 딱 필요한 것들만 가지고 있음으로써 필요 이상으로 다른 객체들에게 영향을 주지 않도록 해야 합니다.</p>
<p>정말 고민된다고 하면 해당 포스트를 참고해봐도 괜찮을 듯 합니다.
GCD도 추후에 포스트..😅</p>
<blockquote>
<p><a href="https://medium.com/@jang.wangsu/swfit-thread-safety-%ED%95%9C-%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%82%AC%EC%9A%A9%EC%9D%80-75c43e567acf">https://medium.com/@jang.wangsu/swfit-thread-safety-한-싱글톤-사용은-75c43e567acf</a></p>
</blockquote>
<p>참고자료입니다.</p>
<blockquote>
<p><a href="https://babbab2.tistory.com/66">https://babbab2.tistory.com/66</a>
<a href="https://medium.com/hcleedev/swift-singleton-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4-b84cfe57c541">https://medium.com/hcleedev/swift-singleton-싱글톤-패턴-b84cfe57c541</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-8 프로퍼티]]></title>
            <link>https://velog.io/@high_sky8320/swift-8-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0</link>
            <guid>https://velog.io/@high_sky8320/swift-8-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0</guid>
            <pubDate>Tue, 04 Oct 2022 07:38:36 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 프로퍼티 입니다.</p>
<h3 id="저장-프로퍼티--stored-property-">저장 프로퍼티 ( Stored Property )</h3>
<p>클래스와 구조체에서만 사용할 수 있고, 값을 저장하기 위해 선언되는 상수/변수</p>
<pre><code class="language-swift">class Fruit {
        let grape : String = &quot;Grape&quot;
        var peach : String = &quot;&quot;
}

struct Fruits {
        let grape : String = &quot;Grape&quot;
        var peach : String = &quot;&quot;
}</code></pre>
<p>→ 여기서 grape와 peach 모두 저장 프로퍼티이다.</p>
<h4 id="저장-프로퍼티-클래스와-구조체-차이점">저장 프로퍼티 클래스와 구조체 차이점?</h4>
<p>클래스 / 구조체의 차이를 잘 알고 있다면 둘의 차이를 알 수 있다.</p>
<p>먼저 <code>클래스</code>의 경우부터 보자면, 옵셔널 상수로 Human 클래스 인스턴스를 선언하고</p>
<pre><code class="language-swift">let dessert : Fruit? = .init()

dessert?.grape = &quot;Shine&quot; // Cannot assign to property: &#39;grape&#39; is a &#39;let&#39; constant
dessert?.peach = &quot;Peaches&quot;</code></pre>
<p>grape는 값 변경을 하면 에러가 나지만 peach는 나지 않는걸 볼 수 있는데, 
그 이유는 클래스는 <strong>참조 타입</strong>이기 때문이다. ( 클래스는 힙에 저장됨 / 공유 )</p>
<p>dessert는 <code>스택</code>에, grape와 peach는 <code>힙</code>에 할당이 되어있으므로 
dessert는 <code>힙</code>에 있는 grape, peach 인스턴스를 <strong>참조</strong>하고 있는 형태이다. </p>
<p>따라서 현재 dessert는 <code>힙</code>에 있는 인스턴스들의 주소값을 지니고 있으니 
<strong>상수로 선언한다는 것은 인스턴스들의 주소값을 상수로 선언한다는 의미이다.</strong></p>
<p>결론적으로 클래스의 경우, 인스턴스 생성시 let이든 var든 프로퍼티에 접근하는 것에 영향을 주지 않고, 
위에 선언한 dessert의 상수 안의 값을 변경하는 것에 영향을 준다. </p>
<p>위의 dessert는 상수이므로 nil을 할당할 수 없고, 다른 클래스 인스턴스(의 주소값)도 할당할 수 없다.</p>
<p>하지만 변수로 선언한다면 두개 다 가능하다. ( 하지만 클래스에 선언한 grape는 상수이므로 변경 불가. )</p>
<p>다음으로 <code>구조체</code>의 경우, 모두 에러가 나게 된다. </p>
<pre><code class="language-swift">let food : Fruits? = .init()

food?.grape = &quot;delicious&quot; // Cannot assign to property: &#39;grape&#39; is a &#39;let&#39; constant
food?.peach = &quot;sweet&quot; // Cannot assign to property: &#39;food&#39; is a &#39;let&#39; constant</code></pre>
<p>먼저 구조체는 값 타입이다. ( 복사! ) 
때문에 클래스와 다르게 저장 프로퍼티들도 모두 <code>스택</code>에 올라가게 된다.</p>
<p>따라서 상수로 선언을 했으니 구조체의 모든 프로퍼티들은 값을 변경할 수 없게 된 것이다.</p>
<p>값을 변경할 수 없으니 nil을 할당할 수도 없고, 다른 구조체 인스턴스도 받을 수 없다.</p>
<p>마찬가지로 var로 선언하면 둘 다 가능하다. ( 하지만 구조체에 선언한 grape는 상수이므로 변경 불가. )</p>
<h4 id="지연-저장-프로퍼티--lazy-">지연 저장 프로퍼티 ( lazy )</h4>
<p>지연 저장된 프로퍼티는 처음 사용될 때까지 초기값은 계산되지 않는 프로퍼티이다.</p>
<p>간단하게, 선언만 될 뿐 초기화되지 않고 있다가 프로퍼티가 호출되는 순간에 초기화 되는 
저장 프로퍼티라고 생각하면 된다.</p>
<p>지연 저장 프로퍼티는 이럴때 주로 사용한다.</p>
<blockquote>
<ol>
<li>인스턴스의 초기화가 완료될 때까지 값을 알 수 없는 외부 요인에 인해 초기값이 달라질 때</li>
<li>프로퍼티의 초기값으로 <strong>필요할 때까지 수행하면 안되는 복잡하거나 계산 비용이 많이 드는 경우</strong></li>
</ol>
</blockquote>
<pre><code class="language-swift">// DataImporter 클래스는 여기서 데이터 가져오기 기능을 제공할 것이다.
class DataImporter {
    var filename = &quot;data.txt&quot;
}

// DataManager 클래스는 여기서 데이터 관리 기능을 제공할 것이다.
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
}

let manager = DataManager()
manager.data.append(&quot;Some data&quot;) // [&quot;Some data&quot;]
manager.data.append(&quot;Some more data&quot;) // [&quot;Some data&quot;, &quot;Some more data&quot;]
// the DataImporter instance for the importer property has not yet been created

print(manager.importer.filename) // Prints &quot;data.txt&quot;</code></pre>
<p>위의 예시에서 DataImporter가 외부 파일에서 데이터를 가져오는 클래스며, 초기화하는데 적지 않은 시간이 걸린다고 가정했을 때, 성능향상과 메모리 낭비를 줄이고자 importer 프로퍼티에 lazy를 사용하였다.</p>
<p>lazy가 붙어 있으므로 importer 프로퍼티의 DataImporter 인스턴스는 
filename 프로퍼티를 조회할 때처럼 importer 프로퍼티에 처음 접근될 때 생성된다. </p>
<blockquote>
<p>lazy의 경우 무조건 변수(var)로 선언해야한다.
상수(let)으로 선언하게 되면 필요한 시점에 초기화(init)를 진행할 수 없기 때문.</p>
</blockquote>
<h3 id="연산-프로퍼티--computed-property-">연산 프로퍼티 ( Computed Property )</h3>
<p>연산 프로퍼티 같은 경우, 저장 프로퍼티와 다르게 구조체, 클래스 뿐만아니라 열거형에도 사용이 가능하다.</p>
<p>저장 프로퍼티와 별도의 저장 공간을 갖지 않고, 다른 저장 프로퍼티의 값을 읽어 연산을 실행하거나, 
프로퍼티로 전달받은 값을 다른 프로퍼티에 저장한다. ( <code>getter</code>, <code>setter</code> 이용하기 )</p>
<p><code>getter</code>의 경우 “얻는 것”이므로 
어떤 저장 프로퍼티의 값을 연산해서 return할 것인지, return 구문이 항상 존재해야 한다.</p>
<p><code>setter</code>의 경우 “설정하는 것”이므로
파라미터로 받은 값을 어떤 저장 프로퍼티에 어떻게 설정할 것인지를 구현해야 한다.</p>
<p>연산 프로퍼티 같은 경우 저장 프로퍼티와 다르게 어떠한 값을 저장하지 않기 때문에 타입 어노테이션을 통해 자료형을 명시해야 한다. 그리고 선언된 자료형 뒤에 { }을 붙인다.</p>
<pre><code class="language-swift">class Fruit {
    var grape : String = &quot;Grape&quot;

    var getSet : String {
        get {
            return &quot;Shine&quot; + &quot; &quot; + self.grape
        }
        set(grape) {
            self.grape = grape
        }
    }
}

let dessert : Fruit = .init()

//get접근
print(dessert.getSet) //Shine Grape

//set접근
dessert.getSet = &quot;Muscat&quot;
print(dessert.getSet) //Shine Muscat</code></pre>
<blockquote>
<p>연산 프로퍼티를 사용하려면 무조건 <strong>읽거나 쓸 수 있는 &quot;저장 프로퍼티&quot;가 먼저 존재</strong>해야 한다.</p>
</blockquote>
<p>위의 예시처럼 저장 프로퍼티에 접근하는 것과 같이 
연산 프로퍼티인 getSet을 읽으면 getSet의 getter가 실행되어 get안에 있는 값이 리턴되며,
연산 프로퍼티인 getSet을 쓰면 “Muscat”이란 값이 setter의 파라미터로 넘어가 set 함수가 실행된다.</p>
<h4 id="추가---set에-대하여">추가 - set에 대하여!!</h4>
<p>set의 파라미터는 단 하나만 존재한다. 
만약 변수명을 짓기 어려운 경우, get처럼 파라미터를 날리고 newValue로 접근한다.</p>
<pre><code class="language-swift">class Fruit {
    var grape : String = &quot;Grape&quot;

    var getSet : String {
        get {
            return &quot;Shine&quot; + &quot; &quot; + self.grape
        }
        set {
            self.grape = newValue
        }
    }
}</code></pre>
<p>값을 읽기만 한다면 set은 생략해도 된다. get을 적어도되고 그냥 리턴구문만 작성해도 문제없다.</p>
<pre><code class="language-swift">class Fruit {
    var grape : String = &quot;Grape&quot;

    var getSet : String {
        return grape
    }
}</code></pre>
<p>하지만 set만 작성하는건 안된다. 무조건 get이 필요하다.</p>
<p><del>swift.org에는 해당 카테고리에 뭐가 많은데.. 아직 잘 모르겠어서 생략하였다.</del></p>
<h3 id="프로퍼티-관찰자-didset과-willset">프로퍼티 관찰자 <code>didSet</code>과 <code>willSet</code></h3>
<p><code>didSet</code>은 <strong>새로운 값이 저장되자마자</strong> 호출되고, <code>willSet</code>은 <strong>값이 저장되기 직전</strong>에 호출된다.</p>
<p><code>didSet</code>은 파라미터 이름과 괄호를 따로 지정하지 않을 경우 oldValue를 사용하고,
<code>willSet</code>은 파라미터 이름과 괄호를 따로 지정하지 않을 경우 newValue를 사용한다.</p>
<pre><code class="language-swift">var fruit : String = &quot;Grape&quot; {
    willSet {
        print(&quot;현재 과일명 = \(fruit), 변경된 과일명 = \(newValue)&quot;)
    }
}

fruit = &quot;peach&quot;
//현재 과일명 = Grape, 변경된 과일명 = peach</code></pre>
<pre><code class="language-swift">var fruit : String = &quot;Grape&quot; {
    didSet {
        print(&quot;변경된 과일명 = \(fruit), 기존 과일명 = \(oldValue)&quot;)
    }
}

fruit = &quot;peach&quot;
//변경된 과일명 = peach, 기존 과일명 = Grape</code></pre>
<p>만약 둘 다 같이 선언할 경우 순서는 이렇게 된다.</p>
<p>willSet 실행 → fruit값 변경 → didSet 실행</p>
<pre><code class="language-swift">var fruit : String = &quot;Grape&quot; {
    willSet {
        print(&quot;현재 과일명 = \(fruit), 변경된 과일명 = \(newValue)&quot;)
    }
    didSet {
        print(&quot;변경된 과일명 = \(fruit), 기존 과일명 = \(oldValue)&quot;)
    }
}

fruit = &quot;peach&quot;
/*
현재 과일명 = Grape, 변경된 과일명 = peach
변경된 과일명 = peach, 기존 과일명 = Grape
*/</code></pre>
<h3 id="타입-프로퍼티--type-property-">타입 프로퍼티 ( Type Property )</h3>
<p>타입 프로퍼티도 연산 프로퍼티처럼 클래스,구조체,열거형에서 사용 가능하다.</p>
<p><strong>저장 타입 프로퍼티</strong>와 <strong>연산 타입 프로퍼티</strong>가 존재하며,
저장 타입 프로퍼티의 경우 선언할 당시 원하는 값으로 항상 초기화가 되어 있어야 한다.
<code>static</code>을 이용해 선언하며, 자동으로 lazy로 작동한다 ( lazy를 직접 붙일 필요 또한 없다 )</p>
<pre><code class="language-swift">class Fruit {
    static let grape : String = &quot;Grape&quot; //저장 타입 프로퍼티

    static var getSet : String { //연산 타입 프로퍼티
        return &quot;Shine&quot; + &quot; &quot; + self.grape 
    }
}</code></pre>
<blockquote>
<p>저장 타입 프로퍼티를 static으로 선언할 경우, 
initializer가 필수거나 getter/setter를 지정해야 한다.
static으로 선언되는 저장 타입 프로퍼티의 경우 초기화할 때 값을 할당할 initializer가 없기 때문.</p>
</blockquote>
<p>타입 프로퍼티는 해당 타입의 인스턴스가 아닌 타입에 대해 조회되고 설정한다.</p>
<p>저장,연산 프로퍼티와 다르게 <code>dessert.(프로퍼티이름)</code>으로 접근이 안되고 ( 그렇게 접근하면 self만 뜸 )
타입 이름을 통해서만 접근이 가능하다.</p>
<pre><code class="language-swift">print(Fruit.grape) //Grape</code></pre>
<p>요약하자면 <strong>타입 프로퍼티는 누군가 나를 불러줬을 때 메모리에 올라간다.</strong> 
( = 저장 프로퍼티의 속성인 lazy )</p>
<p>따라서 위처럼 grape란 프로퍼티를 최초로 호출하면 그때 메모리에 올라가서 초기화가 된다. 
또한, 타입 프로퍼티는 모든 타입이 공통적인 값을 정의하는 데 유용하게 사용된다 ( 싱글톤 )</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-7 클래스와 구조체]]></title>
            <link>https://velog.io/@high_sky8320/swift7-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4</link>
            <guid>https://velog.io/@high_sky8320/swift7-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4</guid>
            <pubDate>Mon, 19 Sep 2022 09:03:38 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 클래스와 구조체 입니다.</p>
<p>실제로 면접에서 자주 물어보는 질문 중 하나가 클래스와 구조체라고 합니다.
어느정도는 알고있지만 막상 대답은 못했던.. 이 기회에 다시 알아봤습니다.</p>
<p>Class, Struct의 차이점을 확실히 알고 있다면 성능개선을 할 수 있다고 합니다.</p>
<h3 id="클래스와-구조체의-공통점">클래스와 구조체의 공통점</h3>
<ul>
<li>값을 저장할 프로퍼티를 선언할 수 있습니다.</li>
<li>함수적 기능을 하는 메서드를 선언할 수 있습니다.</li>
<li>내부값에 .을 사용하여 접근할 수 있습니다.</li>
<li>생성자를 사용해 초기 상태를 설정할 수 있습니다.</li>
<li>extension을 사용하여 기능을 확장할 수 있습니다.</li>
<li>Protocol을 채택하여 기능을 설정할 수 있습니다.</li>
</ul>
<h3 id="클래스와-구조체의-차이점">클래스와 구조체의 차이점</h3>
<ul>
<li>구조체와 열거형은 값타입( Copy-On-Write ), 클래스는 참조타입!!</li>
<li>구조체는 상속 불가, 클래스는 상속 가능</li>
</ul>
<blockquote>
<p>💡 Copy-On-Write?
값 타입 변수를 새로운 변수에 할당할 때, 즉시 새로운 값을 복사하는 것이 아니라, 
기존 인스턴스를 계속 가리키다가 변경사항이 생겼을 때 <strong>복사본</strong>을 만드는 것</p>
</blockquote>
<h3 id="기본-형태">기본 형태</h3>
<pre><code class="language-swift">struct ValueType { }

class ReferenceType { }</code></pre>
<h3 id="클래스-안에-구조체-인스턴스가-있는-경우">클래스 안에 구조체 인스턴스가 있는 경우</h3>
<pre><code class="language-swift">struct ValueType {
    var number = 2
}

class ReferenceType {
    var number = 1
    var structInstance = ValueType()
}

let classInstance = ReferenceType()
let classInstanceCopy = classInstance

classInstanceCopy.number = 0
classInstanceCopy.structInstance.number = 0

print(classInstanceCopy.number) //0
print(classInstanceCopy.structInstance.number) //0

print(classInstance.number) //0
print(classInstance.structInstance.number) //0</code></pre>
<p>참조 타입 인스턴스를 다른 변수에 할당할 때는 참조 값만 전달되기 때문에 내부의 값 타입 인스턴스는 </p>
<p>새로 복사되지 않고 기존 인스턴스를 따라갑니다.</p>
<blockquote>
<p> <strong>클래스 내부에 구조체 인스턴스</strong>가 멤버로 있을 때,
변수에 클래스 인스턴스 할당 시 <strong>같은 구조체 인스턴스</strong>가 따라간다.</p>
</blockquote>
<h3 id="구조체-안에-클래스-인스턴스가-있는-경우">구조체 안에 클래스 인스턴스가 있는 경우</h3>
<pre><code class="language-swift">struct ValueType {
    var number = 2
    var classInstance = ReferenceType()
}

class ReferenceType {
    var number = 1
}

var structInstance = ValueType()
var structInstanceCopy = structInstance

structInstanceCopy.number = 0
structInstanceCopy.classInstance.number = 0

print(structInstance.number) //2
print(structInstance.classInstance.number) //0

print(structInstanceCopy.number) //0
print(structInstanceCopy.classInstance.number) //0</code></pre>
<p>먼저, 값 타입 할당이 일어났기 때문에 구조체 인스턴스가 새로운 변수에 복사됩니다. </p>
<p>이때, 내부에 있는 값 타입 멤버 변수는 별개의 변수로 복사되고, </p>
<p>참조 타입 클래스 인스턴스는 <strong>참조 값이 복사</strong>됩니다. </p>
<p>따라서 두 인스턴스의 멤버 변수인 number는 복사본에만 업데이트되고, </p>
<p>클래스 인스턴스는 참조값이 복사되었기 때문에 <strong>양쪽 인스턴스에 모두 업데이트됩니다.</strong></p>
<blockquote>
<p><strong>구조체 내부에 클래스 인스턴스</strong>가 멤버로 있을 때,
변수에 구조체 인스턴스 할당 시 <strong>클래스 인스턴스의 참조 값</strong>이 따라간다.</p>
</blockquote>
<h3 id="언제-무엇을-쓸까">언제, 무엇을 쓸까?</h3>
<ul>
<li><p><strong>디폴트로는 구조체를 사용하라.</strong></p>
<blockquote>
<p>스위프트의 구조체는 클래스에서 사용할 수 있는 다양한 요소들을 사용할 수 있습니다.
또한 스위프트의 표준 라이브러리들의 데이터들 대부분 구조체를 사용해 구현되어 있습니다.</p>
</blockquote>
</li>
<li><p><strong>Objective-C와 상호 이용(Interoperability) 해야 할 때는 클래스를 사용하라.</strong></p>
<blockquote>
<p>Objective-C에서 지원하는 API를 사용해야 할 때는 Objective-C의 클래스를 상속받아야 사용할 수 있습니다.</p>
</blockquote>
</li>
<li><p><strong>고유 값을 제어해야 할때는 클래스를 사용하고 제어하지 않는다면 구조체를 사용하라.</strong></p>
<blockquote>
<p>주로 파일 관리나 네트워크 연결과 같은 작업을 다룰 때 클래스를 사용합니다.
외부에서 이미 고유성을 처리하고 있는 경우에는 
앱 내에서 데이터의 고유성을 관리할 필요가 없습니다.</p>
</blockquote>
</li>
<li><p><strong>상속과 공유 속성을 모델링 하고 싶을 때는 구조체와 프로토콜을 사용하라.</strong></p>
<blockquote>
<p>앞서 언급했듯이 구조체는 상속이 불가합니다. 
하지만 프로토콜을 통해 계층구조를 표현할 수 있습니다.<br>클래스는 클래스끼리만 상속을 만들 수 있는 반면 
프로토콜은 클래스, 열거형, 구조체가 모두 채택하도록 할 수 있습니다.</p>
</blockquote>
</li>
</ul>
<p>.
.
.
(수정예정)</p>
<blockquote>
<p><a href="https://jeonyeohun.tistory.com/179?category=874083">https://jeonyeohun.tistory.com/179?category=874083</a></p>
</blockquote>
<blockquote>
<p><a href="https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes">https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-6 옵셔널]]></title>
            <link>https://velog.io/@high_sky8320/swift-6-%EC%98%B5%EC%85%94%EB%84%90</link>
            <guid>https://velog.io/@high_sky8320/swift-6-%EC%98%B5%EC%85%94%EB%84%90</guid>
            <pubDate>Wed, 14 Sep 2022 07:12:39 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 옵셔널 입니다.</p>
<p>정말 옵셔널은 스위프트의 꽃이라 생각합니다.
하지만 그만큼 이해하는게 좀 힘든거 같기도 하고..</p>
<h3 id="옵셔널-기본-타입">옵셔널 기본 타입</h3>
<p>기존에 우리가 선언했었던 타입은 다 non-Optional Type입니다.</p>
<p>해당 타입은 무조건 값이 존재해야 합니다. </p>
<p><code>nil</code> 이라는 값을 가질 수 있으면 Optional Type이고,</p>
<p>이 Optional Type을 선언할 땐 타입 옆에 <code>?</code>를 붙입니다.</p>
<pre><code class="language-swift">let age : Int? = 19
print(&quot;내 나이는 \(age)살 입니다.&quot;) //내 나이는 Optional(19)살 입니다.</code></pre>
<blockquote>
<p>옵셔널 중에 타입 옆에 <code>?</code>가 아니라 <code>!</code>를 붙이는 옵셔널 묵시적 추출도 있습니다. 
하지만 API에서 IUO를 return 한 경우를 제외하면 어쨌든 IUO도 강제 추출이기 때문에 
사용하지 않는 것을 권장합니다.</p>
</blockquote>
<h3 id="옵셔널-언래핑--강제-추출-">옵셔널 언래핑 ( 강제 추출 )</h3>
<p>코딩테스트때는 그래도 값이 지정되어 있다면야 써도 되겠지만 아니라면 그냥 쓰지 않는게 낫습니다.</p>
<p>컴파일 에러로 알려주면 다행이지만.. 개발하고 빌드했을 때 런타임 에러가 나면 얼마나 서럽겠습니까..🥲</p>
<p>옵셔널 언래핑은 <code>nil</code>을 언래핑 할 수 없는데 강제 추출은 “값이 있건 없건 알바야? 그냥 풀어!” </p>
<p>이런 느낌이기 때문에 추천하지 않습니다. </p>
<pre><code class="language-swift">let age : Int? = 19
print(&quot;내 나이는 \(age!)살 입니다.&quot;) //내 나이는 19살 입니다.</code></pre>
<h3 id="옵셔널-언래핑--옵셔널-바인딩-">옵셔널 언래핑 ( 옵셔널 바인딩 )</h3>
<p><code>if-let</code>, <code>while-let</code>, <code>guard-let</code>을 사용하여 옵셔널을 안전하게 언래핑 할 수 있습니다.</p>
<p>예시에서 표현식이 nil인지 여부를 판단하고 nil이 아닌 경우 Unwrapping 한 값을 대입합니다.</p>
<pre><code class="language-swift">let age : Int? = 19

if let optionalAge = age { //optionalAge는 Int, age는 Int? 타입
    print(optionalAge) //19
} else {
    print(age) //Optional(19)
}</code></pre>
<p>optionalAge의 값이 nil이 아닌 경우, optionalAge을 Unwrapping 한 값을 age에 대입하므로 19입니다.</p>
<pre><code class="language-swift">let empty : Int? = nil

guard let optionalAge = empty else { return }
print(optionalAge)</code></pre>
<table>
<thead>
<tr>
<th>종류</th>
<th>if-let</th>
<th>guard-let</th>
</tr>
</thead>
<tbody><tr>
<td>바인딩된 상수의 scope</td>
<td>if문 안에서만 사용 가능</td>
<td>guard문 밖에서만 사용 가능</td>
</tr>
<tr>
<td>사용</td>
<td>단순히 옵셔널 처리 값에 대한 피드백만 주고 싶을 때</td>
<td>옵셔널 처리 값이 nil인 경우 무조건 함수의 실행을 종료 시킬 때</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-5 열거형]]></title>
            <link>https://velog.io/@high_sky8320/swift-5-%EC%97%B4%EA%B1%B0%ED%98%95</link>
            <guid>https://velog.io/@high_sky8320/swift-5-%EC%97%B4%EA%B1%B0%ED%98%95</guid>
            <pubDate>Wed, 14 Sep 2022 07:07:24 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 열거형 입니다.</p>
<p><del>원래 하기로 한 양보다 조금 더 나가고 있는듯하나..
열거형 / 함수 / 클로저 / 옵셔널은 너무 중요하고 난이도도 좀 있다고 생각해서 
열거형과 옵셔널 파트를 먼저 공부하게 되었습니다.</del></p>
<p>선택지가 있을 때 사용하면 제일 유용한 열거형~</p>
<h3 id="형태">형태</h3>
<pre><code class="language-swift">enum CompassPoint {
    case north
    case south
    case east
    case west
}

//or

enum Planet {
    case north, south, east, west
}</code></pre>
<p>형태는 이렇게 enum-case 형태입니다. 사용은 이렇게 합니다.</p>
<pre><code class="language-swift">var directionToHead = CompassPoint.west

directionToHead = .east //directionToHead 의 타입은 이미 알고 있으므로 값을 설정할 때 타입을 삭제할 수 있습니다.</code></pre>
<h3 id="원시-값이-있는-열거형">원시 값이 있는 열거형</h3>
<p>기존에 적은 형태는 원시 값이 없는 열거형이었습니다.
이번에는 원시 값이 있는 열거형에 대해 알아보도록 하겠습니다.</p>
<pre><code class="language-swift">enum number : Int {
  case zero //0
    case one //1
  case two //2
  case three //3
}</code></pre>
<p>Int를 enum 선언 시 이름 옆에 명시해주면, 먼저 선언된 case부터 0부터 1씩 증가된 값이 들어갑니다.</p>
<p>RawValue를 직접 지정할 수도 있습니다. 지정하지 않았다면 바로 이전 case에서 +1한 값으로 지정됩니다.</p>
<pre><code class="language-swift">enum number : Int {
  case zero = 0 //0
    case one //1
  case two = 4 //4
  case three //5
}</code></pre>
<p>String은 Character와 다르게 Raw Value를 지정하지 않으면, case 이름과 동일한 Raw Value가 자동으로 만들어집니다. </p>
<pre><code class="language-swift">enum number : String {
  case zero //zero
    case one = &quot;dot&quot; //dot
  case two //two
  case three //three
}</code></pre>
<p>하지만 Int, String을 제외한 Double, Character 같은 타입은 무조건 RawValue를 지정해줘야합니다.</p>
<p>지정해주지 않으면 정수값이 아니라 실수값이라 컴파일러가 알아서 못더해주니 에러가 나게 됩니다.</p>
<p>따라서, Int형이 아닌 Number 자료형을 사용할 경우, </p>
<p>Raw Value를 생략하고 싶다면, 바로 이전 case의 Raw Value를 정수 값으로 해주어야 합니다.</p>
<pre><code class="language-swift">enum number : Double {
  case zero = 1.0 //1.0
    case one = 2.0 //2.0
  case two = 3 //3.0
  case three //4.0
}</code></pre>
<p>하지만 Character 같은 경우는 무조건 모두 Raw Value를 지정해 주어야합니다.</p>
<p>앞에서 A를 적었다고 다음에 +1을 했으니 B가 된다는 생각은.. 🙅‍♀️</p>
<p>원시값이 있는 열거형은 <code>rawValue</code>를 이용하여 접근합니다.</p>
<pre><code class="language-swift">enum number : Int {
  case zero //0
    case one //1
  case two //2
  case three //3
}

let num = number.zero.rawValue //0
let num2 = number(rawValue : 2) //two</code></pre>
<p>만약 init에 없는 Raw Value 값을 대입하면 nil을 반환합니다 ( 옵셔널 타입 )</p>
<pre><code class="language-swift">let test = number.init(rawValue: 3) //three
let test = number.init(rawValue: 4) //nil</code></pre>
<h3 id="switch-case와-enum">Switch-case와 enum</h3>
<blockquote>
<p> <code>switch</code>구문은 열거형 케이스를 고려할 때 완벽해야 합니다.
만약 case가 생략된다면 코드는 컴파일 되지 않습니다.</p>
</blockquote>
<p>허나 만약 모든 열거형 케이스에 대해 <code>case</code>를 제공하는 것이 적절하지 않은 경우 명시적으로 </p>
<p>해결되지 않은 사례를 포함하는 <code>default</code>를 사용합니다.</p>
<pre><code class="language-swift">let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print(&quot;Mostly harmless&quot;)
default:
    print(&quot;Not a safe place for humans&quot;)
}
// Prints &quot;Mostly harmless&quot;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-4 클로저]]></title>
            <link>https://velog.io/@high_sky8320/swift-4-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@high_sky8320/swift-4-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 06 Sep 2022 01:14:42 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 클로저 입니다.</p>
<p>org에 있는 클로저 목차는 이해가 좀.. ㅇ ㅓ렵네요..</p>
<h3 id="형태">형태</h3>
<p><img src="https://velog.velcdn.com/images/high_sky8320/post/cc851991-53af-4d65-bdf7-104628e481f1/image.png" alt=""></p>
<pre><code class="language-swift">let closure = { () -&gt; () in
    print(&quot;Closure&quot;)
}
closure() //Closure</code></pre>
<blockquote>
<p>클로저는 함수랑 다르게 Argument Label을 사용하지 않습니다.</p>
</blockquote>
<h3 id="클로저-축약">클로저 축약</h3>
<pre><code class="language-swift">func closure(number : (Int, Int, Int) -&gt; Int) {
        number(1,2,3)
}</code></pre>
<p>이 함수는 파라미터로 받은 클로저를 실행하는데, 클로저의 파라미터로 1,2,3이란 숫자를 넘겨주고 있습니다.</p>
<p>해당 함수를 호출할 때 이런식으로 클로저를 작성 했어야 했습니다.</p>
<pre><code class="language-swift">closure(number : { (a: Int, b: Int, c: Int) -&gt; Int in
    return a + b + c
})</code></pre>
<p>여기서 우리는 파라미터 형식과 리턴 형식을 생략할 수 있습니다.</p>
<pre><code class="language-swift">closure(number : { (a, b, c) in
    return a + b + c
})</code></pre>
<p>또한 여기서 더 축약이 가능한데, <code>shorthand argument names</code>를 이용하면 됩니다.</p>
<p> <code>shorthand argument names</code> 는 Parameter Name 대신 사용할 수 있습니다.</p>
<p><code>a = $0</code> , <code>b = $1</code>, <code>c = $2</code> 로 경량화하여 아래처럼 작성이 가능합니다.</p>
<pre><code class="language-swift">closure(number : { 
    return $0 + $1 + $2
})</code></pre>
<p>해당 예제에서는 클로저 구문에 return 구문 하나기 때문에 return문도 생략이 가능합니다. </p>
<p>단일 return이 아닌데 return을 없애고 그냥 작성한다면 에러가 뜨게 되니 주의!!</p>
<pre><code class="language-swift">closure(number : { 
    $0 + $1 + $2
})</code></pre>
<p>또한 number 파라미터가 마지막 파라미터라면 트레일링 클로저로 작성할 수 있습니다.</p>
<blockquote>
<p><strong>트레일링 클로저</strong>란, 마지막 파라미터가 클로저일 때, 
이를 파라미터 값 형식이 아닌 함수 뒤에 붙여 작성하는 문법입니다.
이때, Argument Label은 생략됩니다.</p>
</blockquote>
<p>만약 ()에 값이 아무것도 없다면 ()도 생략이 가능합니다.</p>
<pre><code class="language-swift">closure { 
    $0 + $1 + $2
}</code></pre>
<p>따라서 해당 예제는 이렇게까지 축약이 가능합니다. </p>
<h3 id="탈출-클로저">탈출 클로저</h3>
<p>함수 실행을 벗어나서 함수가 끝난 후에도 클로저를 실행하고 싶을 때 사용</p>
<pre><code class="language-swift">func closure(number : @escaping () -&gt; ()) {
}</code></pre>
<p>이렇게 클로저 파라미터 타입 앞에 @escaping을 붙여주면 됩니다.</p>
<table>
<thead>
<tr>
<th>non-escaping closure</th>
<th>escaping closure</th>
</tr>
</thead>
<tbody><tr>
<td>함수의 실행 흐름을 탈출하지 않아, 함수가 종료되기 전에 무조건 실행 되어야 한다.</td>
<td>함수 실행을 벗어나서 함수가 끝난 후에도 클로저를 실행하고 싶을 때 사용한다.</td>
</tr>
</tbody></table>
<p>보통 @escaping은 네트워킹(비동기)을/를 할 때 많이 사용됩니다.</p>
<p>네트워킹 로직은 UI 업데이트와 데이터간의 순서가 중요하기에 @escaping이 필요하다고 합니다. ( 자세한 내용은 추후 네트워크를 다룰 때 작성 예정 )</p>
<h3 id="자동-클로저">자동 클로저</h3>
<p>바로 실행되어야 하는 구문이 지연되어 실행</p>
<p>이스케이핑과 마찬가지로 클로저 파라미터 타입 앞에 붙여줍니다.</p>
<pre><code class="language-swift">func closure(number : @autoclosure () -&gt; ()) {
}</code></pre>
<p>이제 closure란 파라미터는 실제 클로저를 전달받지 않지만, 클로저처럼 사용이 가능합니다.</p>
<blockquote>
<p>@autoclosure 사용시 파라미터가 반드시 없어야 합니다. 
리턴 타입은 상관 없습니다.</p>
</blockquote>
<p>+) @autoclosure @escaping 속성을 둘 다 사용할 수도 있다고 합니다.. 
( 추가예정 )</p>
<blockquote>
<p><a href="https://bbiguduk.gitbook.io/swift/">https://bbiguduk.gitbook.io/swift/</a>
<a href="https://babbab2.tistory.com/">https://babbab2.tistory.com/</a>
<a href="https://okanghoon.medium.com/swift-study-1-%ED%81%B4%EB%A1%9C%EC%A0%80-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98-abe199f22ad8">https://okanghoon.medium.com/swift-study-1-클로저-고차함수-abe199f22ad8</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift-3 함수]]></title>
            <link>https://velog.io/@high_sky8320/swift-3-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@high_sky8320/swift-3-%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 05 Sep 2022 06:53:42 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 함수 입니다.</p>
<h3 id="함수-형태">함수 형태</h3>
<p><img src="https://velog.velcdn.com/images/high_sky8320/post/634e6aa2-7288-4411-a3f0-782a198f00e7/image.png" alt=""></p>
<pre><code class="language-swift">func greet(person: String) -&gt; String {
    return &quot;Hello, &quot; + person + &quot;!&quot; //축약
}
print(greet(person: &quot;Swift&quot;))
//Hello, Swift!</code></pre>
<p>파라미터 값은 여러개를 넣을 수 있습니다.  또한, 기본값도 설정이 가능합니다.</p>
<pre><code class="language-swift">func greet(person: String = &quot;Swift&quot;) -&gt; String {
    return &quot;Hello, &quot; + person + &quot;!&quot;
}
greet() //Hello Swift!
greet(person: &quot;zero&quot;) //Hello zero!</code></pre>
<p>호출시, 파라미터를 지정하지 않으면 기본 값으로 들어가고, 파라미터를 지정해주면 지정 값으로 들어갑니다.</p>
<h3 id="파라미터-없는-함수">파라미터 없는 함수</h3>
<pre><code class="language-swift">func sayHello() -&gt; String {
    return &quot;hello, world&quot;
}
print(sayHello())
//hello, world</code></pre>
<p>함수 정의는 어떠한 파라미터가 없더라도 함수의 이름 뒤에 소괄호가 필요합니다. </p>
<p>값을 print하려면 변수명 뒤에 소괄호를 붙여야 해당 함수에 있는 값이 출력됩니다.</p>
<h3 id="파라미터--argument가-있고-없고-">파라미터 ( Argument가 있고, 없고 )</h3>
<p>파라미터는 이름 뒤에 ()를 치고 안에 원하는 파라미터를 적는 형식인데, </p>
<p>해당 예제는 <code>Argument Label</code> 을 _로 생략하고 <code>Parameter Name</code> 만 선언한 예제입니다.</p>
<p>때문에  함수를 호출할 때 그냥 person에 들어갈 값만 입력을 합니다.</p>
<table>
<thead>
<tr>
<th>종류</th>
<th>뜻</th>
</tr>
</thead>
<tbody><tr>
<td><code>Argument Label</code></td>
<td>함수를 호출할 때 사용하는 이름</td>
</tr>
<tr>
<td><code>Parameter Name</code></td>
<td>함수 내에서 사용할 파라미터의 이름</td>
</tr>
</tbody></table>
<pre><code class="language-swift">func greet(_ person: String) -&gt; String {
    return &quot;Hello \(person)!!&quot;
}

greet(&quot;Zero&quot;)
//Hello Zero!!</code></pre>
<p><code>Argument Label</code> 이 있다면 함수를 호출할 때 함수명(Argument Label : 값 ) 이 됩니다.</p>
<pre><code class="language-swift">func greet(to person: String) -&gt; String {
    return &quot;Hello \(person)!!&quot;
}

greet(to : &quot;Zero&quot;)
//Hello Zero!!</code></pre>
<p>+) 함수는 반환 타입 정의를 요구하지 않습니다. ( 해당 예제에는 리턴타입을 요구했기에 작성한 것 )</p>
<h3 id="in-out-파라미터">In-out 파라미터</h3>
<p>함수의 파라미터 값을 변경하고 <strong>함수 호출이 종료된 후에도 이러한 변경된 값을 유지하고 싶을 때</strong> 사용.</p>
<blockquote>
<p>in-out 파라미터의 인자로 변수만 전달할 수 있습니다. 
상수와 반복은 수정할 수 없기 때문에 인자로 상수 또는 반복 값은 전달할 수 없습니다.</p>
</blockquote>
<p>함수에 수정가능함을 알리기 위해 in-out 파라미터에 인자로 전달할 때 변수의 이름 앞에 <code>&amp;</code>를 붙여줍니다.</p>
<pre><code class="language-swift">func sayHello(name: inout String) {
    name = &quot;zero&quot;
}

var name: String = &quot;Dotzero&quot;
sayHello(name: &amp;name)
print(name)
//zero</code></pre>
<h3 id="가변-파라미터">가변 파라미터</h3>
<p>가변 파라미터는 파라미터의 타입 이름 뒤에 세개의 기간 문자 (<code>...</code>)를 추가하여 작성합니다.</p>
<p>가변 파라미터에 전달된 값은 함수 바디 내에서 적절한 타입의 배열로 사용할 수 있습니다.</p>
<pre><code class="language-swift">func number(of nums: Int...) { } //nums라는 가변 파라미터는 [Int]가 된다.

number(of : 1,2,3,4)</code></pre>
<blockquote>
<p>가변 파라미터는 ,를 이용해 여러 아규먼트를 받기 때문에,
가변 파라미터 바로 뒤에 있는 파라미터는 무조건 <code>Argument Label</code>을 가져야 합니다.
또한 기본값을 가질 수 없고, n개의 파라미터중 하나의 파라미터만 사용 할 수 있습니다.</p>
</blockquote>
<h3 id="중첩-함수--추가-공부-필요-">중첩 함수 ( 추가 공부 필요 )</h3>
<p>함수안에 함수를 선언할 수 있습니다.</p>
<pre><code class="language-swift">func company() {
    print(&quot;company&quot;)

    func staff() {
        print(&quot;staff&quot;)
    }
}</code></pre>
<blockquote>
<p>company 함수 내에서 staff 함수를 실행할 수 있지만, 외부에서는 사용이 불가합니다.
하지만, company 함수에서 staff 함수를 리턴한다면 외부에서 사용이 가능합니다. (예시참고)</p>
</blockquote>
<pre><code class="language-swift">func company() -&gt; () -&gt; () {
    print(&quot;company&quot;)

    func staff() {
        print(&quot;staff&quot;)
    }
        return staff
}

let staff = company()
staff() //staff의 타입은 () -&gt; () 이다.</code></pre>
<blockquote>
<p><a href="https://bbiguduk.gitbook.io/swift/">https://bbiguduk.gitbook.io/swift/</a>
<a href="https://babbab2.tistory.com">https://babbab2.tistory.com</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift-2 제어 흐름]]></title>
            <link>https://velog.io/@high_sky8320/Swift-2-%EC%A0%9C%EC%96%B4-%ED%9D%90%EB%A6%84</link>
            <guid>https://velog.io/@high_sky8320/Swift-2-%EC%A0%9C%EC%96%B4-%ED%9D%90%EB%A6%84</guid>
            <pubDate>Wed, 31 Aug 2022 05:46:11 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 제어흐름 (반복문, 조건문) 입니다.</p>
<h3 id="1-for-in-루프">1. For-in 루프</h3>
<table>
<thead>
<tr>
<th>요소</th>
<th>for-in</th>
<th>forEach</th>
</tr>
</thead>
<tbody><tr>
<td>개념</td>
<td>컬렉션에 저장된 요소 수만큼 반복되며, 저장된 요소가 루프 상수에 하나씩 들어간다</td>
<td>반복 실행하려는 코드를 파라미터로 받고, 저장된 요소는 클로저 상수로 전달된다</td>
</tr>
<tr>
<td>형태</td>
<td>for (for문에서만 쓸 변수명) in (변수명) { }</td>
<td>(변수명).forEach { }</td>
</tr>
</tbody></table>
<p>배열에 아이템, 범위의 숫자, 또는 문자열에 문자와 같은 연속된 것에 대해 <code>for</code>-<code>in</code> 루프를 사용하여 반복할 수 있습니다.</p>
<p>형태는 for ( 반복문 안에서 사용할 변수명 ) in ( 선언해둔 변수명 ) { ( 조건 만족시 실행할 구문 ) } 입니다.</p>
<pre><code class="language-swift">let names = [&quot;Anna&quot;, &quot;Alex&quot;, &quot;Brian&quot;, &quot;Jack&quot;]
for name in names {
    print(&quot;Hello, \(name)!&quot;)
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!</code></pre>
<p>딕셔너리의 키-값 쌍 접근을 위해 반복을 사용할 수도 있습니다.</p>
<p><code>Dictionary</code>의 콘텐츠는 순서가 없으며 반복으로 가져올 아이템에 대한 순서를 보장하지 않습니다.</p>
<pre><code class="language-swift">let numberOfLegs = [&quot;spider&quot;: 8, &quot;ant&quot;: 6, &quot;cat&quot;: 4]
for (animalName, legCount) in numberOfLegs {
    print(&quot;\(animalName)s have \(legCount) legs&quot;)
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs</code></pre>
<p>숫자 범위에 대해 <code>for</code>-<code>in</code> 루프를 사용할 수도 있습니다.</p>
<p>범위를 …으로 잡을 수도있고, ..&lt;으로도 잡을 수 있습니다.</p>
<pre><code class="language-swift">for index in 1...5 {
    print(&quot;\(index) times 5 is \(index * 5)&quot;)
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25</code></pre>
<pre><code class="language-swift">let minutes = 60

for tickMark in 0..&lt;minutes {
    // render the tick mark each minute (60 times)
}</code></pre>
<p>시퀀스로 부터 각 값이 필요하지 않으면 변수 이름의 위치에 언더바를 사용하여 값을 무시할 수 있습니다.</p>
<p>( 해당 패턴(_)을 와일드카드 패턴이라고 부른다고 합니다.. )</p>
<pre><code class="language-swift">let base = 3
let power = 10
var answer = 1

for _ in 1...power { //1~10까지 반복
    answer *= base //반복하면 3의 10승 =&gt; 59049
}
print(&quot;\(base) to the power of \(power) is \(answer)&quot;)
//3 to the power of 10 is 59049</code></pre>
<h3 id="2-while-루프">2. While 루프</h3>
<p><code>while</code>루프는 조건이 <code>false</code>가 될 때까지 구문의 집합을 수행합니다. </p>
<p>이러한 루프는 첫번째 반복이 시작되기 전에 반복 횟수를 알 수 없을 때 가장 잘 사용됩니다.</p>
<p>형태는 while (반복조건) { ( 조건 성립시 수행할 구문 ) } 입니다.</p>
<pre><code class="language-swift">let x = 5
let y = 10

while x &lt; y {
    x+=1
    print(x)
}

/*
6
7
8
9
10
*/</code></pre>
<p><code>repeat-while</code>문도 있는데, C언어의 <code>do-while</code>문과 같습니다.</p>
<p>형태는 repeat { ( 수행할 구문 ) } while ( 반복조건 ) 입니다.</p>
<pre><code class="language-swift">let x = 5
let y = 10

repeat {
    x+=1
    print(x)
} while x &lt; y

/*
6
7
8
9
10
*/</code></pre>
<p><code>while</code>문과 <code>repeat-while</code>의 차이점은 </p>
<p><code>repeat-while</code>은 <code>while</code>과 다르게 조건에 안맞아도 일단 한 번은 꼭 실행한다는 점입니다.</p>
<h3 id="3-if">3. If</h3>
<p>👉🏻 만약(if) ~라면(조건) {(행위)}해라. 아니라면(else if / else) {(행위)} 해라.</p>
<pre><code class="language-swift">let temp = 33

if temp &lt;= 20 {
    print(&quot;It&#39;s very cold. Consider wearing a scarf.&quot;)
} else if temp &gt;= 30 {
    print(&quot;It&#39;s really warm. Don&#39;t forget to wear sunscreen.&quot;)
} else {
    print(&quot;It&#39;s not that cold. Wear a t-shirt.&quot;)
}
//It&#39;s really warm. Don&#39;t forget to wear sunscreen.</code></pre>
<p>여기 <code>if</code> 구문은 특정 따뜻한 기온에 응답하기 위해 추가 되었습니다. </p>
<p>마지막 <code>else</code> 절은 남아있고 어떤 기온이 너무 따뜻하거나 너무 춥지 않을 경우에 응답을 출력합니다.</p>
<p>그러나 마지막 <code>else</code> 절은 옵셔널이고, 이 조건이 완벽하게 필요가 없으면 제외할 수 있습니다.</p>
<h3 id="4-switch-case">4. switch-case</h3>
<p>Swift의 <code>switch-case</code>는 C언어와 다르게 첫번째 케이스가 일치하자마자 <code>switch</code> 구문은 완료됩니다.</p>
<p>C언어처럼 여러 case를 실행하지 않습니다.</p>
<pre><code class="language-swift">let someCharacter: Character = &quot;z&quot;

switch someCharacter {
case &quot;a&quot;, &quot;A&quot; :
    print(&quot;The first letter of the alphabet&quot;)
case &quot;z&quot;:
    print(&quot;The last letter of the alphabet&quot;)
default:
    print(&quot;Some other character&quot;)
}
//The last letter of the alphabet</code></pre>
<p><code>switch</code>는 모든 알파벳 문자 뿐만 아니라 모든 가능한 문자에 대한 케이스를 가지고 있어야 하므로</p>
<p>이 <code>switch</code>구문은 <code>a</code>와 <code>z</code>을 제외한 다른 모든 문자는 <code>default</code>케이스를 사용합니다. </p>
<p>⚠️  만약 <code>default</code>를 작성하지 않는다면 “Switch must be exhaustive” 에러가 뜹니다!!</p>
<p>⚠️  각 케이스의 바디는 적어도 하나의 실행가능한 구문이 포함되어야 합니다!!</p>
<h3 id="5-where">5. Where</h3>
<p>조건을 더 추가하고 싶을 때, 특정 타입에 제한을 두고 싶을 때 등 필요할 때마다 도와주는 역할.</p>
<pre><code class="language-swift">let arr: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]

// i 요소가 2로 나눴을때 0일 경우(짝수)일 때만 블록 실행
for i in arr where i % 2 == 0 {
    print(i)
}

/*
2
4
6
8
*/</code></pre>
<pre><code class="language-swift">//더 간단한 예제를 생각해보고 있습니다..

let yetAnotherPoint = (1, -1)

switch yetAnotherPoint {
case let (x, y) where x == y:
    print(&quot;(\(x), \(y)) is on the line x == y&quot;)
case let (x, y) where x == -y:
    print(&quot;(\(x), \(y)) is on the line x == -y&quot;)
case let (x, y):
    print(&quot;(\(x), \(y)) is just some arbitrary point&quot;)
}
//(1, -1) is on the line x == -y</code></pre>
<h3 id="6-제어-변경">6. 제어 변경</h3>
<p><code>continue</code> : 루프를 완전히 벗어나지 않고 현재 루프 반복 완료</p>
<pre><code class="language-swift">//더 간단한 예제를 생각해보고 있습니다..

let puzzleInput = &quot;great minds think alike&quot;
var puzzleOutput = &quot;&quot;
let charactersToRemove: [Character] = [&quot;a&quot;, &quot;e&quot;, &quot;i&quot;, &quot;o&quot;, &quot;u&quot;, &quot; &quot;]

for character in puzzleInput {
    if charactersToRemove.contains(character) { //charactersToRemove의 index에 있는 알파벳이 puzzleInput에도 존재한다면
        continue //루프를 벗어나지 않고 현재 루프 반복
    }
    puzzleOutput.append(character)
}
print(puzzleOutput)
//grtmndsthnklk ( a,e,i,o,u,공백을 제거한 puzzleInput 값을 puzzleOutput에 추가한 결과 )</code></pre>
<p><code>break</code> : 제어흐름 구문을 즉시 종료 ( C언어와 동일 )</p>
<pre><code class="language-swift">let someChar: Character = &quot;z&quot;

switch someChar {
case &quot;a&quot; :
    print(&quot;The last letter of the alphabet&quot;)
default:
    break
}
print(&quot;get out&quot;) //break를 빠져나와 get out 출력</code></pre>
<p>만약 조건에 일치하지 않았다면 break를 타고 switch-case문을 빠져나와 다음 작업을 수행.</p>
<p><code>fallthrough</code> : 다음 case도 동작하도록 만들기</p>
<p><code>fallthrough</code>키워드는 <code>switch-case</code> 실행을 위한 case 조건을 확인하지 않습니다.</p>
<pre><code class="language-swift">//더 간단한 예제를 생각해보고 있습니다..

let integerToDescribe = 5
var description = &quot;The number \(integerToDescribe) is&quot;

switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += &quot; a prime number, and also&quot;
    fallthrough
default:
    description += &quot; an integer.&quot;
}
print(description)
// Prints &quot;The number 5 is a prime number, and also an integer.&quot;</code></pre>
<p> <code>fallthrough</code>를 사용하지 않으면 default에 있는 &quot; an integer.&quot;은 출력되지 않습니다.</p>
<h3 id="7-guard--내용-추가--공부-더-필요--⭐️">7. Guard ( 내용 추가 / 공부 더 필요!! ) ⭐️</h3>
<p>추후 옵셔널 바인딩 공부시 내용을 더 상세히 다룰 예정..</p>
<table>
<thead>
<tr>
<th>종류</th>
<th>if</th>
<th>guard</th>
</tr>
</thead>
<tbody><tr>
<td>의미</td>
<td>만약 ~라면 ~해라 ( 무한 반복 )</td>
<td>~가 아니라면 끝내라 ( 빠른 종료 )</td>
</tr>
<tr>
<td>구문</td>
<td>if 조건 { (true일때 실행될 구문) } else { (false일때 실행될 구문) }</td>
<td>guard 조건 else { (false 일 때 실행될 구문) return }(true 일 때 실행될 구문)</td>
</tr>
<tr>
<td>특징</td>
<td></td>
<td>함수나 메서드 혹은 반복문 안에서만 사용 가능</td>
</tr>
<tr>
<td>장점</td>
<td></td>
<td>가독성을 높여주고 조건문을 만족하지 않을 때 처리가 확실하다.</td>
</tr>
<tr>
<td>차이</td>
<td>else문을 생략할 수 있다.</td>
<td>항상 else문을 가진다.</td>
</tr>
</tbody></table>
<blockquote>
<p>참고 : <a href="https://80000coding.oopy.io/0bd77cd3-7dc7-4cf4-93ee-8ca4fbca898e">https://80000coding.oopy.io/0bd77cd3-7dc7-4cf4-93ee-8ca4fbca898e</a></p>
</blockquote>
<blockquote>
<p><a href="https://bbiguduk.gitbook.io/swift/">https://bbiguduk.gitbook.io/swift/</a>
<a href="https://babbab2.tistory.com">https://babbab2.tistory.com</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift-1 변수]]></title>
            <link>https://velog.io/@high_sky8320/Swift-1-%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@high_sky8320/Swift-1-%EB%B3%80%EC%88%98</guid>
            <pubDate>Wed, 31 Aug 2022 05:37:03 GMT</pubDate>
            <description><![CDATA[<p>스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 기록할 차시는 변수입니다. ( 기본 / 기본연산자 / 문자열과 문자 / 콜렉션 타입 )</p>
<h3 id="미리보기-🥄">미리보기 🥄</h3>
<pre><code class="language-swift">var number : Int = 10
var plusnumber : UInt = 10 //양의 정수 타입. 64비트 양의 정수형

var sosu1 : Float = 3.14 //32비트 부동소수형
var sosu2 : Double = 3.14 //64비트 부동소수형

var chars : Character = &quot;Hello World!!&quot; //문자 타입
var spring : String = &quot;Hello World!!&quot; //문자열 타입 (추후 자세히 다룰 예정)

var choose : Bool = false //true나 false만 가능

var all : Any = 100 //스위프트의 모든 타입을 지칭
all = &quot;아무거나 넣을 수 있다&quot; //마지막에 String 타입의 값을 넣었더라도 Any는 String이 아니기 때문에 할당 x
all = spring //ERROR

var box : Array&lt;Int&gt; = Array&lt;Int&gt;()
var integers = [Int]() //축약 (추후 자세히 다룰 예정)

var anyDic : Dictionary&lt;String, Any&gt; = [String : Any]() //[key : value]타입
var anyDictionary = [String : Any]() //축약

var integerSet : Set&lt;Int&gt; = Set&lt;Int&gt;() //중복되지 않는 멤버가 순서 없이 존재하는 컬렉션 (축약형 없음)</code></pre>
<h3 id="1-타입-명시-타입-추론">1. 타입 명시, 타입 추론</h3>
<p>Swift는 타입-세이프 언어입니다. 때문에 <strong>Swift는 특정 타입을 지정하지 않으면 타입 추론을 사용합니다.</strong> </p>
<p>덕분에 개발 단계에서 가능한 빨리 오류를 포착하고 수정할 수 있습니다. </p>
<pre><code class="language-swift">let save = 500000 // save is inferred to be of type Int
var spend : String = 800000 // Cannot convert value of type &#39;Int&#39; to specified type &#39;String&#39;</code></pre>
<p>위의 예시처럼 spend는 Int형인데 String으로 타입 명시를 해버려서 틀렸다는 에러를 띄워줍니다.</p>
<blockquote>
<p>Cannot convert value of type &#39;Int&#39; to specified type &#39;String&#39;</p>
</blockquote>
<h3 id="2-let과-var">2. <code>let</code>과 <code>var</code></h3>
<p>상수의 값은 최초 지정 후 변경이 불가능하지만 변수는 다른 값으로 변경이 가능합니다.</p>
<p>상수와 변수는 사용하기 전에 반드시 선언이 되어야 하며, </p>
<p>상수는 <code>let</code>키워드와 함께 선언하고 변수는 <code>var</code>키워드와 함께 선언합니다.</p>
<pre><code class="language-swift">//8월
let save = 500000 //저축은 매달 50만원 고정이므로 let 선언
var spend = 400000 //지출액은 매달 변동되므로 var 선언

//9월
save += 200000 [ ⛔️ Left side of mutating operator isn&#39;t mutable: &#39;save&#39; is a &#39;let&#39; constant ]
spend += 200000 //9월은 명절으로 지출이 20만원 늘었다 (변경가능!!)</code></pre>
<p><strong>코드에서 저장한 값이 변경되지 않는다면 항상 <code>let</code>키워드로 상수로 선언해야 합니다.</strong> </p>
<p><code>var</code>는 오직 값을 저장하고 변경이 필요할 때 선언합니다.**</p>
<h3 id="3-연산자--내용-추가-필요-">3. 연산자 ( 내용 추가 필요!! )</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf3e2923-6ee7-4f9c-8a12-2fc5ea063970/Untitled.png" alt="Untitled"></p>
<p>👉🏻  <code>is</code>, <code>as</code>, <code>as?</code>, <code>as!</code>는 타입캐스팅으로 추후 다룰 예정!!</p>
<h3 id="4-string과-character--⭐️">4. <code>String</code>과 <code>Character</code>  ⭐️</h3>
<p><code>String</code>은 문자열 타입이며, <code>Character</code>는 문자 타입입니다!!!!!</p>
<p>하단에 유용한 String 관련 함수들을 작성해보았습니다.</p>
<ul>
<li>빈 문자열 초기화 [ (변수명).isEmpty ]</li>
</ul>
<pre><code class="language-swift">var emptyStr = &quot;&quot; //혹은 &quot;&quot; 말고 String()도 가능!!

if emptyStr.isEmpty {
    print(&quot;isEmpty here.&quot;)
}
//isEmpty here.</code></pre>
<ul>
<li>문자열 삽입 [ (변수명) ]</li>
</ul>
<pre><code class="language-swift">let multiplier = 3
let message = &quot;\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)&quot;
//3 times 2.5 is 7.5</code></pre>
<ul>
<li>문자 카운팅 [ (변수명).count ]</li>
</ul>
<p>문자열에서 <code>Character</code> 값의 카운트를 구하려면 문자열에서 <code>count</code>프로퍼티를 사용합니다</p>
<pre><code class="language-swift">var word = &quot;cafe&quot;
print(&quot;the number of characters in \(word) is \(word.count)&quot;)
//the number of characters in cafe is 4</code></pre>
<ul>
<li>문자열 접근과 수정 [ (변수명).___Index ]</li>
</ul>
<p><code>String</code>의 메서드 <code>index(before:)</code>와 <code>index(after:)</code>를 사용하여 주어진 인덱스의 전과 후에 접근할 수 있습니다. 주어진 인덱스에서 먼 인덱스에 접근하려면 이러한 메서드를 여러번 호출하는 대신 <code>index(_:offsetBy:)</code> 메서드를 사용할 수 있습니다.</p>
<pre><code class="language-swift">let greeting = &quot;Guten Tag!&quot;
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a</code></pre>
<p>문자열 범위에 벗어나는 인덱스로 접근하거나, 문자열 범위에 벗어나는 인덱스의 <code>Character</code>를 접근하려고 하면 런타임 에러가 발생합니다.</p>
<ul>
<li>삽입과 삭제</li>
</ul>
<pre><code class="language-swift">var welcome = &quot;hello&quot;
welcome.insert(&quot;!&quot;, at: welcome.endIndex) //hello의 마지막인덱스에 !를 삽입하라.
//hello!

welcome.insert(contentsOf: &quot; there&quot;, at: welcome.index(before: welcome.endIndex)) //hello!에 there을 삽입하되, 마지막인덱스인 ! 전에 삽입하라.
//hello there!</code></pre>
<pre><code class="language-swift">welcome.remove(at: welcome.index(before: welcome.endIndex)) //hello there!에서 마지막인덱스인 !를 삭제하라.
//hello there

let range = welcome.index(welcome.endIndex, offsetBy: -6)..&lt;welcome.endIndex
welcome.removeSubrange(range)
//hello</code></pre>
<ul>
<li>문자열에서 원하는 문자 찾기</li>
</ul>
<table>
<thead>
<tr>
<th>유형</th>
<th>hasPrefix</th>
<th>hasSuffix</th>
<th>contains</th>
</tr>
</thead>
<tbody><tr>
<td>용도</td>
<td>앞에 이거 있어? (접두사)</td>
<td>뒤에 이거 있어? (접미사)</td>
<td>이거 포함됨? (요소)</td>
</tr>
<tr>
<td>리턴 타입</td>
<td>Bool</td>
<td>Bool</td>
<td>Bool</td>
</tr>
<tr>
<td>공통점</td>
<td>대소문자를 구별함</td>
<td>대소문자를 구별함</td>
<td>대소문자를 구별함</td>
</tr>
</tbody></table>
<p><code>hasPrefix</code>와 <code>hasSuffix</code>를 이용하여 원하는 문자를 찾을 수 있습니다.</p>
<pre><code class="language-swift">let drink = &quot;Cafe Latte&quot;
let dessert = &quot;chocolates&quot;

print(drink.hasPrefix(&quot;cafe Latte&quot;)) // false - 소문자 c로 시작했기 때문입니다.

print(drink.hasPrefix(&quot;Cafe Latte&quot;)) // true

print(dessert.hasSuffix(&quot;Chocolate&quot;)) // false - 대문자 C이기 때문입니다.

print(dessert.hasSuffix(&quot;chocolates&quot;)) // true
print(dessert.hasSuffix(&quot;chocolate&quot;)) // false</code></pre>
<p><code>hasPrefix</code> 처럼 <code>hasSuffix</code>도 대소문자를 구별하기 때문에 위와 같은 결과를 반환합니다.</p>
<p><code>hasPrefix</code>는 앞으로 검색할 때, <code>hasSuffix</code>는 뒤에서 검색할 때 사용합니다.</p>
<p><code>contains</code>도 마찬가지로 원하는 문자를 찾을 수 있습니다. 배열도, 문자열도 모두 가능합니다.</p>
<p>보통 일부 문자를 포함하는지 확인할 때 많이 사용합니다.</p>
<pre><code class="language-swift">let drink = [&quot;Cafe Latte&quot;, &quot;Vanilla Latte&quot;]
let hello = &quot;Thanks. Here is my own cafe&quot;

print(hello.contains(&quot;Thank&quot;)) // true
print(hello.contains(&quot;Thanks&quot;)) // true

print(hello.contains(&quot;x&quot;)) // false - x라는 문자가 하나도 없습니다.

print(hello.contains(&quot;t&quot;)) // false
print(hello.contains(&quot;T&quot;)) // true</code></pre>
<blockquote>
<p>참고 : <a href="https://velog.io/@un1945/Swift-hasPrefix-hasSuffix-contains">https://velog.io/@un1945/Swift-hasPrefix-hasSuffix-contains</a></p>
</blockquote>
<h3 id="5-배열-딕셔너리-셋---내용-추가-필요--⭐️">5. 배열, 딕셔너리, 셋  ( 내용 추가 필요!! ) ⭐️</h3>
<table>
<thead>
<tr>
<th>이름</th>
<th>형태</th>
</tr>
</thead>
<tbody><tr>
<td>Array</td>
<td>var someArray = [1,2,3]</td>
</tr>
<tr>
<td>Dictionary</td>
<td>var someDic = [ “code” : 1, “number” : 2, “age” : 3 ]</td>
</tr>
<tr>
<td>Set</td>
<td>var someSet : Set<String> = [&quot;Rock&quot;, &quot;Classical&quot;, &quot;Hip hop&quot;]</td>
</tr>
</tbody></table>
<h4 id="5-1-배열">5-1. 배열</h4>
<ul>
<li>빈 배열 생성하기</li>
</ul>
<pre><code class="language-swift">//1. 타입 추론으로 생성하기
var someArray = [1,2,3]
var someArray = [] // ⛔️ Empty collection literal requires an explicit type

//2. 타입 Annotation으로 생성하기
var someArray: [Int] = [1,2,3]
var someArray: [Int] = []

//3. 생성자로 생성하기
var someArray = Array&lt;Int&gt;()
var someArray = [Int]()
var someArray = [Int](repeating: 10, count: 0)   //생성과 동시에 10개 Element 생성 및 0으로 초기화</code></pre>
<ul>
<li>갯수 확인하기</li>
</ul>
<pre><code class="language-swift">var someArray = [1,2,3]

let count: Int = someArray.count      // 배열 갯수 확인 : 3
let isEmpty: Bool = someArray.isEmpty // 배열 비었는지 확인 : false</code></pre>
<ul>
<li>요소 추가</li>
</ul>
<pre><code class="language-swift">var someArray = [1,2,3]

someArray.append(4) // [1, 2, 3, 4]
someArray.append(contentsOf: [5,6,7]) // [1, 2, 3, 4, 5, 6, 7]

someArray.insert(4, at: 3) // [1, 2, 3, 4]
someArray.insert(contentsOf: [5,6,7], at: 4) // [1, 2, 3, 4, 5, 6, 7]</code></pre>
<p>⚠️  배열의 경우 index로 접근하니 ,insert를 해주면 insert를 하는 위치부터 배열을 재배치 해야 하기 때문에 오버헤드가 발생한다. 꼭 필요하지 않다면 append를 사용하는게 좋다.</p>
<ul>
<li>요소 변경</li>
</ul>
<pre><code class="language-swift">var someArray = [1, 2, 3]

//값 변경하기 - Subscript, 범위로 접근하여 값 변경

someArray[0] = 10 // [10, 2, 3]
someArray[0...2] = [0] // [0]
someArray[0..&lt;1] = [] // []

//값 변경하기 - replaceSubrange (범위변경)
someArray.replaceSubrange(0...2, with: [10, 20, 30])     // [10, 20, 30]
someArray.replaceSubrange(0...2, with: [0])              // [0]
someArray.replaceSubrange(0..&lt;1, with: [])               // []

//요소 바꾸기
var someArray = [1, 2, 3, 4, 5]
someArray.swapAt(0, 4) // [5, 2, 3, 4, 1]</code></pre>
<ul>
<li>요소 삭제</li>
</ul>
<pre><code class="language-swift">var someArray = [1, 2, 3, 4, 5, 6, 7, 8. 9]

//삭제하기
array1.remove(at: 8)             // [1, 2, 3, 4, 5, 6, 7, 8] 
array1.removeFirst()             // [2, 3, 4, 5, 6, 7, 8, 9]   
array1.removeFirst(2)            // [4, 5, 6, 7, 8, 9]
array1.removeLast()              // [5, 6, 7, 8]
array1.popLast()                 // [5, 6, 7] 
array1.removeLast(2)             // [5]
array1.removeAll()               // [] 

//특정 범위 삭제하기 
array2.removeSubrange(1...3)     // [1, 5, 6, 7, 8, 9] 
array2[0..&lt;2] = []               // [6, 7, 8, 9]</code></pre>
<ul>
<li>요소 검색</li>
</ul>
<p>배열에서는 <code>contains</code>를 사용합니다. 앞서 설명했던 그 <code>contains</code>입니다. </p>
<table>
<thead>
<tr>
<th>first</th>
<th>firstIndex</th>
</tr>
</thead>
<tbody><tr>
<td>가장 첫 번째 요소의 &quot;값&quot;을 리턴</td>
<td>가장 첫 번째 요소의 &quot;Index&quot;을 리턴</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>last</th>
<th>lastIndex</th>
</tr>
</thead>
<tbody><tr>
<td>가장 마지막 요소의 &quot;값&quot;을 리턴</td>
<td>가장 마지막 요소의 &quot;Index&quot;을 리턴</td>
</tr>
</tbody></table>
<pre><code class="language-swift">var someArray = [1, 2, 3, 2, 5]

someArray.contains(1) //true

someArray.contains { num in // true
    num % 2 == 0
}

if let firstIndex = someArray.firstIndex(of: 2) {
    print(firstIndex)  // 1
}

if let lastIndex = someArray.lastIndex(of: 2) {
    print(lastIndex)  // 3
}</code></pre>
<ul>
<li>정렬</li>
</ul>
<table>
<thead>
<tr>
<th>이름</th>
<th>sort</th>
<th>sorted</th>
</tr>
</thead>
<tbody><tr>
<td>공통(형태)</td>
<td>sort() = 오름차순 정렬</td>
<td>sorted() = 오름차순 정렬</td>
</tr>
<tr>
<td></td>
<td>sort(by : &gt; ) = 내림차순 정렬</td>
<td>sorted(by : &gt; ) = 내림차순 정렬</td>
</tr>
<tr>
<td>차이</td>
<td>배열 원본을 건드리고 배열을 리턴</td>
<td>배열 원본은 건들지 않고 정렬된 새로운 배열을 리턴</td>
</tr>
</tbody></table>
<pre><code class="language-swift">var someArray = [1, 5, 3, 8, 6, 10, 14]

someArray.sort() // [1, 3, 5, 6, 8, 10, 14]
someArray.sort(by: &gt;) // [14, 10, 8, 6, 5, 3, 1]

let sortedArray = someArray.sorted() // [1, 3, 5, 6, 8, 10, 14]
let sortedArray2 = someArray.sorted(by: &gt;) // [14, 10, 8, 6, 5, 3, 1]</code></pre>
<blockquote>
<p>참고 : <a href="https://babbab2.tistory.com/92">https://babbab2.tistory.com/92</a></p>
</blockquote>
<h4 id="5-2-딕셔너리">5-2. 딕셔너리</h4>
<p>형태는 배열과 같이 []에 있으나 배열과 다르게 [key:value] 타입이며,</p>
<p><code>Key</code>는 딕셔너리 키로 사용되는 값의 타입이고 <code>Value</code>는 딕셔너리에 저장될 값의 타입이다.</p>
<p>⚠️  Swift는 타입에 민감하기에 모든 Key의 자료형은 같아야 하고, 모든 Value의 자료형도 같아야 한다.</p>
<pre><code class="language-swift">//딕셔너리 한 개에 여러 타입의 Value를 저장하고 싶을 경우
var dic : [String: Any] = [&quot;name&quot;: &quot;Dotzero&quot;, &quot;age&quot;: 19]

//단, Key값은 무조건 Hashable이란 프로토콜을 준수하는 자료형만 올 수 있음.
// ⛔️ Type &#39;Any&#39; does not conform to protocol &#39;Hashable&#39;
var dic : [Any : Any] = [1 : &quot;Dotzero&quot;, &quot;age&quot;: 19]</code></pre>
<p>Any를 남발하는 것은 정적 바인딩 언어인 Swift를 동적 바인딩 시키는 것이기 때문에 최대한 쓰지말자.</p>
<ul>
<li>요소 접근</li>
</ul>
<pre><code class="language-swift">var dic : [String: Int] = [&quot;code&quot;: 04, &quot;age&quot;: 19]

//해당 키값이 없을 수도 있으니 기본 반환값은 Optional Type 이다.
let number = dic[&quot;code&quot;] // Optional(04)
let fault = dic[&quot;phone&quot;] // nil

//Optional Type이 싫다면 default 값을 직접 명시
let number = dic[&quot;code&quot;, default: 05] //4
let fault = dic[&quot;phone&quot;, default: 8212345678] //8212345678</code></pre>
<ul>
<li>요소 추가 / 삭제</li>
</ul>
<pre><code class="language-swift">var dic : [String: Int] = [&quot;code&quot;: 04, &quot;age&quot;: 19]

dic.updateValue(05, forKey: &quot;code&quot;) // [&quot;code&quot;: 5, &quot;age&quot;: 19]
dic.updateValue(20, forKey: &quot;age&quot;) // [&quot;code&quot;: 5, &quot;age&quot;: 20]

dic.removeValue(forKey: &quot;code&quot;) // [&quot;age&quot;: 20]
dic.removeValue(forKey: &quot;age&quot;) // [:]

dic.removeAll() // [:]</code></pre>
<ul>
<li>요소 검색</li>
</ul>
<pre><code class="language-swift"></code></pre>
<h4 id="5-3-튜플">5-3 튜플</h4>
<p>  익명의 구조체..같은 느낌?</p>
<p>솔직히 보통은 배열이나 딕셔너리를 많이 사용하다보니 튜플이 많이 낯설긴 하다.</p>
<pre><code class="language-swift">let zero = (name : &quot;hyi&quot;, age : 19, city : Seoul)

print(zero.name) //hyi
print(zero.age) //19
print(zero.city) //Seoul </code></pre>
<blockquote>
<p><a href="https://bbiguduk.gitbook.io/swift/">https://bbiguduk.gitbook.io/swift/</a>
      <a href="https://babbab2.tistory.com">https://babbab2.tistory.com</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 프로그래머스 ] 신규 아이디 추천]]></title>
            <link>https://velog.io/@high_sky8320/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B7%9C-%EC%95%84%EC%9D%B4%EB%94%94-%EC%B6%94%EC%B2%9C</link>
            <guid>https://velog.io/@high_sky8320/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B7%9C-%EC%95%84%EC%9D%B4%EB%94%94-%EC%B6%94%EC%B2%9C</guid>
            <pubDate>Sun, 24 Apr 2022 13:24:03 GMT</pubDate>
            <description><![CDATA[<p>역시 경험을 해보는게 최고인 것 같습니다,..🥲
여태까지 연습문제 lv1을 풀다가 처음으로 카카오 lv1 문제를 풀어보았습니다.
확실히 문제도 뭔가 다르긴 하더라고요.. 대신 lv1이라 그런가 지문이 직설적인? 느낌이 강했습니다.</p>
<blockquote>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/72410">https://programmers.co.kr/learn/courses/30/lessons/72410</a></p>
</blockquote>
<p>문제를 읽어보니 요점은 이 부분인 것 같았습니다.</p>
<blockquote>
<p>1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
2단계 new_id에서 알파벳 소문자, 숫자, 빼기, 밑줄, 마침표를 제외한 모든 문자를 제거합니다.
3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
5단계 new_id가 빈 문자열이라면, new_id에 &quot;a&quot;를 대입합니다.
6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
     만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.</p>
</blockquote>
<p>저같은 경우, 각 단계별로 코드를 작성하였습니다.
정답을 맞추고 보니 단계별로 하신분들도 있고, 합칠 수 있는 부분은 합치신분도 계시더라고요.</p>
<pre><code class="language-swift">import Foundation

func solution(_ new_id:String) -&gt; String {

    var own = new_id

    own = own.lowercased()

    own = own.filter{ $0.isLetter || $0.isNumber || $0 == &quot;-&quot; || $0 == &quot;_&quot; || $0 == &quot;.&quot;}

    while own.contains(&quot;..&quot;) {
        own = own.replacingOccurrences(of: &quot;..&quot;, with: &quot;.&quot;)
    }

    if own.hasPrefix(&quot;.&quot;) {
        own.removeFirst()
    }

    if own.hasSuffix(&quot;.&quot;) {
        own.removeLast()
    }

    if own.isEmpty {
        own = &quot;a&quot;
    }

    if own.count &gt;= 16 {
        let ranges = own.index(own.startIndex, offsetBy: 15)..&lt;own.endIndex
        own.removeSubrange(ranges)

        if own.hasSuffix(&quot;.&quot;) {
            own.removeLast()
        }
    }

    if own.count &lt;= 2 {
        while own.count != 3 {
            own += String(own.last!)
        }
    }

    return own
}</code></pre>
<p>작성하기 전에 new_id 값을 받는 변수를 하나 만들어주었습니다. ( own )
1단계는 lowercased()를 이용하여 대문자를 모두 소문자로 만들었습니다.</p>
<pre><code class="language-swift">own = own.lowercased()</code></pre>
<p>2단계는 처음에 생각했을때 filter로 거르면 되겠다!라고 생각은 했는데 이후에 작성을 어떻게 해야할지 감이 안잡혀서 컴포넌트, 정규식등 별의별 친구를 다찾아본 것 같네요..😅
해당부분은 구글링의 힘을 빌려서 작성할 수 있었습니다.</p>
<blockquote>
<p><a href="https://jercy.tistory.com/10">https://jercy.tistory.com/10</a></p>
</blockquote>
<pre><code class="language-swift">own = own.filter{ $0.isLetter || $0.isNumber || $0 == &quot;-&quot; || $0 == &quot;_&quot; || $0 == &quot;.&quot;}</code></pre>
<p>3단계는 ..인 부분을 어떻게 솎아내는지와 어떻게 치환하는지 문제였습니다.
검색해보니 contains()를 이용하여 솎아내고 replacingOccurrences를 이용하여 ..을 .으로 변경할 수 있다고 하여 사용하였습니다. ( 확실히 코드가 짧네요 😀 )</p>
<blockquote>
<p><a href="https://ios-development.tistory.com/373">https://ios-development.tistory.com/373</a></p>
</blockquote>
<pre><code class="language-swift">while own.contains(&quot;..&quot;) {
        own = own.replacingOccurrences(of: &quot;..&quot;, with: &quot;.&quot;)
}</code></pre>
<p>4단계는 contains()를 찾다가 발견하였는데, hasPrefix, hasSuffix를 이용하여 간편하게
처음과 끝에 있는 .을 제거할 수 있어보여 사용하였습니다. 😀</p>
<blockquote>
<p><a href="https://velog.io/@un1945/Swift-hasPrefix-hasSuffix-contains">https://velog.io/@un1945/Swift-hasPrefix-hasSuffix-contains</a></p>
</blockquote>
<pre><code class="language-swift">if own.hasPrefix(&quot;.&quot;) {
    own.removeFirst()
}

if own.hasSuffix(&quot;.&quot;) {
    own.removeLast()
}</code></pre>
<p>5단계는 간단하게 isEmpty로 문자열이 비었는지 판단하고 비었으면 a를 넣게끔 하였습니다.</p>
<pre><code class="language-swift">if own.isEmpty {
    own = &quot;a&quot;
}</code></pre>
<p>6단계는 count로 문자갯수를 판단하였고 &quot;첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.&quot; 이 문구가 감이 안잡혔는데 검색하여 해당자료를 토대로 작성하였습니다.
index의 처음부분부터 15번째까지 살리고 나머지부분을 removeSubrange로 제거하였습니다.</p>
<blockquote>
<p><a href="https://jinnify.tistory.com/52">https://jinnify.tistory.com/52</a></p>
</blockquote>
<pre><code class="language-swift">if own.count &gt;= 16 {
    let ranges = own.index(own.startIndex, offsetBy: 15)..&lt;own.endIndex
    own.removeSubrange(ranges)

    if own.hasSuffix(&quot;.&quot;) {
        own.removeLast()
    }
}</code></pre>
<p>7단계는 지문 그대로.. 작성하였습니다. 😀</p>
<pre><code class="language-swift">if own.count &lt;= 2 {
    while own.count != 3 {
        own += String(own.last!)
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 풀 때 유용한 Swift 문법 정리]]></title>
            <link>https://velog.io/@high_sky8320/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%92%80-%EB%95%8C-%EC%9C%A0%EC%9A%A9%ED%95%9C-Swift-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@high_sky8320/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%92%80-%EB%95%8C-%EC%9C%A0%EC%9A%A9%ED%95%9C-Swift-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 30 Mar 2022 03:10:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문제를 무작정 많이 푸는 것도 하나의 방법이겠지만 알고있으면 더 효율적이게 짤 수 있어요. 😀
해당 포스트의 예시가 문법적으로 오류가 있다면 댓글 달아주시면 감사하겠습니다!</p>
</blockquote>
<h3 id="입력-⌨️">입력 ⌨️</h3>
<blockquote>
<p>대부분의 사이트는 자체 값을 대입해서 돌립니다. 
하지만 내가 직접 입력예시의 값을 주어서 테스트 하고 싶을 때 쓰면 좋습니다. 😀
유의할 점은 readline은 playground에서 동작하지 않습니다!! command line tool을 이용하세요!</p>
</blockquote>
<pre><code class="language-swift">import Foundation

//readline의 리턴형은 String이므로 Int등은 형변환을 해줘야합니다!!
let myName = readLine()! //정수 한개 입력
var number = Int(readLine()!)! //Int로 바로 읽어오기

//공백 단위로 입력받기
let nums = readLine()!.split(seperator:&quot; &quot;) 
let nums = readLine()!.components(seperatedBy:&quot; &quot;) //Substring이 아닌 String으로 처리

//정수 여러 개 입력받기 ( String -&gt; Int 형변환 )
let nums : [Int] = readLine()!.split(seperator:&quot; &quot;).map { Int(String($0))! }
</code></pre>
<blockquote>
<p>components는 foundation의 instance method이므로 
이를 사용하려면 import Foundation 해주어야 합니다!!</p>
</blockquote>
<h3 id="배열-🛍">배열 🛍</h3>
<pre><code class="language-swift">import Foundation

//반복
var arr = Array(1...5) // [1,2,3,4,5]
var arr = Array(repeating: 1, count: 5) // [1,1,1,1,1]

//정렬
var arr = [1,3,2,4]

arr.sort() // 오름차순 [1,2,3,4]
arr.sort(by: &gt;) // 내림차순 [4,3,2,1]
arr.reverse() // 순서반전 [4,2,3,1]

//최대, 최소
var min = arr.min()! //최소 1
var max = arr.max()! //최대 4</code></pre>
<blockquote>
<p>위 친구들은 꽤 자주쓰이니 메모해두면 좋습니다. 😀
Sort는 원본을 변경하여 정렬하지만, Sorted는 원본 변경이 없게 배열을 정렬합니다.</p>
</blockquote>
<pre><code class="language-swift">import Foundation

var arr = [1,2,3,4,5]

arr.append(6) // [1,2,3,4,5,6]

arr[0] // [1]
arr.firstIndex(of : 1) // 0 

arr.remove(at : 2) // [1,2,4,5,6]
arr.removeLast() // [1,2,4,5]
arr.removeFirst() // [2,4,5]

arr.insert(3, at : 2) // [2,3,4,5]
arr.contains(1) // false

arr.first! // 첫 원소 리턴 2
arr.last! // 마지막 원소 리턴 5
arr.popLast()! // 마지막 원소를 지우고 리턴 [2,3,4]

arr.removeAll() //모든 원소 지우기
arr.removeAll(where : { $0 % 2 == 0 }) //조건을 만족하는 모든 원소 지우기
</code></pre>
<blockquote>
<p>아직 많이 경험해보진 않아서 이렇다 말은 못해도
firstIndex, remove, insert 부분은 알아두는게 좋은 것 같습니다.</p>
</blockquote>
<h3 id="맵-🗺">맵 🗺</h3>
<blockquote>
<p>시작하기 전에 맵,필터,리듀스는 고차함수입니다. 
작성할 세 개 말고도 여러 고차함수가 존재하며, 모두 알고있으면 좋습니다. 😀</p>
</blockquote>
<p>그럼 맵에 대해 알아봅시다.</p>
<blockquote>
<p>&quot; 기존의 컨테이너의 요소에 대해 정의한 클로저로 매핑한 결과를 새로운 컨테이너로 반환합니다. &quot;
아까 전에 readLine()에도 봤던 map의 개념입니다 😀
막상 저 한 문장으로 보니까 긴가민가하네요 😅</p>
</blockquote>
<pre><code class="language-swift">import Foundation

var array = [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;]
array.map { Int($0)! } // 각 원소를 전부 Int형으로 맵핑

print(array) // [1,2,3,4,5]</code></pre>
<h3 id="필터-🧺">필터 🧺</h3>
<blockquote>
<p>&quot;기존 컨테이너의 요소에 대해 조건에 만족하는 값에 대해서 새로운 컨테이너로 반환합니다.&quot;
filter의 개념인데요. 그냥 요약하자면 말그대로 필터링이에요! 
조건에 만족하는 데이터를 추출하는겁니다. 😀</p>
</blockquote>
<pre><code class="language-swift">import Foundation

let number = [1,2,3,4,5,6,7,8,9]
let filNumber = number.filter { $0 % 2 == 0 }

print(number) // [2,4,6,8]</code></pre>
<blockquote>
<p>🚨 여기서 잠깐! $0 은 뭐를 뜻하는 걸까?
Swift는 인라인 클로저에서 인수이름을 간단하게 사용하는 방법을 제공합니다.
순서에 따라 $0, $1, $2 등과 같이 단순화 시킬 수 있습니다.
여기서는 number이 $0으로 단순화 된 것이겠네요 😀</p>
</blockquote>
<h3 id="리듀스-➕">리듀스 ➕</h3>
<blockquote>
<p>&quot; 정의한 클로저를 사용하여 기존 컨테이너의 요소를 결합한 결과를 반환하는 고차함수 &quot;
reduce는 연산문제를 할 때 유용하게 쓰이는 고차함수 입니다 😀</p>
</blockquote>
<pre><code class="language-swift">import Foundation

let array = [1,2,3,4,5]
let results = array.reduce(1, *) // 숫자의 곱
//let results = array.reduce(1) { $0 * $1 } 도 가능!

print(results) // 120</code></pre>
<h3 id="컴포넌트-🎞">컴포넌트 🎞</h3>
<blockquote>
<p>Components 는 separateBy 값에 들어간 문자나 공백 기준으로 쪼갭니다.
split이랑 다르게 문자를 작성할 수 있습니다 😀</p>
</blockquote>
<pre><code class="language-swift">import Foundation

let s = &quot;Have a nice Day!&quot;
s.components(separatedBy: &quot;a&quot;) //[&quot;H&quot;, &quot;ve &quot;, &quot; nice D&quot;, &quot;y!&quot;]
s.components(separatedBy: &quot;a&quot;).count //4</code></pre>
<h3 id="수학math-함수-🧮">수학(Math) 함수 🧮</h3>
<pre><code class="language-swift">import Foundation

let number = 9.0

//거듭제곱
pow(number, 2) // 81.0

//제곱근
sqrt(number) // 3.0</code></pre>
<h3 id="기타-☕️">기타 ☕️</h3>
<pre><code class="language-swift">import Foundation

if qwertz.isEmpty { //만약 qwertz가 비어있으면..
    print(&quot;Nothing to see here&quot;)
} 
print(&quot;Not empty here.&quot;)</code></pre>
<pre><code class="language-swift">import Foundation

let s = &quot;Watèr is the Best&quot;
print(s.lowercased()) //watèr is the best
print(s.uppercased()) //WATÈR IS THE BEST</code></pre>
<p>추가중... 🐌</p>
<h3 id="참고자료-📑">참고자료 📑</h3>
<blockquote>
<p><a href="https://icksw.tistory.com/9">https://icksw.tistory.com/9</a>
<a href="https://chanhhh.tistory.com/70">https://chanhhh.tistory.com/70</a>
<a href="https://velog.io/@un1945/Swift-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98-Higher-order-Function">https://velog.io/@un1945/Swift-고차함수-Higher-order-Function</a>
<a href="https://velog.io/@folw159/Swift-components%EC%99%80-split%EC%9D%98-%EC%B0%A8%EC%9D%B4">https://velog.io/@folw159/Swift-components와-split의-차이</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[RxSwift 입문기]]></title>
            <link>https://velog.io/@high_sky8320/RxSwift-%EC%9E%85%EB%AC%B8%EA%B8%B0</link>
            <guid>https://velog.io/@high_sky8320/RxSwift-%EC%9E%85%EB%AC%B8%EA%B8%B0</guid>
            <pubDate>Sat, 01 Jan 2022 15:00:03 GMT</pubDate>
            <description><![CDATA[<p>노션에 따로 기록해두었던 RxSwift의 개념을 Velog에 옮기기로 했다.</p>
<h3 id="시작하기-전에">시작하기 전에..</h3>
<p>영상과 여러 자료를 바탕으로 정리해두긴 했지만, 틀린 내용이 있을 수도 있습니다. 
굉장히 중구난방으로 순서가 되어있습니다.. 피드백 환영합니다 🙂</p>
<p>솔직히 자신이 영어 독해를 잘한다 싶으면 공식문서만 보셔도 이해는 가능합니다.
못한다고 해도 공식문서에 있는 그림은 이해를 할 수 있어야 한다.</p>
<h3 id="rxswift-그게-뭔데-🤔">RxSwift? 그게 뭔데? 🤔</h3>
<blockquote>
<p>RxSwift란 비동기적으로 생기는 데이터를 completion 같은 closure로 전달하는게 아니라 return 값으로 전달하기 위해서 만들어진 유틸리티 이다.</p>
</blockquote>
<p>여기서 말하는 비동기란..
동기 방식은 서버에서 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행할 수 있는 것을 말하고,
비동기 방식은 요청을 보냈을 때 응답 상태와 상관없이 다음 동작을 수행 할 수 있는 것을 말한다.</p>
<pre><code class="language-swift">//optional 일 경우는 default 이므로 명시안해도 된다. 
//((String?) -&gt; Void)?)라면 @escaping이 필요없다는것이다.
func downloadJson(_ url : String, _ completion: @escaping (String?) -&gt; Void) {
    DispatchQueue.global().async {
            let url = URL(string: MEMBER_LIST_URL)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                completion(json)
            }
        }
 }</code></pre>
<p>👉🏻 해당 방식이 RX가 없는 DispatchQueue를 이용한 비동기 처리 방식이다.
👉🏻 비동기로 생긴 데이터를 어떻게 리턴값으로 만들지? 라는 고민을 해결해준게 RX이다.</p>
<pre><code class="language-swift">//예시 ( 주석값은 RxSwift를 적용했을 때를 나타낸다 )
class 나중에생기는데이터&lt;T&gt; { //-&gt; class Observable&lt;T&gt;
    private let task: (@escaping (T) -&gt; Void) -&gt; Void
    init(task: @escaping (@escaping (T) -&gt; Void) -&gt; Void) {
        self.task = task
    }
    func 나중에오면(_ f: @escaping (T) -&gt; Void){ //-&gt; func subscribe
        tast(f)
    }
}

...

func downloadJson(_ url : String -&gt; 나중에생기는데이터&lt;String?&gt;) 
    return 나중에생기는데이터(){ f in
        f(json)
    }</code></pre>
<p>👉🏻 이런식으로 한다면 원하는 동작을 그대로 처리할 수 있으면서 나중에 생기는 데이터를 completion을 쓰지않고 리턴 값으로 처리하는 방식이 된다. </p>
<h3 id="본격적으로-예제-접해보기-observable">본격적으로 예제 접해보기~ Observable!</h3>
<p>먼저 코드부터 ~</p>
<pre><code class="language-swift">...
//RxSwift : Observavle, onNext
//1. 비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법
func downloadJson(_ url : String)-&gt; Observable&lt;String?&gt; {
        return Observable.create() { f in
            DispatchQueue.global().async {
                let url = URL(string: MEMBER_LIST_URL)!
                let data = try! Data(contentsOf: url)
                let json = String(data: data, encoding: .utf8)

                DispatchQueue.main.async {
                    f.onNext(json)
                }
            }
            return Disposables.create()
        }
    }

//RxSwift : .subscribe, event
@IBAction func onLoad() {
        editView.text = &quot;&quot;
        self.setVisibleWithAnimation(self.activityIndicator, true)

    //2. Observable로 오는 데이터를 받아서 처리하는 방법
        downloadJson(MEMBER_LIST_URL)
            .subscribe { event in
                switch event{
                case let .next(json):
                    self.editView.text = json
                    self.setVisibleWithAnimation(self.activityIndicator, false)
                    break
                case .completed:
                    break
                case .error(_):
                    break
                }
        }
    }
//데이터가 전달 될때는 .next
//데이터가 다 전달 됬을 때는 .completed</code></pre>
<p>위의 한글로 설정해둔 클래스명으로 정리를 해보자면 이렇다.</p>
<blockquote>
<p><code>Observable</code> : 나중에 생기는 데이터 class
 <code>Observable.create()</code> : 나중에 생기는 데이터 만들때
 <code>Subscribe</code> : 나중에 오면
 <code>onNext</code> : f에 바로 전달하는게 아닌 onNext로 전달
 <code>event</code> : subscribe 하면 나중에 event 가 온다.
 <code>Disposable</code> 로 작업 취소(create 했으면 Disposable 를 리턴 해야함) <code>disposable.dispose()</code>로       작업을 바로 취소할 수 있다. <code>onLoad()</code>에서 버튼을 누르면 다운을 받아라!했는데 바로 취소하는 셈이다.</p>
</blockquote>
<p>그리고 Observable의 순서(생명주기)는 이렇다.</p>
<blockquote>
<ol>
<li>Create ( Create를 만들어놔도 Subscribe가 없으면 실행되지 않는다 )</li>
<li>Subscribe</li>
<li>onNext / onError ( -&gt; 여기서 에러가 나도 종료 )</li>
<li>onCompleted ( -&gt; 종료 )</li>
<li>Disposed ( -&gt; 취소로 인한 종료 )</li>
</ol>
</blockquote>
<p>⚠️ 동작이 끝난 Observavle은 재사용이 불가능하다.</p>
<p> 위에서 주석처리 한 것처럼 RxSwift의 사용법은 두가지 이다.</p>
<ol>
<li><p>비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법</p>
</li>
<li><p>Observable로 오는 데이터를 받아서 처리하는 방법</p>
<p>첫번째로, 비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법은 이렇다.</p>
</li>
<li><p><code>Observable.create()</code> 한다.</p>
<ol start="2">
<li><p><code>onNext</code>로 데이터를 받는다.</p>
</li>
<li><p>데이터 다 받으면 <code>onCompleted()</code></p>
</li>
<li><p>마지막에 <code>Disposables.create()</code></p>
<pre><code class="language-swift">func downloadJson(_ url : String)-&gt; Observable&lt;String?&gt; {

 return Observable.create(){ emitter in //emitter : 데이터
     emitter.onNext(&quot;Hello&quot;) //데이터를 받는다. 여러개를 받을 수 있음!!
     emitter.onNext(&quot;World&quot;)
     emitter.onCompleted() //다받으면 onCompleted()

     return Disposables.create() //그냥 Disponsable말고 Disposables.create()
 }
}
</code></pre>
</li>
</ol>
</li>
</ol>
<p>//실행하면 Hello, World가 각각 아래의 event로 들어간다.</p>
<pre><code>
두번째로, Observable로 오는 데이터를 받아서 처리하는 방법은 이렇다.
1. `observable` 을 만든다.
2. `subscribe`로 이벤트를 받는다.
3. 이벤트의 next, error, complete 을 처리한다
4. 필요에 따라 취소시킨다. ( → `disposable.dispose()` )

```swift
let observable = downloadJson(MEMBER_LIST_URL)
        let disposable = observable.subscribe{ event in
            switch event{
            case .next(let json):
                self.editView.text = json //1차로 Hello, 2차로 World가 들어온다.
                                self.setVisibleWithAnimation(self.activityIndicator, false)
            case .error(let err):
                break
            case .completed:
                break
            }

        }
        disposable.dispose() // 로 필요에 따라 취소시킬 수 있다.

//최종적으로는 World만 찍힌다. 덮어씌워진것이므로 마지막에 들어온것만 뜸!!</code></pre><p>위 두개는 이해를 쉽게 하기 위한 예제이다.
본격적으로 만들게 되면 이렇게 나온다.</p>
<pre><code class="language-swift">func downloadJson(_ url : String)-&gt; Observable&lt;String?&gt; {

        //1. 비동기로 생기는 데이터를 Observable 로 감싸서 리턴하는 방법
        return Observable.create(){ emitter in
            let url = URL(string: url)!
            let task = URLSession.shared.dataTask(with: url){ (data, _ ,error) in

                //error는 nil 이여야 하닌데 아닐땐 onError
                guard error == nil else{
                    emitter.onError(error!)
                    return
                }

                //데이터가 제대로 왔다면 onNext로 Json 접근
                if let data = data , let json = String(data: data, encoding: .utf8){
                    emitter.onNext(json)
                }

                //다 받았을 경우 끝 ( Observavle의 끝 )
                emitter.onCompleted()

            }
            task.resume()

            //중간에 cancel 하면 task를 Cancel 한다.
            return Disposables.create() {
                task.cancel()
            }
        }
    }

let observable = downloadJson(MEMBER_LIST_URL)
        let disposable = observable.subscribe{ event in
            switch event{
            case .next(let json):
                DispatchQueue.main.sync {
            self.editView.text = json 
                                          self.setVisibleWithAnimation(self.activityIndicator, false)
                                          }
            case .error(let err):
                break
            case .completed:
                break
            }

        }
        disposable.dispose()</code></pre>
<p>예제로 본 것처럼 event에는 next, error, completed가 있다.
( 여기서 switch-case문이 순환참조상태이다. 순환참조의 설명은 생략. )</p>
<h3 id="operator">Operator!</h3>
<p>하지만 위의 예제는 길다. 해당 코드를 더 짧게 쓸 수 있는? 기본 사용법에 귀찮은 것을 없애주는 친구가 있는데, 그게바로 Opreator이다.
처음에 설명했듯이, ReactiveX 공식문서에 Opreator의 모든 종류들에 대한 설명이 전부 세세하게 적혀있다. 🙂
<a href="https://reactivex.io/documentation/operators.html">링크텍스트</a></p>
<p>⚠️ Operator는 순서가 굉장히 중요하다. 순서에따라 출력 결과가 달라지기 때문이다. ⚠️
쉬운 이해를 위해 아까 작성했던 우리의 영원한 친구 Hello world에서 이렇게 줄일 수 있다.</p>
<pre><code class="language-swift">func downloadJson(_ url : String)-&gt; Observable&lt;String?&gt; {

     return Observavle.just(&quot;Hello World&quot;)
}</code></pre>
<p>just는 데이터를 하나밖에 보내지 못한다. 만약 두개를 보내고 싶으면 배열을 이용한다.</p>
<pre><code class="language-swift">func downloadJson(_ url : String)-&gt; Observable&lt;[String?]&gt; {

     return Observavle.just([&quot;Hello&quot;, &quot;World&quot;])
}</code></pre>
<p>만약 하나씩 하나씩 보내고 싶다면 from을 사용한다.</p>
<pre><code class="language-swift">func downloadJson(_ url : String)-&gt; Observable&lt;String?&gt; {

         //Hello 한 번 내려가고 World 한 번 내려가고 ( 하나씩 내려감 )
     return Observavle.from([&quot;Hello, World&quot;]) 
}</code></pre>
<h3 id="코드-줄이기">코드 줄이기</h3>
<p>아까 subscribe로 다시 돌아가보면, switch-case문에 있는 error나 completed는 해당 코드에서 사용할 일이 없는 것을 알 수 있다.</p>
<pre><code class="language-swift">let observable = downloadJson(MEMBER_LIST_URL)
        _ = observable.subscribe { event in
            switch event{
            case .next(let json):
                DispatchQueue.main.sync {
            self.editView.text = json 
                                          self.setVisibleWithAnimation(self.activityIndicator, false)
                                }
            case .error(let err):
                break
            case .completed:
                break
            }

        }
        disposable.dispose()</code></pre>
<p>안쓰는데 왜 적어야해?
그래서! onNext인 경우, 이렇게 한 줄로 작성이 가능하다!</p>
<pre><code class="language-swift">_ = downloadJson(MEMBER_LIST_URL) {
    .subscribe(onNext : {print($0)})
} 

//만약 completed도 받고싶다면
_ = downloadJson(MEMBER_LIST_URL) {
    .subscribe(onNext : {print($0)}, onCompleted : {print(&quot;Com&quot;)})
} 

//error도 받고싶다면
_ = downloadJson(MEMBER_LIST_URL) {
    .subscribe(onNext : {print($0)}, 
            onCompleted : {print(&quot;Com&quot;)}, 
        onError : { error in print(error)})
} </code></pre>
<h3 id="disposable">Disposable</h3>
<p>앞서 설명했던 disposable.dispose()의 그 disposable이다.
disposable은 리소스를 해제할 때 사용한다. 자동차로 비유하자면 브레이크 느낌이랄까?</p>
<pre><code class="language-swift">//취소 ( observer 로직 끝 )
disposable.dispose()

//취소 ( viewdidAppear 내부 )
disposable.forEach{$0.dispose()}

//화면에 여러개의 취소시켜야 하는 작업이 있을 경우
 var disposable : [Disposable] = []
 let d = Observable.zip(jsonObservable,helloObservable){$1 + &quot;\n&quot; + $0!}
            .observeOn(MainScheduler.instance) // 메인쓰레드로 바뀌고
            .subscribe ( onNext: {json  in
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
        })
 disposable.append(d)</code></pre>
<p>화면에 여러개 취소도 sugar api가 있다. DisposeBag()이라고 담는가방이라고 생각하면 된다.
DisposeBag를 사용하면..!</p>
<pre><code class="language-swift">...
var disposable = DisposeBag()

let d = Observable.zip(jsonObservable,helloObservable){$1 + &quot;\n&quot; + $0!}
            .observeOn(MainScheduler.instance) // 메인쓰레드로 바뀌고
            .subscribe ( onNext: {json  in
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
        })

disposable.insert(d)

//만약 마지막 코드도 귀찮다! 싶으면 이렇게도 가능하다.
...
var disposeBag = DisposeBag()

Observable.zip(jsonObservable,helloObservable){$1 + &quot;\n&quot; + $0!}
            .observeOn(MainScheduler.instance) // 메인쓰레드로 바뀌고
            .subscribe ( onNext: {json  in
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
        })
        .disposed(by: disposeBag)</code></pre>
<p> 노션에 정리해두었던 개념은 이렇다.
 일단 옮겨놓기만 했고, 코드도..좀 줄간격이 이상한데.. 일단 올리고 내일해야겠다.
 정교한 정리와 퀄리티업은 자료를 더 찾으면서 하기로.. 🥲</p>
]]></description>
        </item>
    </channel>
</rss>