<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sewoong.log</title>
        <link>https://velog.io/</link>
        <description>iOS Developer</description>
        <lastBuildDate>Fri, 21 Apr 2023 11:21:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sewoong.log</title>
            <url>https://velog.velcdn.com/images/grumpy-sw/profile/e7a50529-1dfd-4073-9416-cb3373497d2c/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sewoong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/grumpy-sw" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[HIG] Components - Boxes]]></title>
            <link>https://velog.io/@grumpy-sw/HIG-Components-Boxes-0b2qiy23</link>
            <guid>https://velog.io/@grumpy-sw/HIG-Components-Boxes-0b2qiy23</guid>
            <pubDate>Fri, 21 Apr 2023 11:21:45 GMT</pubDate>
            <description><![CDATA[<pre><code>Apple Developer의 Human Interface Guidelines를 읽고 정리한 글입니다.
전문을 번역한 것이 아닌 혼자 읽고 정리한 글이기에 잘못 이해하거나 오역된 정보가 있을 수 있습니다.</code></pre><p>👉 <a href="https://developer.apple.com/design/human-interface-guidelines/components/selection-and-input/steppers/">원문 보러가기</a></p>
<h2 id="stepper">Stepper</h2>
<p>스테퍼는 값을 증감시키는 두 세그먼트 컨트롤로 이루어진 UI 요소이다.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7336c34f-ed5b-4208-80ee-73c778d9600b/Untitled.png" alt="Untitled"></p>
<p>스테퍼 자체는 값을 표시하지 않기 때문에 현재 값을 표시하는 필드 옆에 배치한다.</p>
<h2 id="best-practices">Best practices</h2>
<p><strong>스테퍼가 영향을 미치는 값을 명확하게 하라.</strong></p>
<p>스테퍼 자체로는 값을 표시할 수 없으므로 사용자가 스테퍼가 어떤 값을 변경시키는지 명확히 알 수 있어야 한다.</p>
<p><strong>값이 크게 변경된다면 스테퍼를 텍스트 필드와 연결하라.</strong></p>
<p>스테퍼는 몇 번의 탭이나 클릭을 통해 작은 변경을 수행하는 경우에 적합하다. 사용하는 값의 변경 폭이 크다면 특정 값을 직접 입력하는 옵션이 더욱 유용할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC18] - Using Collections Effectively]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC18-Using-Collections-Effectively</link>
            <guid>https://velog.io/@grumpy-sw/WWDC18-Using-Collections-Effectively</guid>
            <pubDate>Mon, 03 Apr 2023 11:27:10 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2018 &#39;Using Collections Effectively&#39;를 시청하고 정리한 글입니다.</code></pre><h1 id="using-collections-effectively">Using Collections Effectively</h1>
<p><img src="https://i.imgur.com/hn30mtj.png" alt=""></p>
<blockquote>
<p>최적의 성능을 위한 컬렉션의 사용법</p>
</blockquote>
<h2 id="collections">Collections</h2>
<p><strong>Without Arrays</strong>
<img src="https://i.imgur.com/iBzjIRO.png" alt=""></p>
<p><strong>Without Dictionaries</strong>
<img src="https://i.imgur.com/NYa7vES.png" alt=""></p>
<p><strong>With Collections</strong>
<img src="https://i.imgur.com/5nxGEAx.png" alt=""></p>
<h3 id="collection-protocol">Collection Protocol</h3>
<p><img src="https://i.imgur.com/6icnH49.png" alt=""></p>
<p>Collection은 startIndex와 endIndex, 그리고 subscript를 내부적으로 저장하고 있다.</p>
<p>Collection 프로토콜이 어떻게 구현되어 있는지 살펴보자. </p>
<p><img src="https://i.imgur.com/skvthGr.png" alt=""></p>
<ul>
<li>Collection의 정의는 <code>Element</code>의 연속된 집합</li>
<li>Comparable을 채택한 <code>Index</code></li>
<li>해당 인덱스에서 요소를 검색하거나 사용하기 위해 <code>subscript</code>를 제공</li>
<li>Collection의 경계를 나타내기 위해 <code>startIndex</code>, <code>endIndex</code></li>
<li>다음 인덱스로 이동할 수 있는 <code>func index(after i: Index) -&gt; Index</code></li>
</ul>
<p><img src="https://i.imgur.com/dSY0peV.png" alt=""></p>
<p>그리고 Collection의 여러 기능들이 이들을 응용하여 만든 것이다.</p>
<p><img src="https://i.imgur.com/dwUHtXL.png" alt=""></p>
<p>예를 들어 그림과 같은 한 칸씩 건너뛰는 기능을 확장을 통해 구현한다고 해보자.</p>
<p><img src="https://i.imgur.com/lELLEuH.png" alt=""></p>
<p>직접 배열 요소들을 모두 순회하면서 일부 값을 건너뛰는 <code>everyOther(_ body: (Element) -&gt; Void)</code> 메서드를 주어진 프로퍼티와 메서드를 응용하여 구현할 수 있다.</p>
<p><img src="https://i.imgur.com/rw4nzVi.png" alt=""></p>
<p>Collection 프로토콜은 단일 프로토콜이 아니다. 그림처럼 계층 구조로 이루어져 있으며 각 프로토콜은 Collection의 기능들을 향상시킨다.</p>
<p><img src="https://i.imgur.com/G4MFNMW.png" alt=""></p>
<p>예를 들면, BidirectionalCollection 프로토콜은 Collection에서 이전 인덱스로 이동할 수 있는 <code>func index(before: Self.Index) -&gt; Self.Index</code>를 제공한다.</p>
<p><img src="https://i.imgur.com/yohA0No.png" alt=""></p>
<p>RandomAccessCollection은 인덱스에서 다른 인덱스를 계산하거나 두 인덱스 간의 거리를 계산하는 <code>func distance(from start: Index, to end: Index) -&gt; Int</code>를 제공한다.</p>
<p><img src="https://i.imgur.com/X7rCQKd.png" alt=""></p>
<p>흔히 Collection이라고 일컫는 Array, Set, Dictionary 뿐만 아니라 다른 많은 타입들도 Collection 프로토콜을 채택하여 Collection을 사용하고 있다. 
이는 프로토콜의 범용성 덕분</p>
<p>프로토콜을 준수하는 타입이 Collection의 기능을 어떻게 사용하는지 먼저 타입의 Index를 계산하는 방법부터 확인해보자.</p>
<h3 id="-collection-index"># Collection Index</h3>
<p>Indices - 타입이 인덱싱되는 방법</p>
<p><img src="https://i.imgur.com/pc6tg4I.png" alt=""></p>
<ul>
<li>각 Collection에는 고유한 Index가 존재한다.</li>
<li>그리고 Index는 Comparable해야 한다.</li>
</ul>
<p>Collection에서 첫 번째 요소를 구하는 방법</p>
<p><img src="https://i.imgur.com/dz35YZA.png" alt=""></p>
<ul>
<li>Array는 <code>array[0]</code>처럼 정수 형태의 Index로 접근할 수 있지만 Set은 순서가 없기 때문에 사용할 수 없다.</li>
<li><code>startIndex</code>를 사용하여 <code>array[array.startIndex]</code>, <code>set[set.startIndex]</code>를 사용할 수 있지만 Collection이 비어있는 경우도 고려해야 한다.</li>
<li>따라서 Optional하게 값을 반환하는 <code>first</code> 프로퍼티를 사용하는 것이 안전하다.</li>
</ul>
<p>Collectioin에서 두 번째 요소를 구하는 방법</p>
<p>위와 같이 두 번째 요소를 반환하는 프로퍼티를 확장을 통해 구현한다고 가정해보자.</p>
<ul>
<li>Collection이 Array, Set, Dictionary 심지어 아직 존재하지 않는 Collection 타입일 가능성이 있음.</li>
<li>모든 Collection에 두 개 이상의 요소가 있는 것이 아니므로 <code>[1]</code> 혹은 <code>[startIndex + 1]</code>과 같이 사용할 수 없음</li>
</ul>
<p><img src="https://i.imgur.com/08yTjN5.png" alt=""></p>
<p>따라서 다음의 단계를 거쳐야 한다.</p>
<ol>
<li>Collection이 비어있는가?</li>
<li>SecondIndex에 해당하는 Index를 구함</li>
<li>SecondIndex가 유효한가?</li>
<li>해당 Index위치의 Element 반환</li>
</ol>
<p>모든 종류의 Collection에 대응하기 위해 이런 복잡한 과정을 거쳐야만 했고 이를 개선하기 위해 <strong>Slice</strong> 개념이 도입되었다.</p>
<h3 id="-slice"># Slice</h3>
<p><img src="https://i.imgur.com/HLApoOY.png" alt=""></p>
<p>Slice는 Collection의 일부만을 묘사하는 타입이다.
Slice의 특징</p>
<ul>
<li>항상 시작과 끝이 존재한다.</li>
<li>원본 Collection과 별도로 존재한다.</li>
<li>추가적으로 저장소를 사용하지 않고 원본을 참조한다.</li>
</ul>
<p>그렇다면 Slice를 사용하여 secondIndex를 확인해보자.</p>
<p><img src="https://i.imgur.com/15WuVU6.png" alt=""></p>
<ol>
<li>원본 Array로 시작하여 첫 번째 요소를 제거한 더 짧은 Slice 생성</li>
<li>원본 Array의 두 번째 Index를 가리키는 secondIndex 생성</li>
<li>secondIndex와 Slice의 firstIndex가 같은지 확인</li>
</ol>
<p><img src="https://i.imgur.com/VmfUsFg.png" alt=""></p>
<p>따라서 Slice를 통해 Collection의 두 번째 요소를 구하는 코드를 이렇게 바꿀 수 있다.</p>
<p><img src="https://i.imgur.com/79ya4Rq.png" alt=""></p>
<p>Array 뿐만 아니라 Collection을 채택한 다른 여러 타입들에서 Slice를 사용한다. 다만 Set의 경우 순서가 없기 때문에 표준 라이브러리의 Slice를 사용하고 나머지 타입의 경우 자체 Slice 타입으로 다양한 옵션을 갖고 있다.</p>
<h3 id="lazy-functions">Lazy Functions</h3>
<p><img src="https://i.imgur.com/KgxKSdE.png" alt=""></p>
<p>Slice가 존재하는 한 Array는 참조하고 있는 객체가 있으므로 버퍼에서 해제되지 않는다. 이것을 제거하고 싶다면, Slice로부터 Array의 복사본을 만든 다음 Slice를 초기화해주어야 한다. 이 작업은 lazy copy와 유사하게 동작한다.</p>
<p>이렇게 해서 얻을 수 있는 이점은? - 성능
Swift의 함수 호출은 기본적으로 eager. 즉 input이 들어오면 응당 output이 존재한다는 것</p>
<p><img src="https://i.imgur.com/ZMwWs1o.png" alt=""></p>
<p>예시를 보면, 4000개의 요소를 모두 계산을 한 후 4000개에 모두 필터링을 해야 한다. 그러나 이것은 불필요할지도 모르는 중간 계산이다. 실제로 우리가 필요한 자리는 딱 4개.</p>
<p><img src="https://i.imgur.com/JxTtv23.png" alt=""></p>
<p>이렇게 원본 Collection을 <code>lazy</code>한 Collection으로 래핑하고 작업을 수행하면 실제로 매핑하지 않고 나중에 사용할 필요가 있을 때 저장한다.</p>
<p><img src="https://i.imgur.com/NlkCkXo.png" alt=""></p>
<p>결국 items의 first를 호출하면 함수들이 대상 Collection에 대해 알지 못하는 상태로 동작한다.</p>
<p><img src="https://i.imgur.com/mAOwQ6T.png" alt=""></p>
<p>이에 대해 조금 더 자세한 예시를 살펴보자.
&quot;Bear&quot;를 포함하는 Array를 얻기 위한 코드이다. </p>
<p>lazy 하게 동작하기 때문에 다음 두 번의 호출은 필터링 로직을 각각 타게 될 것이다. </p>
<p><img src="https://i.imgur.com/whUEmkf.png" alt=""></p>
<p>이 문제를 해결하는 방법은 간단하다. 연산 결과를 Collection으로 복사한 뒤 사용하는 방법이다.</p>
<p><img src="https://i.imgur.com/NapRb6P.png" alt=""></p>
<p>따라서 언제 Lazy Collection을 사용해야 할까?</p>
<ul>
<li>Chain Compute 상황에서 오버헤드를 줄일 수 있다.</li>
<li>Collection 계산 결과의 일부만 필요한 경우</li>
<li>Side Effect가 없는 경우</li>
<li>API의 경계를 넘는 경우 일반 Collection으로 구체화 필요</li>
</ul>
<h3 id="mutable-collection--rangereplaceablecollection">Mutable Collection &amp; RangeReplaceableCollection</h3>
<p><img src="https://i.imgur.com/XXvrPW1.png" alt=""></p>
<p>다시 Collection 프로토콜로 돌아와서, MutableCollection은 Collection의 내용을 변경할 수 있도록 setter가 제공된다.</p>
<p><img src="https://i.imgur.com/DxDkoFM.png" alt=""></p>
<p>RangeReplaceableCollection은 요소를 제거하거나 삽입할 때 사용하는 기능을 제공하는 프로토콜이다.</p>
<h2 id="crash-about-collection">Crash About Collection</h2>
<p><img src="https://i.imgur.com/gFc9qpc.png" alt=""></p>
<p>근본적인 원인은 대부분 이 두 가지</p>
<ul>
<li>Collection이 변경되거나</li>
<li>다수의 Thread가 Collection에 접근하거나</li>
</ul>
<h3 id="avoid-index-invalidation">Avoid Index Invalidation</h3>
<p><img src="https://i.imgur.com/Don3907.png" alt=""></p>
<p><img src="https://i.imgur.com/uAjcZHq.png" alt=""></p>
<p>해당 Index가 더 이상 유효하지 않다. Collection을 변경하는 순간 index가 무효화되었기 때문이다.</p>
<p><img src="https://i.imgur.com/ZWCEV4u.png" alt=""></p>
<p>그렇기 때문에</p>
<ul>
<li>Collection 변경 후 Index를 계산해야 한다.</li>
<li>Slice는 변경된 후에도 기존 상태를 유지하므로 주의해야 한다.</li>
<li>Index 계산에는 적지 않은 시간이 소요될 수 있으므로 필요한 경우에만 계산하는 것이 좋다.</li>
</ul>
<h3 id="multithreaded-mutable-collection">Multithreaded Mutable Collection</h3>
<p><img src="https://i.imgur.com/0A6EaTF.png" alt=""></p>
<p>Multi-Thread 환경에서 Collectiion에 접근했을 때 발생할 수 있는 결과는 다양하다. 동일한 코드로 동일한 결과를 얻지 못한다는 것은 즉 프로그램의 실행 결과를 예측할 수 없다는 뜻.
Xcode의 Thread Sanitizer를 사용해서 진단했을 때 Access race가 발생했음을 알 수 있다.</p>
<p><img src="https://i.imgur.com/YHbGEl6.png" alt=""></p>
<p>DispatchQueue를 Serial한 Queue로 바꾸어 해결한 모습</p>
<p><img src="https://i.imgur.com/Hdk21Bg.png" alt=""></p>
<ul>
<li>Collection을 Single Thread에서만 볼 수 있도록 격리하는 것이 좋다.</li>
<li>그렇지 못한 경우 상호 배제 기능을 제공할 적절한 방법을 사용해야 한다.(<code>Serial Dispatch Queue</code> 또는 <code>Locks</code>)</li>
</ul>
<h3 id="prefer-immutable-collections">Prefer Immutable Collections</h3>
<p><img src="https://i.imgur.com/u2lf8jA.png" alt=""></p>
<p>변경 불가능한 Collection을 사용한다면 이런 위험의 가능성을 모두 피할 수 있다.</p>
<h3 id="forming-new-collections">Forming New collections</h3>
<p><img src="https://i.imgur.com/W2S8RrH.png" alt=""></p>
<p>혹은 새로운 Collection을 구성할 때 필요한 만큼의 크기를 정확하게 구할 수 있다면 성능상의 이점을 활용할 수 있다.</p>
<h2 id="foundation-collections">Foundation Collections</h2>
<p><img src="https://i.imgur.com/LsIPW8Q.png" alt=""></p>
<p>Foundation에서 제공하는 이런 <strong>NS</strong> 접두사가 붙은 Collection들은 모두 참조 타입이다.</p>
<p><img src="https://i.imgur.com/fCURPKF.png" alt=""></p>
<p>값 타입과 참조 타입의 Collection에 변경이 발생했을 때의 동작은 차이가 있다. 
값 타입을 보면 y는 x를 참조하고 있다가 y에 변경이 일어나면 동일한 배열을 복사한 후 append를 수행한다.
반면에 참조 타입은 x와 y가 동일한 위치를 가리키는 포인터가 되어 결국 y에 대한 append는 x에도 append를 한 것과 같다.</p>
<p><img src="https://i.imgur.com/11i5kIs.png" alt=""></p>
<p>Swift의 모든 Objective-C API는 값 타입으로 동작한다.
NS 접두사가 붙은 타입은 참조 타입이므로 두 Collection에 대한 구현이 다르게 되어있을텐데, 어떻게 둘을 연결한 것일까?</p>
<p>이에 대한 해답은 <strong>Bridging</strong>이라는 기술이다.</p>
<h3 id="bridging">Bridging</h3>
<p><img src="https://i.imgur.com/s0Jt7UI.png" alt=""></p>
<p>Bridging은 서로 다른 런타임 표현 사이를 변환하는 방법이다. 즉 Swift와 Objective-C 서로가 호환되도록 변환하는 것을 의미한다. 이 과정을 거칠 때마다 비용이 발생한다.</p>
<p><img src="https://i.imgur.com/MvPfEal.png" alt=""></p>
<p>기본적으로 내부 요소 타입이 Bridge될 경우에는 Eager하게, 그렇지 않으면 Lazy하게 동작한다.</p>
<p><img src="https://i.imgur.com/lYea0ZF.png" alt=""></p>
<p>예시 코드를 보자. 분석 결과 두 번의 Bridge가 발생한 것을 확인했다.</p>
<p><img src="https://i.imgur.com/8Rh824W.png" alt=""></p>
<p>코드 수정을 통해 Bridge를 하나로 줄였다. 이 경우는 작은 Bridge이므로 비용이 저렴하지만 루프를 통해 비용이 합산될 위험이 있다. 따라서 작은 문자열을 반복적으로 연결하지 않도록 주의해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HIG] Components - Boxes]]></title>
            <link>https://velog.io/@grumpy-sw/HIG-Components-Boxes</link>
            <guid>https://velog.io/@grumpy-sw/HIG-Components-Boxes</guid>
            <pubDate>Mon, 03 Apr 2023 11:21:27 GMT</pubDate>
            <description><![CDATA[<pre><code>Apple Developer의 Human Interface Guidelines를 읽고 정리한 글입니다.
전문을 번역한 것이 아닌 혼자 읽고 정리한 글이기에 잘못 이해하거나 오역된 정보가 있을 수 있습니다.</code></pre><p>👉 <a href="https://developer.apple.com/design/human-interface-guidelines/components/layout-and-organization/boxes/">원문 보러가기</a></p>
<h2 id="boxes">Boxes</h2>
<p><a href="https://developer.apple.com/design/human-interface-guidelines/images/intro/components/box-intro-dark_2x.png!%5B%5D(https://velog.velcdn.com/images/grumpy-sw/post/c973a7e2-29a3-4cab-8b6b-2b8b955203da/image.png)">https://developer.apple.com/design/human-interface-guidelines/images/intro/components/box-intro-dark_2x.png![](https://velog.velcdn.com/images/grumpy-sw/post/c973a7e2-29a3-4cab-8b6b-2b8b955203da/image.png)</a></p>
<p>box는 논리적으로 관련이 있는 정보와 구성 요소들을 포함하여 다른 요소들과 시각적으로 구별되는 그룹으로 만드는 방법이다.
기본적으로 box는 <strong>테두리 혹은 배경색</strong>의 구분을 통해 다른 인터페이스 요소들과 구분한다.</p>
<h2 id="best-practices">Best practices</h2>
<p><strong>box를 상대적으로 작게 유지한다.</strong></p>
<ul>
<li>만약 box의 크기가 화면 전체 크기에 가까워지면 컨텐츠의 분리를 전달하는 데 효과적이지 못하다.</li>
</ul>
<p><strong>box 내에서 추가적인 그룹화를 할 때는 padding과 alignment를 사용하여 나타낸다.</strong></p>
<ul>
<li>테두리는 뚜렷한 시각적 요소를 나타내므로, box 내부에 box를 중첩하여 추가하는 것은 인터페이스가 복잡하고 제한적인 느낌을 준다.</li>
</ul>
<h2 id="content">Content</h2>
<p><strong>사용자가 box의 내용과 목적을 명확히 이해할 수 있도록 간결한 제목을 제공하는 것이 좋다.</strong></p>
<ul>
<li>제목은 box가 어떤 목적 혹은 논리적인 연관성을 갖고 다른 요소들과 구분되었는지 이해를 돕는다.</li>
<li>또한 사용자가 VoiceOver 기능을 사용하는 경우 box 내의 컨텐츠가 어떤 컨텐츠인지 쉽게 예측하도록 한다.</li>
</ul>
<p><strong>box의 제목은 내용을 설명하는 간단한 문구로 작성한다.</strong></p>
<ul>
<li>문장 스타일(첫 글자는 대문자, 나머지는 소문자)를 사용한다.</li>
</ul>
<h2 id="in-ios">in iOS</h2>
<p><strong>기본적으로 iOS에서는 box를 나타낼 때 secondary 및 tertiary backgroundColor를 사용한다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HIG] Platforms - Designing for iOS]]></title>
            <link>https://velog.io/@grumpy-sw/HIG-Platforms-Designing-for-iOS</link>
            <guid>https://velog.io/@grumpy-sw/HIG-Platforms-Designing-for-iOS</guid>
            <pubDate>Mon, 03 Apr 2023 11:11:22 GMT</pubDate>
            <description><![CDATA[<pre><code>Apple Developer의 Human Interface Guidelines를 읽고 정리한 글입니다.
전문을 번역한 것이 아닌 혼자 읽고 정리한 글이기에 잘못 이해하거나 오역된 정보가 있을 수 있습니다.</code></pre><p>👉 <a href="https://developer.apple.com/design/human-interface-guidelines/platforms/designing-for-ios">원문 보러가기</a></p>
<h2 id="designing-for-ios">Designing for iOS</h2>
<p><img src="https://velog.velcdn.com/images/grumpy-sw/post/4f4a4aa7-b4ab-4e48-8bb3-2f65c06bd4b0/image.png" alt="https://developer.apple.com/design/human-interface-guidelines/platforms/designing-for-ios"></p>
<p>Human Interface Guidelines(이하 HIG)에서는 앱을 디자인하기 전에 먼저 기본적인 기기에 대한 특성을 이해하는 것을 권장한다. 기기의 특성에 대한 높은 이해도가 있다면 사용자가 선호하는 방향의 앱을 구현하고 제공하는 데 도움이 되기 때문이다.</p>
<p>문서에서 설명하는 iOS 기기의 특징은 다음과 같다.</p>
<ul>
<li>Display
  iPhone은 중간(medium) 크기의 고해상도 디스플레이를 사용한다.</li>
<li>Ergonomics
  iPhone을 사용할 때 사람들은 필요에 따라 가로/세로 방향을 전환하면서 사용하며 이 때 각각 한 손 또는 양 손으로 잡는 경향이 있다. 장치와 상호작용하는 동안 사용자와 기기 사이의 시청 거리는 1~2피트를 넘지 않는다.</li>
<li>Inputs
  멀티 터치 제스처, 온스크린 키보드, 음성 등을 통해 사용자는 입력을 수행한다. 필요한 경우 위치, 가속도계, 자이로스코프 등을 추가적으로 사용한다.</li>
<li>App interactions
  iOS 기기를 사용하는 사용자는 앱을 사용하다가 다른 앱에서의 이벤트 혹은 SNS를 확인하거나 메시지를 전송하는 등 일반적으로 동시에 여러 개의 앱을 열어두고 빈번하게 전환을 하며 사용한다.</li>
<li>System features
  iOS는 사용자에게 익숙하고 일관된 방식으로 시스템 및 앱과 상호작용하는 기능들을 제공한다.</li>
</ul>
<h2 id="best-practices">Best practices</h2>
<p>사용자에게 앱이 iOS 환경에서 편안하고 익숙하게 느껴져야 한다. 사용자가 기대하는 대로 동작해야 하고 새로운 기능이라 할지라도 iOS를 사용하는 사용자라면 해당 기능에 대해 쉽게 예측할 수 있도록 익숙한 디자인을 사용하는 것이 중요하다.
이를 위해 다음 몇 가지 규칙을 준수해야 한다.</p>
<ul>
<li>사용자가 최소한의 상호 작용으로 세부 정보 및 작업을 검색할 수 있도록 화면 컨트롤의 수를 제한하여 주요 컨텐츠에 집중할 수 있게 해야 한다.</li>
<li>장치 방향, 다크 모드 혹은 손쉬운 사용 등에 원활하게 대응하여 자신에게 적합한 것을 선택하여 자신만의 방법대로 앱을 사용할 수 있어야 한다.</li>
<li>사용자가 iPhone을 잡는 방식대로 상호작용을 활성화해야 한다. 예를 들어 세로 모드를 사용하는 중에는 iPhone을 한 손으로 잡는 경향이 있으므로 제어 관련 컴포넌트를 하단에 배치하여 상호 작용을 한 손으로 편리하게 제공해야 한다.</li>
<li>플랫폼 기능(Face ID, 지문 인식 등)을 통해 사용 가능한 정보가 있는 경우 직접 데이터 입력을 요구하지 않고 허락을 받은 후에 플랫폼 기능을 사용하는 것이 좋다. 예를 들어 결제를 수락하거나 인증을 진행할 때 본인인증이 필요한 경우 별도로 입력을 받는 대신 iOS에서 제공하는 Face ID를 대신 활용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC20] - Eleminate animation hitches with XCTest]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC20-Eleminate-animation-hitches-with-XCTest</link>
            <guid>https://velog.io/@grumpy-sw/WWDC20-Eleminate-animation-hitches-with-XCTest</guid>
            <pubDate>Sat, 04 Feb 2023 05:48:05 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2020 &#39;Eleminate animation hitches with XCTest&#39;를 시청하고 정리한 글입니다.</code></pre><h1 id="eleminate-animation-hitches-with-xctest">Eleminate animation hitches with XCTest</h1>
<p><img src="https://i.imgur.com/Ye1YkEJ.png" alt=""></p>
<p>XCTest를 사용하여 hitch를 감지하고 regression을 포착하는 방법에 대해 알아보자!</p>
<p><img src="https://i.imgur.com/1eFA8uN.png" alt=""></p>
<p>애니메이션은 User Experience의 중요한 부분이다.
특정 동작을 하는 상황에서 애니메이션이 흐트러지거나 부드럽지 않게 응답하는 경우, 사용자의 눈에 띄고 결국 부정적인 사용자 경험의 원인이 된다.</p>
<h3 id="hitch">Hitch</h3>
<p>이러한 사용자가 인지할 수 있을 만큼의 부정적인 반응을 <strong>hitch</strong>라고 한다. hitch는 사용자의 주의를 분산시키고 품질에 악영향을 준다.
hitch는 프레임이 예상보다 화면에 늦게 나타난 시간을 의미한다.</p>
<p><img src="https://i.imgur.com/Fsh8glw.png" alt=""></p>
<p>hitch가 발생하는 원인은 주어진 시간 안에 프레임이 제대로 그려지지 않았기 때문이다.
앱의 프레임 별로 진행 상황을 보자. 그 전까지는 점진적인 스크롤이 발생하고 프레임 3에서 프레임 4로 넘어갈 때 갑자기 크게 점프한다.</p>
<p>앱의 프레임에 대해서 알아보자.</p>
<p><img src="https://i.imgur.com/q5Scf0h.png" alt=""></p>
<p>각 기기별로 프레임이 교체되는 성능이 다르다. iPhone 및 iPad는 60Hz의 주사율을 갖는데, 이는 즉 프레임 당 1/60인 16.67ms의 주기를 가진다는 뜻이다.
이 주기마다 화면이 디스플레이의 프레임을 교체할 것인지 결정하는 것이 VSYNC인데, 프레임이 이를 놓치게 되면 hitch가 발생한다.</p>
<p>위의 상황에서는 16.67ms만큼의 hitch가 발생하였다.</p>
<p><img src="https://i.imgur.com/ZdJIEj6.png" alt=""></p>
<p>hitch를 측정하는 방법은 두 가지가 있다.
Hitch time: 프레임이 늦게 표시된 시간(ms)
Hitch ratio: Hitch time per 1 second(ms/s)</p>
<p><img src="https://i.imgur.com/IrPSht9.png" alt=""></p>
<p>fps(Frame per secends)를 사용하지 않고 hitch time을 사용하는 이유</p>
<ul>
<li>기기별 성능이 절대적인 척도가 될 수는 없음.</li>
<li>애니메이션을 사용하지 않는 화면의 경우 업데이트하지 않는 쉬는 시간이므로 굳이 프레임을 업데이트할 필요가 없음.</li>
<li>성능 및 배터리를 위해 기능 별로 원하는 fps가 다를 수 있기 때문에 기기의 fps를 기준으로 적용하는 것은 무리가 있음.<ul>
<li>예: 게임의 경우 30fps, 비디오 24fps, 시계 앱 10fps 등</li>
</ul>
</li>
</ul>
<p><img src="https://i.imgur.com/dSQn4wd.png" alt=""></p>
<p>Hitch ratio의 기준
Hitch time의 궁극적인 목표는 당연히 0</p>
<ul>
<li>0 ~ 5 : Good</li>
<li>5 ~ 10: Warning</li>
<li>10 ~ : Critical</li>
</ul>
<h3 id="performance-xctest">Performance XCTest</h3>
<p><img src="https://i.imgur.com/2S5rE8v.png" alt=""></p>
<p>iOS 14에서 도구 모음을 사용하면 개발, 프로덕션 단계 모두에서 hitch를 추적할 수 있다. XCTest, MetricKit, Xcode Organizer를 사용하지만 이번 세션에서는 XCTest만 사용하여 개발 단계에서 hitch를 추적하고 이를 수정하는 내용만 다룰 것이다.</p>
<p><img src="https://i.imgur.com/tYLpPxB.png" alt=""></p>
<p>새로 도입된 XCTMetrics. 테스트에서 측정하려는 시스템적인 부분을 지정한다. 이 중에서 이번 세션은 애니메이션에 대한 성능 테스트를 수행하는 데 사용되는 <code>XCTOSSignpostMetric</code>에 중점을 둘 것이다.</p>
<p><img src="https://i.imgur.com/M5lwvrF.png" alt=""></p>
<p>XCTOSSignpostMetric으로 측정할 수 있는 것은 다음과 같다.</p>
<ul>
<li>Duration: os_signpost 사이의 간격</li>
<li>장애 관련 메트릭(count, duration, ratio of hitches)</li>
<li>Frame count, Frame rate</li>
</ul>
<p><img src="https://i.imgur.com/gYTm1vM.png" alt=""></p>
<p>이러한 metrics를 수집하려면 os_signpost 간격을 내보내야 한다.</p>
<p><img src="https://i.imgur.com/IuXVCmh.png" alt=""></p>
<p>Non-animation의 경우(duration만 반환) .begin, .end를 사용</p>
<p><img src="https://i.imgur.com/060WQJQ.png" alt=""></p>
<p>Animation Interval인 경우 .animationBegin을 사용한다.</p>
<p><img src="https://i.imgur.com/lCxV88D.png" alt=""></p>
<p>혹은 미리 정의된 animation interval을 사용한다.</p>
<p>간단한 Test에 이를 적용시켜보자.</p>
<p><img src="https://i.imgur.com/eDOL9R7.png" alt=""></p>
<p>Meal Plannner를 탭하고 foodCollection을 위로 스와이프하여 아래로 스크롤한다.
측정할 대상은 <code>scrollDecelerationMetric</code>이다.</p>
<p>measure 블록은 기본적으로 성능 측정을 위해 5번 실행된다. 따라서 이대로 테스트를 진행하게 되면 다른 콘텐츠를 스와이프하게 되므로 테스트가 의미 없어진다. 따라서 테스트를 다음과 같이 수정하여 실행 사이에 앱의 상태를 재설정해준다. </p>
<p><img src="https://i.imgur.com/GLQQUS1.png" alt=""></p>
<p>측정 상태를 수동으로 제어할 것임을 옵션으로 설정하고, measure 블록에서 stopMeasuring()을 호출한다. 그러고 나서 테스트 환경을 재설정한다.</p>
<h4 id="✨-tips-테스트를-하기-전에-불필요한-설정을-제거하고-빠르게-실행하여-성능-측정에-미치는-영향을-제거하는-것이-좋다">✨ Tips 테스트를 하기 전에, 불필요한 설정을 제거하고 빠르게 실행하여 성능 측정에 미치는 영향을 제거하는 것이 좋다.</h4>
<p><img src="https://i.imgur.com/fCerqf4.png" alt=""></p>
<ol>
<li>Performance XCTest에 대해 별도의 test scheme을 사용</li>
</ol>
<p><img src="https://i.imgur.com/ZClM2vk.png" alt=""></p>
<ol start="2">
<li>Build configuration -&gt; Release, Debugger 비활성화</li>
</ol>
<p><img src="https://i.imgur.com/kGnJouZ.png" alt=""></p>
<ol start="3">
<li>자동 스크린샷 수집과 코드 커버리지 해제</li>
</ol>
<p><img src="https://i.imgur.com/te7QrUd.png" alt=""></p>
<ol start="4">
<li>모든 진단 옵션 해제</li>
</ol>
<p>이제 Performance Test를 실행하고 나서 테스트 결과를 보는 방법이다. 다섯 번의 반복에 대한 </p>
<p><img src="https://i.imgur.com/K2sBrxe.png" alt=""></p>
<p><img src="https://i.imgur.com/Nz7KBaU.png" alt=""></p>
<p><img src="https://i.imgur.com/t4Wmwn9.png" alt=""></p>
<p>측정값을 수집한 것을 볼 수 있다. 5회 동안 Hitch ratio로 약 1.2ms/s를 얻었다. 이 값을 Baseline으로 설정하여 regression을 잡으면 된다.</p>
<p><img src="https://i.imgur.com/dXGc7Yc.jpg" alt=""></p>
<p>여기서 새로운 기능을 추가해보자. 먼저 UIImageView에 Image를 설정하고, 이미지의 크기를 조정하는 코드를 추가했다. 이 상태로 성능 테스트를 다시 실행하면</p>
<p><img src="https://i.imgur.com/O2inXgu.png" alt=""></p>
<p>hitch가 증가했다. 
scaleAspectFit() 호출에 문제가 있는 것 같다. 현재 UI 렌더링을 담당하는 메인 스레드에서 이미지를 다시 그리고 있는데, 새로운 픽셀을 생성하고 메모리를 할당하는 데 CPU를 사용하고 있다.</p>
<p><img src="https://i.imgur.com/aiQWS0d.png" alt=""></p>
<p> Core Animation의 setContentMode를 사용하여 이 이미지를 GPU로 다시 그리는 것으로 CPU의 사용을 줄일 수 있다. 이를 통해 이미 존재하는 이미지 픽셀을 사용할 수 있으므로 결국 메인 스레드에서 수행하는 작업의 양이 줄어들었다.</p>
<p><img src="https://i.imgur.com/qg33lnN.png" alt=""></p>
<p>수정 후 테스트 결과: 정상</p>
<h3 id="recap">Recap</h3>
<p>Hitch: 프레임이 예상보다 늦게 화면에 나타나서 발생하는 버벅거림
Performance XCTest를 통하여 개발 단계에서 hitch를 포착할 수 있다.</p>
<ul>
<li>이미 선언되었거나 커스텀된 <code>os_signpost</code>의 주기를 측정한다.</li>
<li>measure마다 상태 초기화를 시켜 동일한 환경을 유지해야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC15] - UI Testing in Xcode]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC15-UI-Testing-in-Xcode</link>
            <guid>https://velog.io/@grumpy-sw/WWDC15-UI-Testing-in-Xcode</guid>
            <pubDate>Wed, 01 Feb 2023 11:03:46 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2015 &#39;UI Testing in Xcode&#39;를 시청하고 정리한 글입니다.</code></pre><h1 id="ui-testing-in-xcode">UI Testing in Xcode</h1>
<p><img src="https://i.imgur.com/jOiu7bP.png" alt=""></p>
<h2 id="overview">Overview</h2>
<p><img src="https://i.imgur.com/FCwT1nf.png" alt=""></p>
<p><strong>UI testing</strong></p>
<ul>
<li>User Interface 요소를 찾고 상호 작용하며 상태에 대한 UI properties들의 유효성을 검사할 수 있음.</li>
</ul>
<p><strong>UI recording</strong></p>
<ul>
<li>프로젝트에 대한 UI Test를 빠르게 설정할 수 있는 기능</li>
</ul>
<p><strong>Test reporting</strong></p>
<ul>
<li>테스트 결과의 pass, fail 및 result를 보여주는 기능</li>
</ul>
<p><img src="https://i.imgur.com/yo3L3Wu.png" alt=""></p>
<p>UI Testing의 핵심 기술은 두 가지이다. 바로 XCTest와 Accessibility</p>
<p><img src="https://i.imgur.com/UDFZOWe.png" alt=""></p>
<p>XCTest는 Xcode에서 제공하는 테스트 프레임워크이다.
XCTest가 제공하는 기능은 다음과 같다.</p>
<ul>
<li>테스트 케이스에 대한 하위 클래스를 만들어 테스트용 메서드를 구현하여 테스트를 실행할 수 있다.</li>
<li>Assertion을 통해 예상 결과와 테스트 결과를 비교할 수 있다.</li>
<li>Xcode와 통합되어 있어 코드 작성이나 디버깅 등을 IDE에서 모두 처리할 수 있다.</li>
<li>Swift와 Objective-C 모두 지원</li>
</ul>
<p>(* XCTest가 처음 도입된 것은 Xcode 5. Xcode 6에서는 Performance Test를 지원하도록 확장되었고 이번 Xcode 7에서는 UI Test를 지원하게 됨)</p>
<p><img src="https://i.imgur.com/xrFEjNS.png" alt=""></p>
<p>Accessibility(접근성)은 다른 모든 사용자에게도 동일한 User Experience를 제공하기 위해 도입된 기술이다. 예를 들어 Voice Over를 통해 시각 장애인들에게도 일반 사용자들과 같은 동일한 경험을 제공할 수 있는데 UI Test에서 이를 사용할 수 있다. 
또한 접근성을 통해 테스트 환경에서 실제로 사용자가 사용하는 것처럼 조작할 수 있다.</p>
<p><img src="https://i.imgur.com/Vw1Eezv.png" alt=""></p>
<p>UI Test를 하기 위한 필수 조건</p>
<ul>
<li>iOS 9/OS X 10.11 이상</li>
<li>iOS Device가 신뢰할 수 있는 상태</li>
</ul>
<h2 id="xctest-and-ui-testing">XCTest and UI Testing</h2>
<h3 id="1-xcode-target-type">1. Xcode target type</h3>
<p><img src="https://i.imgur.com/CpAZZ0v.png" alt=""></p>
<p>UI Testing Xcode Targets</p>
<ul>
<li>테스트 중인 앱과 별도의 프로세스에서 실행될 수 있어야 함</li>
<li>개인 정보 보호에서 접근성 사용 권한이 있어야 함</li>
<li>Cocoa Touch UI Testing Bundle(iOS)/Cocoa UI Testing Bundle(OS X)</li>
<li>Target to be Tested에 테스트 대상으로 설정이 되어야 함</li>
</ul>
<h3 id="2-api">2. API</h3>
<p><img src="https://i.imgur.com/qwUfNr6.png" alt=""></p>
<p>새로운 API는 크게 세 가지가 있다.
<strong>XCUIApplication</strong>, <strong>XCUIElement</strong>, <strong>XCUIElementQuery</strong></p>
<h3 id="3-ui-recording">3. UI Recording</h3>
<p><img src="https://i.imgur.com/PedQJ4X.png" alt=""></p>
<p>Device 혹은 시뮬레이터에 직접 상호작용을 하는 기능으로 
해당 동작에 대한 코드를 생성해준다.</p>
<h3 id="ui-테스트-과정">UI 테스트 과정</h3>
<p><img src="https://i.imgur.com/KLGJAID.png" alt=""></p>
<ol>
<li>UI testing target 추가</li>
<li>UI Recording으로 앱과 상호작용 하는 코드 생성</li>
<li>XCTAssertion으로 유효성 검사 수행</li>
</ol>
<h2 id="ui-testing-api">UI Testing API</h2>
<p><img src="https://i.imgur.com/XxFPNKy.png" alt=""></p>
<p>UI 테스트의 과정을 API별로 핵심만 간단하게 나타낸 코드</p>
<h3 id="xcuiapplication">XCUIApplication</h3>
<p><img src="https://i.imgur.com/yFfkdrE.png" alt=""></p>
<pre><code class="language-swift">let app = XCUIApplication()
app.launch()</code></pre>
<ul>
<li>테스트 앱의 프록시. 앱의 Life Cycle과 무관하게 별도의 프로세스에서 실행된다.</li>
<li>시작할 때 항상 새로운 프로세스를 생성한다.<ul>
<li>중간 상태인 경우 테스트 환경이 달라질 수 있기 때문에 처리해야 하는 변수를 최소화</li>
</ul>
</li>
<li>시작점 역할을 한다.</li>
</ul>
<h3 id="xcuielement">XCUIElement</h3>
<p><img src="https://i.imgur.com/vagk47M.png" alt=""></p>
<ul>
<li>테스트할 App의 인터페이스 요소에 관한 것</li>
<li>Cell, Button 등과 같은 타입이 있다. </li>
<li>타입과 레이블 값을 조합하여 요소를 찾는다.</li>
<li>App 상에서 트리 구조를 형성한다.</li>
<li>트리 구조와 타입, 식별자를 통해 쿼리는 참조 대상을 찾는다.</li>
</ul>
<p><img src="https://i.imgur.com/Xp1JMNH.png" alt=""></p>
<p>이렇게 짜여진 이유는 <strong>요소의 고유성</strong> 때문</p>
<p><img src="https://i.imgur.com/kLt1LWh.png" alt=""></p>
<ul>
<li>모든 요소는 쿼리를 통해서 접근하고 쿼리는 단일 인스턴스이어야 한다.</li>
<li>그렇지 않으면 사용자의 이벤트를 정확하게 반영할 수 없기 때문<ul>
<li>예를 들어 &quot;하트 모양 버튼을 클릭한다.&quot; 라는 이벤트가 주어질 때, 동일한 모양의 버튼이 여러 개 있다면?</li>
<li>사람이 직접 테스트를 진행하는 것이 아니기 때문에, 일치하는 항목이 정확히 하나만 있음을 보장해야 한다.</li>
<li>단 한가지 예외는 <code>exists</code> 프로퍼티</li>
</ul>
</li>
</ul>
<p>이벤트 합성</p>
<p><img src="https://i.imgur.com/5efcCTH.png" alt=""></p>
<p>사용자와의 상호 작용을 시뮬레이션 하는 방법. 대체로 플랫폼 별로 다름
Example)</p>
<ul>
<li>button.click() : OS X</li>
<li>button.tap() : iOS</li>
</ul>
<p>두 플랫폼에서 동일한 유형도 있음</p>
<ul>
<li>textField.typeText(&quot;Hello, World!&quot;) : OS X &amp; iOS</li>
</ul>
<h3 id="xcuielementquery">XCUIElementQuery</h3>
<p><img src="https://i.imgur.com/391Qj0t.png" alt=""></p>
<p>요소를 특정하기 위한 API. 액세스할 수 있는 요소의 컬렉션 형태
접근성에 표시되는 요소만 찾을 수 있다.
주로 사용하는 features</p>
<ul>
<li><code>count</code></li>
<li>identifier로 특정</li>
<li><code>elementAtIndex()</code></li>
</ul>
<p>XCUIElementQuery는 계층 구조와 필터링을 통해 사용한다.</p>
<ol>
<li>계층 구조(Relationships)</li>
</ol>
<p><img src="https://i.imgur.com/PMhfqUg.png" alt=""></p>
<p>Descendants: 본인 하위 모든 subview들을 포함
Children: Descendants보다 제한적으로 본인 바로 아래 단계에 있는 elements만을 지칭
Containment: 고유한 데이터를 포함하는 경우에 유용</p>
<ol start="2">
<li>필터링(Filtering)</li>
</ol>
<p><img src="https://i.imgur.com/Ddblm8O.png" alt=""></p>
<p>요소에 대한 타입(Button, Table 등) 혹은 Identifier 등을 통해 쿼리를 필터링 가능</p>
<h4 id="쿼리-표현-방법">쿼리 표현 방법</h4>
<p>쿼리를 생성할 때 Relationships와 Filtering을 결합하여 적절하게 활용한다.</p>
<p># <strong>descendantsMatchingType()</strong></p>
<p><img src="https://i.imgur.com/SMcaZT4.png" alt=""></p>
<p><img src="https://i.imgur.com/FG6E6Bq.png" alt=""></p>
<p>첫 번째는 <code>descendantsMatchingType()</code>으로, 가장 일반적으로 사용하는 쿼리이다. 
타입을 통해 매칭되는 모든 Descendants Elements를 편리하게 찾을 수 있다.</p>
<p># <strong>childrenMatchingType()</strong></p>
<p><img src="https://i.imgur.com/xzO0FCG.png" alt=""></p>
<p>두 번째는 Direct Child 관계의 Elements만 찾는 <code>childrenMatchingType()</code>이다.</p>
<p>일반적으로 잘 쓰이진 않지만 descendantsMatchingType()과 비교하여 차별점을 갖고 있다.</p>
<p># <strong>containingType()</strong></p>
<p><img src="https://i.imgur.com/XYbm8h5.png" alt=""></p>
<p>마지막은 고유한 특정 값을 통해 찾는 <code>containingType()</code>이다.</p>
<p>Cell들은 모두 익명이지만, Cell의 Label 값을 통해 원하는 특정 Cell을 골라낼 수 있다.</p>
<h4 id="쿼리-결합">쿼리 결합</h4>
<p><img src="https://i.imgur.com/XmpGQGI.png" alt=""></p>
<p>쿼리의 또다른 특징 중 하나는 서로 결합할 수 있다.
쿼리의 출력을 가져와 다음 쿼리의 입력으로 만들 수 있다.</p>
<p>쿼리를 통해 요소에 접근하는 방법</p>
<p><img src="https://i.imgur.com/ojoexDh.png" alt=""></p>
<p>위에서 언급했듯이 컬렉션 형태이므로 Subscript 혹은 Index로 찾을 수 있다.
Unique한 요소를 찾는다면 element도 사용 가능하다.(예: 화면에서 항상 UINavigationBar는 유일함)</p>
<h4 id="쿼리-평가">쿼리 평가</h4>
<p><img src="https://i.imgur.com/XtO4Pau.png" alt=""></p>
<p>쿼리에 대한 값 등은 호출 시에 결정된다.
또한 UI가 변경되면 쿼리의 값 또한 변경되어 항상 최신 View에 대한 쿼리와 값을 유지한다.
이런 면에서 쿼리와 요소는 URL과 유사하다.</p>
<ul>
<li>URL을 자유롭게 만들 수 있다.</li>
<li>리소스를 즉시 가져오지 않기 때문에 URL이 잘못되었는지 생성 시점에서는 알 수 없다.</li>
</ul>
<h3 id="정리">정리</h3>
<p>XCUIApplication</p>
<ul>
<li>테스트할 App을 실행하기 위한 클래스</li>
</ul>
<p>XCUIElement</p>
<ul>
<li>앱을 구성하는 UI Element에 대한 클래스</li>
</ul>
<p>XCUIElementQuery</p>
<ul>
<li>UI Element를 지정하는 방법에 대한 클래스</li>
</ul>
<h2 id="test-reports">Test Reports</h2>
<p><img src="https://i.imgur.com/o0XjKsu.png" alt=""></p>
<p>테스트 결과에 대한 보고서</p>
<ul>
<li>성공, 실패 여부와 실패 시 실패 원인 제공</li>
<li>Xcode, Xcode Server 모두 동일한 UI 제공</li>
<li>(Xcode Server) Device 별 테스트 결과 제공</li>
<li>실패했을 때의 스크린 샷</li>
</ul>
<p><img src="https://i.imgur.com/AUkmlYP.png" alt=""></p>
<p>또한 API 호출에 대한 Nested Activities 제공</p>
<h2 id="when-to-use-ui-testing">When to Use UI Testing</h2>
<p>이미 Unit Test가 있는데, UI Test는 언제 사용하는지??</p>
<p><img src="https://i.imgur.com/NhcNeJS.png" alt=""></p>
<p>UI Test는 Unit Test를 대체하는 것이 아닌 보완하는 역할을 한다. Model과 Controller의 논리적인 부분에 대해서는 Unit Test를 여전히 사용해야 한다.</p>
<p><img src="https://i.imgur.com/3P4abLc.png" alt=""></p>
<p>그럼에도 불구하고 다음과 같은 상황에서는 UI Test가 효과적이다.
앱의 사용방법을 안내하거나, 앱의 자동화하기 좋은 workflow 등</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC21] - Meet the UIKit button system]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC21-Meet-the-UIKit-button-system</link>
            <guid>https://velog.io/@grumpy-sw/WWDC21-Meet-the-UIKit-button-system</guid>
            <pubDate>Wed, 18 Jan 2023 11:01:07 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2021 &#39;Meet the UIKit button system&#39;를 읽고 정리한 글입니다.</code></pre><h1 id="meet-the-uikit-button-system">Meet the UIKit button system</h1>
<blockquote>
<p>iOS에서 새로 업데이트 된 버튼에 대해 알아보자!</p>
</blockquote>
<h3 id="buttons-in-ios-15">Buttons in iOS 15</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210724103-332647c0-3c4a-4c58-a758-4a6fe5965acd.png" alt="스크린샷 2023-01-05 오전 3 10 41"></p>
<p>버튼은 사용자의 입력을 요청하는 가장 일반적인 방법.
iOS 15에서 UIKit은 기본적으로 네 가지 스타일을 제공한다.(Plain / Gray / Tinted / Filled)
그리고 다음과 같은 기능들이 추가되었다.</p>
<ul>
<li>Dynamic Type(supported by default)</li>
<li>Multiline</li>
<li>Accessibility</li>
<li>Easier customization -&gt; UIButtonConfiguration</li>
</ul>
<h3 id="uibuttonconfiguration">UIButtonConfiguration</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210724162-f9e209da-ae92-4281-9c8b-52727d587325.png" alt="스크린샷 2023-01-05 오전 3 11 45"></p>
<p><code>Sign In</code> 버튼을 구성하는 코드는 다음과 같다. 이 버튼은 매우 중요한 버튼이므로 우리는 이 버튼을 <code>filled</code> 스타일로 변경하여 더 눈에 띄게 만들어볼 것이다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210724202-4c3372a8-97b1-4b76-8432-b9609afea95a.png" alt="스크린샷 2023-01-05 오전 3 12 18"></p>
<p>버튼의 configuration 프로퍼티에 <code>.filled()</code> 값을 입력하기만 하면 된다. 버튼의 타이틀과 이미지에 대한 코드를 각각 업데이트하지 않고 버튼 스타일을 쉽게 변경할 수 있다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210724245-9bc8f09c-4580-4486-aef6-e977f3ef9544.png" alt="스크린샷 2023-01-05 오전 3 13 31"></p>
<p>UIButtonConfiguration에는 스타일 말고도 다양한 옵션이 있다. <code>Add to Cart</code> 버튼은 지금 기본적인 버튼 스타일이지만 UIButtonConfiguration을 활용하여 UX를 강화할 것이다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210724826-b3d88968-4efc-4fe8-ab1b-f3435047d8cb.png" alt="스크린샷 2023-01-05 오전 3 14 03"></p>
<p><code>.tinted()</code> 스타일의 config에 <code>title</code>, <code>image</code>, <code>imagePlacement</code> 설정을 할 수 있다.
버튼에 이미지를 추가하여 버튼이 어떤 역할을 하는지 한 눈에 파악할 수 있게 되었다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210725400-46902974-fe6d-4811-9ce5-ae5a3100fd7c.png" alt="스크린샷 2023-01-05 오전 3 14 42"></p>
<p>여기에 추가적으로 적용할 두 가지 기능이 있다.</p>
<ol>
<li>subtitle 표시</li>
<li>버튼을 누르면 filled로 이미지 전환</li>
</ol>
<p><img src="https://user-images.githubusercontent.com/63997044/210725036-d57956a4-432d-4d35-b0b3-1fcd8696da25.png" alt="스크린샷 2023-01-05 오전 3 16 17"></p>
<p>이를 위해 적절한 시점에 이미지 속성을 업데이트해야 하기 떄문에 <code>configurationUpdateHandler</code>가 필요하다.
핸들러를 사용하면 좋은 점은 업데이트 코드를 중앙집중할 수 있다는 점이다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210725048-868d66a4-55b6-486b-81f3-9cc1271e7f18.png" alt="스크린샷 2023-01-05 오전 3 16 34"></p>
<p>configurationUpdateHandler를 호출하는 방법: <code>setNeedsUpdateConfiuration()</code> 메서드</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210725545-21d1e30b-4064-40b6-b34e-a51b65f1362e.png" alt="스크린샷 2023-01-05 오전 3 16 54"></p>
<p><code>showsActivityIndicator = true</code> 버튼에 Activity Indicator가 활성화된다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210725557-0f64708b-d1fc-44d8-bb97-ac7cd9e3264a.png" alt="스크린샷 2023-01-05 오전 3 17 05"></p>
<p>버튼의 내부 레이아웃 구조는 다음과 같다. contentInset, imagePadding 및 titlePadding을 통해 버튼 요소 사이의 공간을 조정할 수 있다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210725560-da37cfc9-23b0-429e-b93c-e4587b0f8db3.png" alt="스크린샷 2023-01-05 오전 3 17 19"></p>
<p>이런 식의 구성 방법을 시멘틱 스타일이라고 한다.
시멘틱 스타일을 사용하면 버튼을 간단하게 만들 수 있다.</p>
<img width="1059" alt="스크린샷 2023-01-05 오후 4 33 44" src="https://user-images.githubusercontent.com/63997044/210726052-78179c97-b476-4ed9-8c79-a88a2321d339.png">

<p>Check Out 버튼을 구성한다면 시멘틱 스타일과 configureUpdateHandler를 조합하여 설정하면 된다.</p>
<h3 id="toggle-buttons">Toggle Buttons</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210726318-5fc840bf-d00a-4312-bd69-e1f6e5b6c527.png" alt="스크린샷 2023-01-05 오전 3 19 13"></p>
<p>iOS에서의 toggle 버튼
on/off 상태를 나타냄
UIControl에서 selected 상태를 유지</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210726341-5b23e494-0810-406c-b4a9-d282bec535ab.png" alt="스크린샷 2023-01-05 오전 3 19 29"></p>
<p>일반 버튼을 toggle 버튼으로 만드는 방법</p>
<ul>
<li>button.changesSelectionAsPrimaryAction = true</li>
</ul>
<h3 id="pop-up-button">Pop-up Button</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210726459-fb56c5b8-61b1-42ed-bd2d-2cefc134598e.png" alt="스크린샷 2023-01-05 오전 3 20 10"></p>
<p>여기서 버튼은 메뉴에 해당하고 하위 항목을 선택하면 색상과 이미지가 전환되고 버튼 또한 업데이트된다.</p>
<img width="460" alt="스크린샷 2023-01-05 오후 4 37 26" src="https://user-images.githubusercontent.com/63997044/210726646-0b899e51-fa72-428a-bd46-89c6a2d42ef2.png">

<p>Pop-up 버튼을 구현하는 코드를 알아보자.
button의 메뉴에 목록을 등록한다. 특정 아이템을 기본으로 지정하려면 state: .on 옵션을 추가한다.
Pop-up 버튼을 구현하려면 showsMenuAsPrimaryAction을 true로 설정해야 한다.
또한 토글 버튼과 마찬가지로 changesSelectionAsPrimaryAction을 true로 변경해야 한다.</p>
<h3 id="mac-catalyst">Mac Catalyst</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210726817-62e7ef3b-beed-48cb-b7a9-bdd83d893237.png" alt="스크린샷 2023-01-05 오전 3 22 48"></p>
<p>Mac Catalyst로 빌드한 Mac 앱은 iPad 앱과 코드를 공유한다.
iOS식으로 만든 Pull-down, Pop-up, Toggle 버튼들은 자동으로 Mac 버전으로 적용된다.</p>
<img width="459" alt="스크린샷 2023-01-05 오후 4 39 51" src="https://user-images.githubusercontent.com/63997044/210727036-8a43ced6-d718-4ed6-b993-26331a9e2ec6.png">

<p>iPad app button/Mac Catalyst button</p>
<img width="558" alt="스크린샷 2023-01-05 오후 4 40 28" src="https://user-images.githubusercontent.com/63997044/210727132-49bc04df-465c-4d54-9bc0-522612e66002.png">

<p>preferredBehavioralStyle = <code>.automatic</code> 혹은 <code>.pad</code>으로 지정하여 기본 스타일을 변경할 수 있다.</p>
<h3 id="uimenu">UIMenu</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210727195-03204a20-ff0c-45f3-91a1-2524661d444b.png" alt="스크린샷 2023-01-05 오전 3 24 51"></p>
<p>Pop-up 버튼 예제에서 봤듯이 대부분의 기능은 UIMenu에 구축된다.
UIButtonConfiguration과 결합하여 다양한 스타일의 버튼을 생성할 수 있다.
또한 메뉴 내에서의 계층 구조 등 여러 기능들을 제공한다.</p>
<p><img src="https://user-images.githubusercontent.com/63997044/210727199-1fe8c020-b980-48a7-b94b-5d0bd15266fd.png" alt="스크린샷 2023-01-05 오전 3 25 09"></p>
<p>하위 메뉴를 구성하는 Pull-down 메뉴 바 코드이다.
메뉴 내 하위 메뉴에서 단일 선택을 지원하기 위해 options: .singleSelection 옵션을 적용한다.
menu.selectedElements 프로퍼티를 통해 선택된 하위 메뉴에 대해 쉽게 액세스할 수 있다.</p>
<h3 id="요약">요약</h3>
<p><img src="https://user-images.githubusercontent.com/63997044/210727204-eeb6064b-f426-4158-86a4-3b57dd521207.png" alt="스크린샷 2023-01-05 오전 3 25 17"></p>
<p>App에서 better button을 만드는 방법!</p>
<ul>
<li>configuration을 추가하여 풍부한 스타일을 사용한다.</li>
<li>기존 Picker 스타일을 Toggle, Pop-up 버튼으로 대체한다.</li>
<li>UIButton을 상속한 서브클래스를 대체한다.</li>
<li>Mac Catalyst 자동 변환을 사용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[소수 판별 알고리즘]]></title>
            <link>https://velog.io/@grumpy-sw/%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@grumpy-sw/%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Wed, 18 Jan 2023 10:45:33 GMT</pubDate>
            <description><![CDATA[<h2 id="단일-숫자-소수-판별">단일 숫자 소수 판별</h2>
<p>주어진 자연수 n에 대하여 소수의 여부를 확인하고 싶을 때는 n의 제곱근까지만 약수 여부를 검증하는 것이 가장 빠르다.</p>
<pre><code class="language-swift">func isPrimeNumber(_ n: Int) -&gt; Bool {
    let squareRoot = Int(sqrt(Double(n)))

    if n == 1 {
        return false
    }

    if n == 2 || n == 3 {
        return true
    }

    for number in 2...squareRoot {
        if n % number == 0 {
            return false
        }
    }

    return true
}</code></pre>
<hr>
<h2 id="sieve-of-eratosthenes에라스토테네스의-체">Sieve of Eratosthenes(에라스토테네스의 체)</h2>
<p>2부터 n까지의 모든 소수를 찾는 방법이다.
원리: 범위만큼 배열을 생성하고, 소수가 아닌 수를 하나씩 지워나간다.</p>
<ol>
<li>각각의 값이 소수 여부를 나타내는 크기가 n+1인 Boolean 배열을 생성한다.</li>
<li>2부터 시작해서 각 숫자의 배수들을 모두 false로 바꾼다.</li>
<li>위의 과정을 끝까지 거친 후 true로 남아있는 수들이 n까지의 모든 소수이다.</li>
</ol>
<p><img src="https://i.imgur.com/KN7OHTT.png" alt=""></p>
<pre><code class="language-swift">func sieve(_ n: Int) -&gt; [Int] {
    // 소수 판별 배열 생성
    var sieves = Array(repeating: true, count: n+1)
    sieves[0] = false
    sieves[1] = false

    // 2부터 n의 제곱근까지 탐색 시작
    // index의 배수 위치에 있는 모든 값을 false로 바꾼다.
    var index = 2
    while index * index &lt;= n {
        if sieves[index] {
            var k = index * index
            while k &lt;= n {
                sieves[k] = false
                k += index
            }
        }
        index += 1
    }

    // 소수인 숫자들만 따로 모은 배열 생성하여 리턴
    var primeNumbers = [Int]()
    for index in 0..&lt;sieves.count {
        if sieves[index] {
            primeNumbers.append(index)
        }
    }

    return primeNumbers
}
</code></pre>
<p>이 알고리즘의 시간 복잡도는 <code>O(nloglogn)</code>이다.</p>
<hr>
<h2 id="factorization인수분해">Factorization(인수분해)</h2>
<p>숫자를 주요 인수로 분해하는 과정을 인수분해라고 한다. 주어진 숫자 n에 대하여 n을 소수끼리의 곱으로 인수분해하려고 한다.
위의 아리스토테네스의 체 알고리즘을 약간 수정하여 빠른 인수분해가 가능하다. </p>
<p>먼저 인수분해를 하기 위한 배열 생성이 필요하다.</p>
<pre><code class="language-swift">func prepareArrayForFactorization(_ n: Int) -&gt; [Int] {
    var array = Array(repeating: 0, count: n+1)
    var index = 2

    while index * index &lt;= n {
        if array[index] == 0 {
            var k = index * index
            while k &lt;= n {
                if array[k] == 0 {
                    array[k] = index
                }
                k += index
            }
        }
        index += 1
    }
    return array
}</code></pre>
<p>이 함수는 소수인 경우 0, 소수가 아닌 수는 자신을 구성하는 가장 작은 소수로 구성된다. 예를 들어 만약 n = 20이라면 다음과 같이 배열이 생성된다.
<img src="https://i.imgur.com/0zWOuFj.png" alt=""></p>
<p>다음으로 주어진 수 n에 대하여 소인수 분해를 하는 함수를 작성한다. 
위에서 만든 배열의 값으로 n을 계속 나누어 0이 나올 때까지 나눈 수를 primeFactors 배열에 담고, 마지막으로 나누고 남은 수까지 넣어주면 소인수분해가 완료된다. 예를 들어 n = 20이라면 소인수분해의 결과로 <code>[2, 2, 5]</code>를 반환하게 된다.</p>
<pre><code class="language-swift">func factorization(_ n: Int) -&gt; [Int] {
    var primeFactors: [Int] = []
    let array = prepareArrayForFactorization(n)

    var x = n

    while array[x] &gt; 0 {
        primeFactors.append(array[x])
        x /= array[x]
    }
    primeFactors.append(x)
    return primeFactors
}</code></pre>
<p>이 함수의 시간 복잡도는 <code>O(log x)</code>이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC18] - A Tour of UICollectionView]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC18-A-Tour-of-UICollectionView</link>
            <guid>https://velog.io/@grumpy-sw/WWDC18-A-Tour-of-UICollectionView</guid>
            <pubDate>Sun, 08 Jan 2023 05:04:03 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2018 &#39;A Tour of UICollectionView&#39;를 읽고 정리한 글입니다.</code></pre><h1 id="a-tour-of-uicollectionview">A Tour of UICollectionView</h1>
<h3 id="-introduction"># Introduction</h3>
<blockquote>
<p><code>UICollectionView</code>는 애플리케이션에서 뛰어난 사용자 경험을 달성하는 데 도움이 되는 유연하고 강력한 도구입니다. UICollectionView API를 시작하기부터 고급 업데이트 애니메이션까지 다루는 방법을 알아봅시다.</p>
</blockquote>
<h2 id="-uicollectionview-concepts"># UICollectionView Concepts</h2>
<img width="1174" alt="스크린샷 2022-09-15 오후 4 16 11" src="https://user-images.githubusercontent.com/63997044/190339533-c0db0176-a424-47ba-b6d5-623ac2f65d3f.png">

<p>오늘 다룰 것</p>
<ul>
<li>레이아웃</li>
<li>업데이트</li>
<li>애니메이션</li>
</ul>
<h3 id="key-uicollectionview-concepts">Key UICollectionView Concepts</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 16 51" src="https://user-images.githubusercontent.com/63997044/190339651-f6280b2f-7f99-440b-9243-b0e3443da0ba.png">

<p>코드를 보기 전에 CollectionView에 대해 이해해야 할 세 가지 핵심 개념을 보겠다. 바로 <strong>Layout</strong>, <strong>Data Source</strong>, <strong>Delegate</strong>이다.</p>
<h3 id="uicollectionviewlayout">UICollectionViewLayout</h3>
<img width="1172" alt="스크린샷 2022-09-15 오후 4 16 59" src="https://user-images.githubusercontent.com/63997044/190339681-828acf92-9690-443b-b5b4-df30cdfd246f.png">

<p><strong>Layout</strong></p>
<ul>
<li>컨텐츠들의 시각적 배열.</li>
<li>UITableView와 유사</li>
<li>Attribute들: Bounds, center, frame...</li>
<li>CollectionViewLayout은 추상 클래스이므로 직접 사용하는 것이 아니라 하위 클래스를 사용한다.</li>
</ul>
<h3 id="uicollectionviewflowlayout">UICollectionViewFlowLayout</h3>
<img width="1175" alt="스크린샷 2022-09-15 오후 4 17 24" src="https://user-images.githubusercontent.com/63997044/190339757-9d4ab3d3-dd3d-4ddc-9681-eb63fb154703.png">

<ul>
<li>UICollectionViewLayout의 Concrete 하위 클래스(이미 만들어져있는 클래스)</li>
<li>UICollectionViewDelegate를 확장한 UICollectionViewDelegateFlowLayout사용</li>
<li>line-based</li>
</ul>
<h3 id="flow-layout">Flow Layout</h3>
<h3 id="1-line-orientation">1. Line Orientation</h3>
<img width="1168" alt="스크린샷 2022-09-15 오후 4 17 59" src="https://user-images.githubusercontent.com/63997044/190339871-50151c63-1aff-4e69-8425-43520d4d3c2d.png">

<p>이것을 따라서 항목을 배치할 수평선의 방향을 의미한다. Horizontal / Vertical 두 가지가 존재한다.</p>
<h3 id="2-line-spacing">2. Line Spacing</h3>
<img width="1172" alt="스크린샷 2022-09-15 오후 4 18 26" src="https://user-images.githubusercontent.com/63997044/190339957-12cf71e7-a58f-4422-96e6-c25bc5556453.png">

<p>Line Spacing은 그림에서 수평선 사이의 간격을 의미하는데, 항목들이 배치되는 수평선 사이의 공간을 의미한다.</p>
<h3 id="3-inter-item-spacing">3. Inter-Item Spacing</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 18 46" src="https://user-images.githubusercontent.com/63997044/190340033-2d4d16f4-5f3d-4ef3-b811-4fbc48dac2d3.png">

<p>Inner-Item Spacing은 레이아웃 선을 따라서 생긴 항목들 사이의 공간들을 의미한다. </p>
<h3 id="uicollectionviewdatasource">UICollectionViewDataSource</h3>
<img width="1174" alt="스크린샷 2022-09-15 오후 4 19 15" src="https://user-images.githubusercontent.com/63997044/190340141-ba4d62b5-d2c6-45b9-84d0-fc8c32792eea.png">

<p><strong>Data Source</strong>
UITableView를 사용할 때의 코드와 매우 유사하다. Layout이 컨텐츠를 어디에 그릴지 결정하는 것이라면, Data Source는 컨텐츠가 무엇인지에 해당한다.
여기서 핵심적인 메서드는 다음 세 가지</p>
<ul>
<li><code>optional func numberOfSection(in collectionView: UICollectionView) -&gt; Int</code>: CollectionView의 섹션 수이며, 디폴트 값은 1이다.</li>
<li><code>func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int</code>: 각각의 개별 섹션에 있는 항목들의 수를 나타낸다.</li>
<li><code>func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell</code>: 사용자에게 표시할 실제 컨텐츠를 제공하는 메서드이다.</li>
</ul>
<h3 id="uicollectionviewdelegate">UICollectionViewDelegate</h3>
<img width="1173" alt="스크린샷 2022-09-15 오후 4 19 39" src="https://user-images.githubusercontent.com/63997044/190340226-cb851e3a-debe-4863-8778-6331973a254b.png">

<p><strong>Delegate</strong></p>
<ul>
<li>Delegate를 사용하는 것은 부가적(optional)인 행동이다. </li>
<li>CollectionView는 UIScrollView를 상속받기 때문에 ScrollView에서 제공하는 것과 동일한 Delegate를 사용하지만 확장할 수 있다.</li>
<li>스크롤 동작을 수정할 경우, 그리고 콘텐츠와 상호 작용할 때의 제어(강조 표시 혹은 선택에 대한 기능 제공) 등을 수정하는 경우 <code>UICollectionViewDelegate</code>의 메서드로 작업한다. </li>
<li>또한 WillDisplayItem(화면에 나타났을 때), DidEndDisplayingItem(화면에 사라졌을 때)와 같은 메서드도 제공한다.<pre><code class="language-swift">func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
  &lt;#code#&gt;
}
</code></pre>
</li>
</ul>
<p>func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    &lt;#code#&gt;
}</p>
<p>```</p>
<h2 id="demo코드로-살펴보기">Demo(코드로 살펴보기)</h2>
<img width="897" alt="스크린샷 2022-09-15 오후 4 27 05" src="https://user-images.githubusercontent.com/63997044/190341832-c2c9172e-afa3-4c43-92d0-05a08d0bc098.png">

<p>미리 준비한 UICollectionViewFlowLayout을 상속받은 ColumnFlowLayout을 준비했음.
이걸로 CollectionView를 생성한다.
그리고 DataSource와 Delegate를 self로 선언한다.</p>
<img width="878" alt="스크린샷 2022-09-15 오후 4 28 03" src="https://user-images.githubusercontent.com/63997044/190342026-9e2c8dd4-2590-419f-b479-0d09f5c690d2.png">

<p>DataSource 관련 필수 메서드 2개를 구현
<img width="884" alt="스크린샷 2022-09-15 오후 4 28 32" src="https://user-images.githubusercontent.com/63997044/190342156-fdf83995-d366-4e4b-a80d-be24b2311213.png"></p>
<p>Delegate 메서드 구현. 클릭했을 때의 동작 구현을 위해 메서드 구현</p>
<img width="865" alt="스크린샷 2022-09-15 오후 4 30 05" src="https://user-images.githubusercontent.com/63997044/190342498-227d6530-39e0-4b14-876d-d7be7a86a4f0.png">

<p>ColumnFlowLayout의 코드 내용. 항목 크기를 설정하고, 간격을 보기좋게 조정했다.</p>
<img width="860" alt="스크린샷 2022-09-15 오후 4 30 34" src="https://user-images.githubusercontent.com/63997044/190342609-8a6346a9-2fe8-45dc-8964-6844fda779d5.png">

<p>시뮬레이터 실행 모습.</p>
<img width="858" alt="스크린샷 2022-09-15 오후 4 31 16" src="https://user-images.githubusercontent.com/63997044/190342752-c8b9da88-f34f-4210-90ca-543aac0060ae.png">

<p>UICollectionViewLayoutsPrepare() 메서드는 레이아웃이 무효화될 때마다 호출되며, FlowLayout의 경우 CollectionView의 bounds가 변경될 때마다 레이아웃이 무효화된다. 예를 들면 디바이스가 회전하거나 실행하는 화면이 조정되는 경우이다.</p>
<p>사진은 가로로 디바이스를 회전시킨 상황인데, 회전하면서 레이아웃이 무효화되고 재설정하는 메서드가 호출되어 레이아웃을 다시 맞춰주는 모습이다. 그러나 가로로 방향을 회전하면서 더 많은 항목들을 표시할 수 있다. 이 때 FlowLayout을 사용하면 쉽게 해결할 수 있다. 앞에서 얘기했던 대로 Flow Layout은 다음 줄로 넘어가기 전에 한 줄에 가능한 한 많은 항목들을 배치하려고 하는 성질이 있다. 따라서 항목의 크기를 변경해주면 된다.</p>
<img width="847" alt="스크린샷 2022-09-15 오후 4 33 01" src="https://user-images.githubusercontent.com/63997044/190343110-92d7cb35-403e-44ba-b2b1-7cd505fc0560.png">

<p>itemSize 설정 부분을 다음과 같이 바꿔주었다. 임의의 너비를 300포인트로 잡고 한 줄에 들어갈 수 있는 열 수를 계산한다. 이를 기반으로 사용 가능한 너비를 구해 itemSize에서 사용한다.</p>
<img width="843" alt="스크린샷 2022-09-15 오후 4 33 35" src="https://user-images.githubusercontent.com/63997044/190343246-9856676e-f839-4175-a8b6-382e1a0448ac.png">

<p>원하는 대로 결과가 출력된 모습.</p>
<hr>
<h2 id="when-to-go-custom">When to Go Custom?</h2>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 20 45" src="https://user-images.githubusercontent.com/63997044/190340430-b2057502-d10d-4e6f-979a-1a83ad3817b3.png">

<p>이것은 Flow Layout으로 작동하지 않을 것이다. 왜냐하면 라인 기반 레이아웃의 모습이 아니기 때문이다.</p>
<h3 id="creating-a-custom-uicollectionviewlayout">Creating a Custom UICollectionViewLayout</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 21 07" src="https://user-images.githubusercontent.com/63997044/190340499-4dee2e1a-e36b-4afa-8372-86bd1667f376.png">

<p>이런 레이아웃을 짜려면 당연하게도 커스텀 레이아웃을 만들어야 한다. 이럴 때 사용하는 유용한 4+1개의 메서드를 소개하겠다.</p>
<h3 id="providing-content-size">Providing Content Size</h3>
<img width="1176" alt="스크린샷 2022-09-15 오후 4 21 28" src="https://user-images.githubusercontent.com/63997044/190340563-66172f37-a007-464a-a8ed-c2893f866de3.png">

<p>첫 번째 메서드는 <code>collectionViewContentSize</code>.
UICollectionView가 UIScrollView의 하위 클래스인 것을 기억해야 한다.
UIScrollView의 기능 중 하나는 커다란 컨텐츠 영역이 있고 그 안에서 컨텐츠가 이동한다는 것이다.
이 메서드(프로퍼티)는 모든 항목들을 포함하는 사각형 모양의 크기를 반환한다.</p>
<h3 id="providing-layout-attributes">Providing Layout Attributes</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 21 41" src="https://user-images.githubusercontent.com/63997044/190340605-a5a92208-9d46-4399-bcab-eccb07f3e761.png">

<p>다음 두 메서드는 레이아웃의 속성들을 제공하는 메서드이다.
먼저, <code>layoutAttributesForElements(in rect: CGRect)</code>는 항목을 처음 표시하거나 사용자의 스크롤로 인해서 화면에 표시해야 하는 항목을 알아야할 때 주기적으로 호출된다. 기하학적인 영역에 대한 것 
다음은 <code>layoutAttributesForItem(at indexPath: IndexPath)</code>이다. indexPath의 단일 항목에 대한 속성을 알려주는 메서드이다.</p>
<h3 id="preparing-the-layout">Preparing the Layout</h3>
<img width="1172" alt="스크린샷 2022-09-15 오후 4 21 49" src="https://user-images.githubusercontent.com/63997044/190340626-a3fdd140-fa74-423f-93df-c1419a5189df.png">

<p>네 번째 메서드는 <code>prepare()</code>.
레이아웃이 무효화될 때마다 호출된다.</p>
<h3 id="handling-bounds-changes-in-your-custom-layout">Handling Bounds Changes in Your Custom Layout</h3>
<img width="1175" alt="스크린샷 2022-09-15 오후 4 22 14" src="https://user-images.githubusercontent.com/63997044/190340716-a1d619b2-58bd-45af-a686-8163d98a5ea4.png">

<p>CollectionView의 bounds가 변경될 때마다 호출되는 메서드.
bounds의 변경이란 앱의 크기가 변경되거나, CollectionView의 크기가 변경되거나 혹은 스크롤을 통해 원점이 변경되는 상황을 가리킨다. 즉 이 메서드는 스크롤할 때마다 호출될 것인데 기본적으로는 false를 반환한다. </p>
<h2 id="demo코드로-살펴보기-1">Demo(코드로 살펴보기)</h2>
<img width="861" alt="스크린샷 2022-09-15 오후 4 34 40" src="https://user-images.githubusercontent.com/63997044/190343500-d100024a-9808-4229-9183-3f0b1aa19a51.png">

<p>MosaicLayout 생성. UICollectionViewLayout의 하위 클래스
contentBounds: 모든 항목들의 대표 범위
cachedAttributes: 성능이 중요할 떄 빠르게 참조할 수 있도록 캐시된 속성 배열</p>
<img width="849" alt="스크린샷 2022-09-15 오후 4 34 59" src="https://user-images.githubusercontent.com/63997044/190343564-774291ca-1440-464e-bb61-f8ac57e82b03.png">

<p>prepare는 레이아웃이 무효화될 때마다 한번씩 호출되기 때문에 설정을 잡아주기 적합하다.
createAttributes 메서드에서 할 동작</p>
<ul>
<li>나중에 빠르게 가져올 수 있도록 속성 배열에 캐싱</li>
<li>contentBounds와 attributes.frame의 결합(contentBounds의 최신화)</li>
</ul>
<img width="852" alt="스크린샷 2022-09-15 오후 4 36 03" src="https://user-images.githubusercontent.com/63997044/190343746-5e026c3c-15fe-408d-bc4c-7f25c8cad0a4.png">

<p>따라서 위에서 얘기했던 메서드를 구현한다. collectionView의 bounds가 변경될 때만 true를 반환.</p>
<img width="841" alt="스크린샷 2022-09-15 오후 4 36 58" src="https://user-images.githubusercontent.com/63997044/190343922-541256d0-e219-4d69-ae65-d48a37ad61d1.png">

<p>시뮬레이터로 확인한 레이아웃 모양. 모자이크 구성으로 멋지게 표현되었으며 회전을 통해 bounds가 변경되었기 때문에 레이아웃이 무효화되고 새로 업데이트하는 것을 볼 수 있다.
그러나 스크롤 성능은 매우 좋지 않다. 코드로 돌아가서 (스크롤할 때) 무슨 일이 일어나고 있는지 확인해보자.</p>
<img width="848" alt="스크린샷 2022-09-15 오후 4 37 37" src="https://user-images.githubusercontent.com/63997044/190344038-add4bfcc-5262-41dd-b95b-2056d6f18386.png">

<p>이 메서드가 스크롤하는 동안 너무 자주 호출되고 있다. 전체 배열을 필터링하기 때문에 CollectionView의 항목 수가 많은 경우 비용이 많이 든다.</p>
<img width="847" alt="스크린샷 2022-09-15 오후 4 38 36" src="https://user-images.githubusercontent.com/63997044/190344315-620eaf5a-eefd-4beb-9370-4c1f28fe0af8.png">

<p>layoutAttributesForElements 메서드를 효율적으로 개선해보자. 먼저 (미리 준비한) 이진 탐색 메서드를 사용하여 인덱스를 찾고, 그 위치에서 시작하여 위아래로 반복문을 돌려 나머지 속성 집합을 구축하는 방법으로 구현하였다. 간단하게 설명하면, 배열을 반복하지 않기 때문에 메서드 호출에 대한 비용이 줄어들어 스크롤이 빨라졌다.</p>
<hr>
<h2 id="our-totally-cool-update-animation">Our Totally Cool Update Animation</h2>
<p><img src="https://user-images.githubusercontent.com/63997044/190340832-825de873-cad6-43f0-9d09-12fc2a6f250f.gif" alt="Sep-15-2022 05-32-27"></p>
<p>이런 애니메이션
UICollectionView가 제공하는 도구인 Perform Batch Updates API를 사용할 것임.
이 API를 사용하면 애니메이션과 동시에 수행하는 일련의 업데이트를 Collection View에 전달할 수 있다.
<img width="854" alt="스크린샷 2022-09-15 오후 4 39 31" src="https://user-images.githubusercontent.com/63997044/190344712-d68ede3e-4914-4cc0-adde-1ba1d2bffaed.png"></p>
<p>PerformBatchUpdates에 대한 호출을 추가. Data Source 업데이트와 Collection View 업데이트를 모두 수행하고 있는데 이는 동기화를 깔끔하게 유지하고 불일치를 피하는 좋은 방법이다.</p>
<img width="837" alt="스크린샷 2022-09-15 오후 4 40 31" src="https://user-images.githubusercontent.com/63997044/190344890-ed10eaa1-3c5d-4f4b-8ab8-bc42577efe78.png">

<img width="1171" alt="스크린샷 2022-09-15 오후 4 40 45" src="https://user-images.githubusercontent.com/63997044/190344935-33d71848-b0f4-48bb-b170-cbd476aad781.png">

<p>에러 발생!</p>
<h3 id="performbatchupdates">performBatchUpdates()</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 23 14" src="https://user-images.githubusercontent.com/63997044/190340941-26084acf-75d4-4575-81c8-725132f2c57e.png">

<p>동시에 여러 업데이트를 진행하고 애니메이션 제공
Data Source 업데이트와 동시에 CollectionView 업데이트 진행
Data Source 업데이트의 순서는 중요하지만 CollectionView 업데이트는 순서가 중요하지 않다.</p>
<h3 id="data-source-updates-order-matters">Data Source Updates: Order Matters</h3>
<img width="1171" alt="스크린샷 2022-09-15 오후 4 23 32" src="https://user-images.githubusercontent.com/63997044/190341017-3bf82630-9590-41b2-a9a0-8d779a146028.png">

<p>3개의 요소가 있는 2개의 배열로 예시를 들 것이다. 
<img width="1159" alt="스크린샷 2022-09-15 오후 4 24 30" src="https://user-images.githubusercontent.com/63997044/190341236-ae144dce-dcf5-4abb-ac44-636e86dd532c.png"></p>
<p>우리의 직관대로라면 순서에 의해 결과가 다르게 나타날 것이다. 이는 우리가 원하는 결과가 아니다.
<img width="1170" alt="스크린샷 2022-09-15 오후 4 24 26" src="https://user-images.githubusercontent.com/63997044/190341223-734d4868-815e-41ab-8661-ef98c0151654.png"></p>
<p>그런데 코드와 같이 클로저로 업데이트 동작을 지정하고 <code>performBatchUpdates()</code>를 수행하면 결과가 동일하게 나올 것이다.</p>
<h3 id="collection-view-updates-coalescing">Collection View Updates Coalescing</h3>
<img width="1177" alt="스크린샷 2022-09-15 오후 4 24 58" src="https://user-images.githubusercontent.com/63997044/190341323-e7b405e7-464a-4753-b401-0b3460a7c91f.png">

<p>CollectionView로 전송되는 업데이트의 순서가 중요하지 않은 이유는 무엇일까?
업데이트 작업들을 각각 살펴보겠다.</p>
<ol>
<li>Delete</li>
</ol>
<ul>
<li>IndexPath 내림차순으로 처리된다.</li>
<li>항상 이전 상태의 indexPath를 참조한다.
(Data Source 업데이트 이전과 이후 상태를 각각 이전/이후 상태라고 칭함)</li>
</ul>
<ol start="2">
<li>Insert</li>
</ol>
<ul>
<li>IndexPath 오름차순으로 처리된다.</li>
<li>삽입 작업에서 참조하는 IndexPath는 항상 최종 상태 혹은 업데이트 이후 상태르 참조한다.</li>
</ul>
<ol start="3">
<li>Move</li>
</ol>
<ul>
<li>Delete와 Insert의 혼합 작업</li>
<li>From은 이전 상태, to는 이후 상태</li>
</ul>
<ol start="4">
<li>Reload</li>
</ol>
<ul>
<li>실제로 삽입과 삭제로 분리할 수 있다.</li>
<li>reload에서 지정된 IndexPath는 이전 상태를 참조한다.</li>
</ul>
<img width="1175" alt="스크린샷 2022-09-15 오후 4 25 07" src="https://user-images.githubusercontent.com/63997044/190341355-1e6c1705-f423-4f2a-8384-e9ae0a521525.png">

<p>따라서 다음과 같은 작업들의 결합이 일어나게 되면, Collection View는 충돌이 일어날 것이다.</p>
<ul>
<li>같은 위치에 이동과 삭제를 시도할 때</li>
<li>같은 위치에 이동과 삽입을 시도할 때</li>
<li>둘 이상의 위치를 동일한 위치로 이동을 시도할 때</li>
<li>유효하지 않은 IndexPath를 참조할 때</li>
</ul>
<img width="1175" alt="스크린샷 2022-09-15 오후 4 25 27" src="https://user-images.githubusercontent.com/63997044/190341425-c7e25308-fb6f-46de-bc0e-ea35a8e9101d.png">

<p>오류를 범하지 않으려면 이 네 가지 규칙을 지키면서 항상 Data Source 업데이트를 적용하고 동기화되었는지 확인해야 한다.</p>
<ul>
<li>Move와 Delete 및 Insert를 분리한다.</li>
<li>모든 삭제 작업과 삽입 작업을 두 개의 개별 목록으로 분리한다.</li>
<li>먼저 내림차순으로 삭제를 처리한 다음 오름차순으로 삽입을 적용한다.</li>
</ul>
<h3 id="what-about-reloaddata">What About reloadData()?</h3>
<img width="1175" alt="스크린샷 2022-09-15 오후 4 25 47" src="https://user-images.githubusercontent.com/63997044/190341519-7dc2ac5d-4670-4422-8ecb-e02723aa0169.png">

<ul>
<li>위의 업데이트 작업 없이 Data Source와 Collection View의 동기화 작업을 하는 메서드</li>
<li>애니메이션이 제공되지 않음.</li>
<li>&quot;The Sledgehammer&quot;</li>
<li>애니메이션이 없기 때문에 사용자에게 좋은 경험을 주지 않으므로 꼭 필요한 경우에만 사용해야 함.</li>
</ul>
<h2 id="demo코드로-살펴보기-2">Demo(코드로 살펴보기)</h2>
<img width="844" alt="스크린샷 2022-09-15 오후 4 42 52" src="https://user-images.githubusercontent.com/63997044/190345342-976c6ec1-c294-42ba-bad5-f4e0cf189ca4.png">

<p>같은 IndexPath를 사용하는 것에서 충돌이 시작함.
따라서 이를 분리하는 작업을 먼저 시작하겠다.</p>
<img width="847" alt="스크린샷 2022-09-15 오후 4 43 31" src="https://user-images.githubusercontent.com/63997044/190345456-130ac19e-5c86-42df-8749-1b1e226c851e.png">

<p>Remove와 Move, Insert를 각각 분리한다.
먼저 내림차순으로 삭제를 수행한다.
그 후 삽입을 오름차순으로 처리한다.
마지막으로 CollectionView를 업데이트하여 애니메이션을 재생한다.</p>
<img width="1169" alt="스크린샷 2022-09-15 오후 4 26 08" src="https://user-images.githubusercontent.com/63997044/190341596-67b0e999-1c27-40a4-a773-973ea3c2415c.png">]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC16] - Concurrent Programming With GCD in Swift 3]]></title>
            <link>https://velog.io/@grumpy-sw/WWDC16-Concurrent-Programming-With-GCD-in-Swift-3</link>
            <guid>https://velog.io/@grumpy-sw/WWDC16-Concurrent-Programming-With-GCD-in-Swift-3</guid>
            <pubDate>Sun, 08 Jan 2023 05:00:08 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2016 &#39;Concurrent Programming With GCD in Swift 3&#39;를 읽고 정리한 글입니다.</code></pre><h1 id="concurrent-programming-with-gcd-in-swift-3">Concurrent Programming With GCD in Swift 3</h1>
<h2 id="모든-코드를-main-thread에서-실행한다면">모든 코드를 main thread에서 실행한다면</h2>
<p><img src="https://i.imgur.com/mmF4VaQ.png" alt="">
새로운 프로젝트를 만들때 해당 응용 프로그램은 기본으로 메인 스레드를 갖게된다.
메인 스레드는 UI에 표시될 모든 코드를 실행하는 역할을 한다.
메인 스레드에 데이터 변환이나 이미지 처리와 같은 큰 작업을 하게된다면 <strong>UI에 문제가 생기게된다.</strong>
UI가 느려지거나 멈추는 상황이 발생할 수 있다.</p>
<p>이러한 문제를 방지하기 위해서는 애플리케이션에 동시성 개념을 도입해야한다.
동시성을 사용하면 애플리케이션의 여러 작업을 동시에 할수 있게 된다.
동시성을 사용하기 위해서 새로운 스레드를 생성하고 새로운 스레드에 작업을 시키게된다.</p>
<h2 id="concurrency">Concurrency</h2>
<p><img width="1208" alt="스크린샷 2022-08-25 오전 1 03 18" src="https://user-images.githubusercontent.com/63997044/186467083-07a9fd6a-06dc-43b2-bae0-bb9464dcc135.png"><br></p>
<p>하지만 동시성을 도입할 경우 스레드 안정성을 유지하기가 어려워진다.
새로 만든 스레드에서 수행하는 작업이 다른 스레드에서 수행할 작업에 영향을 끼칠수 있기 때문이다.</p>
<h2 id="gcd-동시성-라이브러리">GCD 동시성 라이브러리</h2>
<p><img src="https://i.imgur.com/mzf3wS0.png" alt="">
이러한 문제를 좀더 쉽게 관리하기위해 GCD라는 동시성 라이브러리를 만들었고
GCD는 다중 스레드 코드를 작성하는데 도움이된다.</p>
<p>GCD는 동시성을 지원하기 위해 스레드에 DispatchQueue와 RunLoop를 도입했고 이를 이용해 새로운 스레드를 만들고 그 스레드에서 실행루프를 실행시켜 작업을할수 있도록한다.</p>
<h2 id="dispatch-queue와-run-loop">dispatch queue와 run loop</h2>
<p><img src="https://i.imgur.com/9X4EeT1.png" alt=""></p>
<ul>
<li>worker</li>
</ul>
<ol>
<li>dispatch queue <code>수행할 일</code>들을 closure 형태로 넣는다.</li>
<li>disapatch queue가 thread를 하나 가져온다.</li>
<li>dispatch queue 내부의 <code>수행할 일</code>이 끝나면 thread는 알아서 해제한다.</li>
</ol>
<ul>
<li>thread</li>
</ul>
<ol>
<li>own thread를 만든다.</li>
<li>그 스레드에서 run loop가 동작한다.</li>
</ol>
<p>DispatchQueue는 작업항목을 큐에 저장할수 있도록 하는것이다.
큐에 저장된 작업을 수행하기위해 Dispatch는 적합한 스레드를 만들거나 가져와 작업을 수행하도록 한다.</p>
<p>Dispatch는 해당 스레드에 대한 모든 작업이 실행 완료되면 스레드를 자체적으로 해제시키게 된다.</p>
<p>이처럼 DispatchQueue에 작업항목을 저장하여 새로운 스레드를 생성할수 있고 새로운 스레드에서 해당 작업을 수행하기위해 런루프를 실행할수 있게 되는것이다.</p>
<p>메인 스레드는 유일하게 애플리 케이션의 메인 런루프와 메인 큐를 가질수 있다.</p>
<p><strong>DispatchQueue에 작업항목을 저장할수 있는 방법에는 2가지가 있다.</strong></p>
<ol>
<li>Asynchronous Execution. (비동기 실행)</li>
<li>Synchronous Execution. (동시 실행)</li>
</ol>
<h2 id="1-asynchronous-execution-비동기-실행">1. Asynchronous Execution. (비동기 실행)</h2>
<p><img src="https://i.imgur.com/h7cqX0s.png" alt="">
여러 작업항목을 DispatchQueue에 저장하면 해당작업을 실행하기위해 새로운 스레드를 생성하고 Dispatch는 Queue 에서 작업항목을 하나씩 가져와 실행한다.</p>
<p><img src="https://i.imgur.com/9eBzhUu.png" alt="">
대기열에 저장된 모든 작업항목이 완료되면 시스템에서 만들어진 스레드를 해제시킨다.</p>
<h2 id="2-synchronous-execution-동기-실행">2. Synchronous Execution. (동기 실행)</h2>
<p><img src="https://i.imgur.com/fWbbopY.png" alt="">
DispatchQueue에 저장한 작업이 끝날때 까지 새로만든 스레드를 기다리게 할수 있다.</p>
<p><img src="https://i.imgur.com/N0bT366.png" alt="">
DispatchQueue는 대기중인 스레드로 제어를 전달하고 해당 항목을 실행한후 DispatchQueue의 제어가 Dispatch에 의해 다시 작업자(Worker)스레드로 전달된다.</p>
<p><img src="https://i.imgur.com/OYbU950.png" alt="">
DispatchQueue에 남은 작업항목을 모두 실행하게되면 사용중이던 스레드를 헤제 시킨다.</p>
<h2 id="getting-work-off-your-main-thread">Getting Work Off Your Main Thread</h2>
<p><strong>위의 설명들은 다른 스레드에 작업을 수행시키는 방법에대한 설명이다.</strong>
하고자 했던것은 메인 스레드에서 작업하게 되면 UI에 문제가 생길수있으니 다른 스레드에 작업을 수행하도록 하는것이었고 메인스레드에서 수행한 Transform을 가져와 다른 큐에서 실행함으로써 이를 수행하게된다.</p>
<p><img src="https://i.imgur.com/WuU8u1u.png" alt="">
메인 스레드에서 데이터를 변환하던것을 다른 큐(스레드)에서 실행하도록 하면된다.</p>
<p><img src="https://i.imgur.com/hXdpP4v.png" alt="">
데이터를 변환하고 싶을때 해당 데이터의 값을 다른 큐의 변환 코드로 이동시킨다.</p>
<p><img src="https://i.imgur.com/ZTQJnR5.png" alt="">
다른 큐에서 데이터를 변환하고 변환된 데이터를 메인스레드로 보낸다.</p>
<p>이를통해 메인 스레트가 이벤트를 처리하는 동안 데이터를 변환하는 작업을 수행할 수 있게된다.</p>
<h2 id="코드는-어떨까">코드는 어떨까?</h2>
<p><img src="https://i.imgur.com/b7y6TVB.png" alt=""></p>
<ol>
<li>DispatchQueue객체를 생성하여 작업을 저장할 큐(대기열)를 생성할 수 있다.</li>
<li>DispatchQueue 객체를 생성할때 레이블이 필요하며 해당 레이블은 디버깅할때 사용될수 있다.(어떤 DispatchQueue에서 문제가 발생했는지 특정하기위함 인것 같다.)</li>
<li>DispatchQueue는 FIFO(선입선출) 순서로 작업을 실행한다. 즉 DispatchQueue에 저장된 순서대로 실행된다는 뜻이다.</li>
<li>생성된 DispatchQueue객체에 비동기 메서드를 사용하여 수행해야할 작업을 제출할 수 있다.(.async 메서드)</li>
</ol>
<p>다시 이미지의 코드를 보면 이미지의 크기를 조정하는 작업을 다른 스레드에서 수행하도록 저장했다.</p>
<h2 id="작업이-완료된-데이터를-main스레드로-가져오는-방법은-어떻게-해야할까">작업이 완료된 데이터를 Main스레드로 가져오는 방법은 어떻게 해야할까??</h2>
<p><img width="1208" alt="스크린샷 2022-08-25 오전 1 07 25" src="https://user-images.githubusercontent.com/63997044/186468161-75c20c93-419d-4c60-b3cb-dfe19b3882a5.png"><br>
DispatchQueue는 메인 스레드에서 실행해야할 작업이 무엇인지 작성할수 있다. 
DispatchQieie.main을 호출한 다음 async를 호출하고 내부에 메인스레드에서 수행항 작업을 코드로 작성해주면된다.</p>
<p>위와같이 코드를 작성하게되면 이미지의 크기를 조정하는 작업은 다른스레드에서 수행하게되고 크기가 조정된 이미지를 메인스레드에서 UI에 반영하는 작업을 하게된다.</p>
<h2 id="controlling-concurrency">Controlling Concurrency</h2>
<p><img src="https://i.imgur.com/kO2kCj5.png" alt="">
프로그램에서 동시성을 제어하는것에는 <strong>비용(cost)</strong> 이 발생한다.
Dispatch가 사용하는 스레드 풀은 기기의 모든 호출을 제어하기위해 동시성을 제한한다.</p>
<p>스레드를 차단할 때 애플리케이션의 다른 부분을 기다리거나 시스템 호출을 기다리는 경우 차단된 Worker 스레드로 인해 <strong>더많은 Worker 스레드</strong>가 생성될수 있다. -&gt; <strong>Thread Explosion</strong>의 위험이 있다.</p>
<p>이를 위해 적절한 수의 thread를 가지도록 해야한다.</p>
<h2 id="structuring-your-application애플리케이션-구조화">Structuring Your Application(애플리케이션 구조화)</h2>
<p><img src="https://i.imgur.com/htpNnxf.png" alt="">
이러한 것들을 애플리케이션에 적용하기 위해서는 독립적인 데이터 흐름으로 애플리케이션의 영역을 식별, 분류 하는것이 좋다. </p>
<p><strong>예</strong>를들어 이미지를 변환하거나 데이터베이스가 존재하는 경우 이러한 영역을 서로 다른 하위시스템으로 분할한 다음 각각 DispatchQueue를 사용하게되면 많은 DispatchQueue를 사용하지 않아도되고 많은 스레드 문제를 겪지않고 독립적으로 실행할 수 있는 DispatchQueue를 사용하도록 할 수 있다.</p>
<h2 id="chaining-vs-grouping-work">Chaining vs. Grouping Work</h2>
<p>동시성 프로그래밍을 사용하기위해 유용한 방법들에는 두가지 방법이 있다.</p>
<ol>
<li>Chaining</li>
<li>Grouping Work</li>
</ol>
<h2 id="chaining">Chaining</h2>
<p><img src="https://i.imgur.com/6sKMlvv.png" alt="">
위에서 설명하고 사용한 방식으로 하나의 작업을 수행한후 다른 스레드에서 또다른 작업을 수행, 그리고 다른 스레드에서 작업하도록 할수있도록 하는 방법이다.</p>
<h2 id="grouping">Grouping</h2>
<p><img src="https://i.imgur.com/NALFqax.png" alt="">
작업을 그룹화하고 작업이 완료되기를 기다리는것.</p>
<p>여러 개의 서로 다른 작업 항목을 생성하려는 단일 작업 항목이 있고
해당 작업 항목이 완료되었을 때만 작업을 진행하려는 경우 해당 작업을 수행할 수 있다.</p>
<h2 id="dispatchgroup">DispatchGroup</h2>
<p><img src="https://i.imgur.com/4QdRUh8.png" alt="">
이전 다이어그램으로 돌아가서 UI가 3가지 작업항목을 생성하면 3가지의 작업항목윽 DispatchGroup으로 묶을수 있고 Group에 속한 모든 작업들이 종료되기를 추적할 수 있다.</p>
<p>사용방법은 DispatchGroup 객체를 생성하면된다.</p>
<p><img src="https://i.imgur.com/Xawto43.png" alt="">
DispatchGroup을 사용하면 그룹에 작업을 추가하고 다른 큐(스레드)에서 작업을 수행할 수 있으면서도 동일한 그룹과 연관시킬수도 있다.</p>
<p>DispatchGroup에 작업을 저장 할때마다 Group에 저장된 작업 항목의 갯수만큼 카운터를 증가시킨다.</p>
<p><img src="https://i.imgur.com/jYVHFcn.png" alt="">
그룹에 저장된 마지막 작업을 수행한후 모든 작업이 완료된 시점에 작업을 수행하도록 할수있으며 notify(queue:)를 사용하여 선택한 Queue에서 어떤 작업을 할것인지 작성할수있다.</p>
<p><img src="https://i.imgur.com/UqYtALI.png" alt="">
그룹에 저장된 작업들이 실행되면 실행횟수에 따라 작업을 저장할때 증가했던 카운터가 감소하게된다.</p>
<p>위의 이미지는 그룹의 Database에 해당하는 작업이 완료된 화면이다.
3개였던 카운터가 2개로 감소한 모습을 확인할 수 있다.</p>
<h2 id="synchronizing-between-subsystems">Synchronizing Between Subsystems</h2>
<p>동기식 실행을 사용하여 하위 시스템간의 상태를 직렬화(Serial)할 수 있다.
DispatchQueue는 기본적으로 직렬(Serial)이다.
이것을 이용해 <strong>상호 배제(mutual exclusion)</strong> 속성에 사용할수있다.
Serial Queue 사용하면 해당 작업 끝날때까지 다른작업을 수행하지 않는 것을 말한다.</p>
<p><img src="https://i.imgur.com/QBI8nig.png" alt="">
이것을 이용해 <strong>스레드 세이프</strong>하도록 구축할수 있다.
(여러 스레드가 동시에 작업하는것을 방지하게 하여 여러스레드가 동시에 공유자원에 접근하는것(=<strong>레이스 컨디션</strong>)을 막을수 있다.)</p>
<p><img src="https://i.imgur.com/9xH6W3e.png" alt="">
하지만 이러한 패턴을 사용할때는 주의해야한다.
작업을 수행하지 않도록 정지시키기 때문에 각각의 스레드가 서로의 작업이 끝나기를 기다리게될경우 아무작업도 수행하지 못한채 서로 작업이 끝나기를 기다리게되며 이를 <strong>DeadLock(교착상태)</strong> 문제가 발생할수 있다.</p>
<h2 id="choosing-a-quality-of-service">Choosing a Quality of Service</h2>
<p><img src="https://i.imgur.com/pYFpadb.png" alt="">
개발자의 의도에 따라 Dispatch에 저장할 작업을 분류할 수 있다.
그리고 Dispatch는 개발자가 작성한 의도에 따라 CPU우선순위, IO스케쥴링 우선순위 로 나누어 실행된다.
여기서 우선순위란 어떤작업을 먼저해야할지가 아니라 어떤 작업에 자원을 더많이 쓸것이냐의 우선순위 이다.</p>
<h2 id="using-quality-of-service-classes">Using Quality of Service Classes</h2>
<p><img src="https://i.imgur.com/AaXSreJ.png" alt="">
Qos를 사용하려면 DispatchQueue의 비동기 메서드 .async에 선택적 매개변수(넣어도되고 안넣어도되고, 기본값이 있다는소리)로 전달할 수 있다.</p>
<p>위의 이미지처럼 .async에 Qos인자를 넣게되면 모든작업을 background, userInitiated 우선도로 작업하게된다.</p>
<p>DispatchQueue에 저장된 작업항목들을 수행하던중 우선순위가 더 높은 작업항목을 추가 하게되면 수행하던 작업항목들의 Qos우선도를 올려 우선작업하도록 한다.</p>
<h2 id="dispatchworkitem">DispatchWorkItem</h2>
<p><img src="https://i.imgur.com/knkAYH5.png" alt="">
DispatchWorkItem을 사용해 실행 방법을 <strong>다양하게</strong> 제어할수 있도록 할수 있다.
DispatchWorkItem의 인자 flags에 assignCurrentContext를 전달하면 &quot;dispatch queue에 submit하는 시점&quot; 말고 &quot;create 시점&quot;의 QoS를 사용하게 된다.</p>
<p>즉, 나중에 사용할수 있도록 작업을 저장할수 있으며 사용할때 Qos와 함께 DispatchQueue에 저장하여 사용할수 있다.</p>
<h2 id="waiting-for-work-items">Waiting for Work Items</h2>
<p><img src="https://i.imgur.com/cIXWoq9.png" alt="">
DispatchWorkItem의 유용한 기능으로 wait을 사용하여 작업완료 시점을 신호로 보낼수 있다.</p>
<p>wait을 사용하여 다른 작업을 진행하기 전에 해당 작업항목을 완료해야함을 Dispatch에 표시할 수 있다.</p>
<p>wait을 사용할경우 wait이전의 작업항목들과 이후의 작업항목이 무엇인지 알수 있기 때문에 이전의 작업항목들의 Qos를 높여 빠르게 수행하도록 할수 있지만(= <strong>priority inversion 해결</strong>을 위한 QoS의 조절) 세마포어를 사용하는 경우는 이전의 항목의 Qos를 높이지 않는다.</p>
<h2 id="swift-3-and-synchronization">Swift 3 and Synchronization</h2>
<p><img src="https://i.imgur.com/Rf1vp2q.png" alt="">
swfit 언어는 thread-safe를 지원하지 않는다.
그렇기 때문에 동기화를 해야한다.</p>
<h2 id="traditional-c-locks-in-swift">Traditional C Locks in Swift</h2>
<p><img src="https://i.imgur.com/pfa5x8H.png" alt=""></p>
<p>Darwin 모듈은 전통적인 C 방식(구조체 기반의)의 lock을 사용한다.
mutex나 하나의 lock을 사용하는 Swift에 적합하지 않다.</p>
<ul>
<li>동기화 확인하는 tool: <a href="https://wwdctogether.com/wwdc2016/412">WWDC_Thread Sanitizer and Static Analysis</a></li>
</ul>
<h2 id="correct-use-of-traditional-locks">Correct Use of Traditional Locks</h2>
<ul>
<li><p>그래도 전통적인 lock 방식 사용하고 싶다면?
<img src="https://i.imgur.com/xcgDbAZ.png" alt="">
<code>Foundation.lock</code>을 사용하라
이건 class-based!</p>
</li>
<li><p>문제 1: unfair lock이다.</p>
</li>
<li><p>문제 2: 다른 thread의 진입을 막지 못함</p>
</li>
<li><p>문제 3: spin lock 하지 않음(spin lock = busy waiting = lock 풀릴 때까지 기다리기)</p>
</li>
</ul>
<h3 id="use-gcd-for-synchronization">Use GCD for Synchronization</h3>
<p><img width="1209" alt="스크린샷 2022-08-25 오전 1 14 15" src="https://user-images.githubusercontent.com/63997044/186469581-409a4a06-4729-4b69-835e-f17b4617a67d.png"><br></p>
<p>동기화 목적으로 이제 <strong>Dispatch Queue</strong>를 사용하도록 하자.</p>
<p>DispatchQueue.sync</p>
<ul>
<li>전통적인 lock 방식보다 오용의 위험이 적다</li>
<li>디버깅 도구와 더 잘 통합되어 있다.(뇌피셜: dispatch queue 레이블로 확인 용이)</li>
</ul>
<p>간단한 구현 예시
<img width="1170" alt="스크린샷 2022-08-25 오후 7 59 19" src="https://user-images.githubusercontent.com/63997044/186647468-2c4573fc-64ff-4931-9ab9-66d89a9cfe59.png"><br></p>
<h2 id="preconditions-전제-조건">Preconditions 전제 조건</h2>
<p><img width="1208" alt="스크린샷 2022-08-25 오전 1 14 36" src="https://user-images.githubusercontent.com/63997044/186469661-54fa20c2-b3a4-40ec-9528-76307cbd824e.png"><br>
이번 릴리즈의 새로운 기능?
이 코드는 주어진 큐에서 꼭 실행되어야 한다고 조건을 걸수도 있고, 이 큐에서는 실행되면 안된다고 조건을 걸수도 있다.</p>
<h2 id="object-lifecycle-in-a-concurrent-world">Object Lifecycle in a Concurrent World</h2>
<p><img width="1205" alt="스크린샷 2022-08-25 오전 1 15 20" src="https://user-images.githubusercontent.com/63997044/186469823-65e290fb-74eb-42c4-b50f-de924389d742.png"><br></p>
<p>동기화가 필요하지 않은 값이 전달되는 방식으로 앱을 구성할 수 있으면 제일 좋음. 그러나 실제 코드에서는 서브시스템에 액세스할 일부 개체가 필요하고 개체 참조가 발생한다. 그리고 이런 참조를 제거하는 것이 실제로는 어려울 수 있다.
따라서 크래시를 피하기 위해 객체 상태는 4단계를 거쳐야 함
setup - activated - invalidated - deallocated</p>
<ol>
<li>Setup
개체를 만들고 목적에 필요한 프로퍼티를 제공한다.</li>
<li>Activated
객체를 활성화시킨다. 이 객체를 다른 서브시스템이 알 수 있게 됨. 동시성 프로그래밍 환경에서 해당 객체를 사용할 수 있게 됨</li>
<li>Invalidated
객체를 제거하고 싶다면, 비활성화를 거쳐야 한다. 앱의 모든 부분, 모든 서브시스템에서 이 객체가 사용되지 않고 사라져야 함을 확인한다.</li>
<li>Deallocation
Invalidated 단계가 끝나면 할당 해제 단계를 수행한다.</li>
</ol>
<h2 id="observer-pattern">Observer Pattern</h2>
<h3 id="의도한-ui-동작-방식">의도한 UI 동작 방식</h3>
<p><img src="https://i.imgur.com/AMHUbOR.png" alt=""></p>
<p><img src="https://i.imgur.com/JXXwuX9.png" alt=""></p>
<p><img src="https://i.imgur.com/JbKlncK.png" alt=""></p>
<p><img src="https://i.imgur.com/szCAO7c.png" alt="">
=&gt; state가 바뀌면 main queue에서 noti를 받을 수 있도록 구현한다.</p>
<p><img src="https://i.imgur.com/SmTEtz2.png" alt=""></p>
<p><img src="https://i.imgur.com/mkBy9mU.png" alt=""></p>
<h3 id="deallocation">Deallocation</h3>
<p><img src="https://i.imgur.com/QBWceMa.png" alt=""></p>
<p>deinit 내부에서 unregister 호출</p>
<ul>
<li>서브시스템을 register할 때 참조를 가져옴</li>
<li>메인 스레드에서 해제해도 하나가 남아있으므로 참조 카운트가 남아있으므로 deinit이 실행되지 않음.</li>
<li>따라서 메모리에서 해제되지 않고 남아있는 상태가 됨</li>
</ul>
<p><img src="https://i.imgur.com/X0bFkz6.png" alt="">
그렇다면 약한 참조를 사용하면 해결될까?</p>
<ul>
<li>실제 앱은 예시보다 훨씬 복잡한 구조임</li>
<li>따라서 메인 스레드에서 참조를 해제한다고 하더라도 다른 모든 객체가 참조하지 않는다는 보장을 할 수 없다!</li>
</ul>
<h3 id="invalidation-as-a-state">Invalidation as a State</h3>
<p><img width="1207" alt="스크린샷 2022-08-25 오전 1 17 10" src="https://user-images.githubusercontent.com/63997044/186470159-93d0e787-2976-4bbb-a6a6-b8e6f5e3c756.png"><br></p>
<p><strong>해결법</strong> - 세 번째 단계인 <code>Invalidation</code> 단계에서 명시적 함수 호출.
invalidate 메서드 내에서 unregister를 수행
BusyController의 상태값 flag로 서브시스템의 상태를 체크하도록 함</p>
<h2 id="gcd-object-lifecycle">GCD Object LifeCycle</h2>
<p>concurrently하게 사용되는 GCD 객체 - 정확히 동일한 패턴을 따름</p>
<h3 id="setup">Setup</h3>
<p><img width="1204" alt="스크린샷 2022-08-25 오전 1 17 29" src="https://user-images.githubusercontent.com/63997044/186470215-e6b17e3a-b6fa-45be-8e3a-c1122055849e.png"><br></p>
<p>DispatchQueue의 작업과 속성들 
DispatchSource의 속성들과 타겟 큐, 이벤트 핸들러 등등
(label, attributes, queue 등)</p>
<h3 id="activation">Activation</h3>
<p><img width="1205" alt="스크린샷 2022-08-25 오전 1 17 48" src="https://user-images.githubusercontent.com/63997044/186470274-417439ec-ae2a-48df-9297-289487f02c8a.png"><br></p>
<p>활성화가 되면 더 이상 개체의 속성이 변경되지 않아야 한다.</p>
<h3 id="cancellation">Cancellation</h3>
<p><img width="1205" alt="스크린샷 2022-08-25 오전 1 18 05" src="https://user-images.githubusercontent.com/63997044/186470333-a60c599a-f452-438c-bd41-0dfa89ffe285.png"><br></p>
<p>취소하면 항목에 대한 이벤트 모니터링이 중지된다.
또한 취소 핸들러가 실행된다.
그리고 소스에 대한 핸들러가 할당 해제된다.</p>
<h3 id="deallocation-1">Deallocation</h3>
<p><img width="1205" alt="스크린샷 2022-08-25 오전 1 18 19" src="https://user-images.githubusercontent.com/63997044/186470380-eb6e950f-1886-4a91-967e-269dd565a057.png"><br>
할당 해제될 때 객체는 활성화(Activate)되어있어야하고, 종료된 상태가 아니어야 한다(Not Suspended). 일시중단되거나 비활성화된 것은 개체를 제거하는 코드를 실행하기에 안전하지 않은 상태라고 판단한다.</p>
<h2 id="참고한-문서및-자료">참고한 문서및 자료</h2>
<p><a href="https://developer.apple.com/videos/play/wwdc2016/720/">wwdc Concurrent Programming With GCD in Swift 3</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정렬 알고리즘]]></title>
            <link>https://velog.io/@grumpy-sw/%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@grumpy-sw/%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Fri, 09 Dec 2022 05:35:24 GMT</pubDate>
            <description><![CDATA[<h3 id="1-버블-정렬">1. 버블 정렬</h3>
<pre><code class="language-swift">func bubbleSort(_ numbers: [Int]) -&gt; [Int] {
    var numbers = numbers
    for i in stride(from: numbers.count -  1, through: 0, by: -1) {
        for j in stride(from: 0, to: i, by: 1) {
            if numbers[j] &gt; numbers[j+1] {
                numbers.swapAt(j, j+1)
            }
        }
    }
    return numbers
}</code></pre>
<p>서로 인접한 두 원소를 검사하여 자리를 바꾸어 정렬하는 방법이다.
시간복잡도는 O(n²)이다.</p>
<h3 id="2-선택-정렬">2. 선택 정렬</h3>
<pre><code class="language-swift">func selectionSort(_ numbers: [Int]) -&gt; [Int] {
    var numbers = numbers
    for i in stride(from: 0, to: numbers.count - 1, by: 1) {
        var minIdx = i
        for j in stride(from: i+1, to: numbers.count, by: 1) {
            if numbers[minIdx] &gt; numbers[j] {
                minIdx = j
            }
        }
        numbers.swapAt(i, minIdx)
    }
    return numbers
}</code></pre>
<p>정해진 위치에 알맞은 원소를 넣는 알고리즘이다.
시간복잡도는 O(n²)이다.</p>
<h3 id="3-삽입-정렬">3. 삽입 정렬</h3>
<pre><code class="language-swift">func insertionSort(_ numbers: [Int]) -&gt; [Int] {
    var numbers = numbers

    for i in 1..&lt;numbers.count {
        for j in 0..&lt;i {
            if numbers[i] &lt; numbers[j] {
                let temp = numbers[i]
                numbers.remove(at: i)
                numbers.insert(temp, at: j)
                break
            }
        }
    }
    return numbers
}</code></pre>
<p>정해진 원소를 알맞은 위치에 넣는 알고리즘이다.
시간복잡도는 O(n²)이다.</p>
<h3 id="4-퀵-정렬">4. 퀵 정렬</h3>
<pre><code class="language-swift">func quickSort(_ numbers: [Int]) -&gt; [Int] {
    if numbers.count &lt;= 1 {
        return numbers
    }
    let pivot = numbers[(numbers.count) / 2]
    let left = numbers.filter { $0 &lt; pivot }
    let right = numbers.filter { $0 &gt; pivot }
    return quickSort(left) + [pivot] + quickSort(right)
}</code></pre>
<p>하나의 pivot(기준)을 고르고, pivot보다 작은 원소들을 왼쪽으로, pivot보다 큰 원소들을 오른쪽으로 나눠 비균등한 크기의 두 리스트로 분할한다. 분할된 부분 리스트를 정렬한 다음 전체를 합쳐 정렬된 리스트를 얻는 방법이다.
(여기에서는 pivot을 가운데 지점으로 잡았지만 어느 위치든 가능하다.)
시간복잡도는 O(nlog₂n)이다.</p>
<h3 id="5-합병-정렬">5. 합병 정렬</h3>
<pre><code class="language-swift">func mergeSort(_ numbers: [Int]) -&gt; [Int] {
    if numbers.count &lt;= 1 {
        return numbers
    }

    let left = Array(numbers[0..&lt;numbers.count / 2])
    let right = Array(numbers[(numbers.count / 2)..&lt;numbers.count])

    func merge(_ left: [Int], _ right: [Int]) -&gt; [Int] {
        var left = left
        var right = right
        var result: [Int] = []

        while !left.isEmpty &amp;&amp; !right.isEmpty {
            if left.first! &lt; right.first! {
                result.append(left.removeFirst())
            } else {
                result.append(right.removeFirst())
            }
        }

        if !left.isEmpty {
            result.append(contentsOf: left)
        }

        if !right.isEmpty {
            result.append(contentsOf: right)
        }
        return result
    }
    return merge(mergeSort(left), mergeSort(right))
}</code></pre>
<p>하나의 리스트를 균등한 크기의 두 리스트로 분할하고 분할된 리스트를 정렬한 다음 정렬된 리스트를 합하여 전체가 정렬된 리스트가 되도록 만드는 방법이다.
합병 정렬은 분할 정복을 통해 이루어진다.
시간복잡도는 O(nlog₂n)이다.</p>
<h3 id="6-힙-정렬">6. 힙 정렬</h3>
<pre><code class="language-swift">func heapSort(_ numbers: [Int]) -&gt; [Int] {
    var numbers = numbers

    func isLeaf(_ index: Int, _ maxIdx: Int) -&gt; Bool {
        return maxIdx &lt; 2 * index + 1
    }

    func makeSubHeap(_ root: Int, _ maxIdx: Int, _ numbers: inout [Int]) {

        var children = [numbers[2 * root + 1]]
        if maxIdx &gt; 2 * root + 1 {
            children.append(numbers[2 * root + 2])
        }

        var bigIdx = 2 * root + 1

        if children.first! &lt; children.last! {
            bigIdx += 1
        }

        if numbers[root] &lt; numbers[bigIdx] {
            numbers.swapAt(root, bigIdx)
        }
    }

    func makeHeap(_ maxIdx: Int) {
        for i in stride(from: maxIdx, through: 0, by: -1) {
            if isLeaf(i, maxIdx) {
                continue
            }
            makeSubHeap(i, maxIdx, &amp;numbers)
        }
    }
    makeHeap(numbers.count - 1)

    for i in stride(from: numbers.count - 1, to: 0, by: -1) {
        numbers.swapAt(0, i)
        makeHeap(i-1)
    }

    return numbers
}</code></pre>
<p>최대 힙(내림차순)/최소 힙(오름차순) 트리를 구성해 정렬한다.
먼저 리스트를 완전 이진 트리 형태로 구성한다. 그 후 root에 있는 요소를 꺼내서 리스트의 맨 뒤로 이동시키고, 나머지로 다시 트리를 구성하여 마지막 요소가 남을 때까지 반복한다. 
시간복잡도는 O(nlog₂n)이다.</p>
<h3 id="정렬-알고리즘-비교">정렬 알고리즘 비교</h3>
<p>(<a href="https://gmlwjd9405.github.io/2018/05/06/algorithm-bubble-sort.html">https://gmlwjd9405.github.io/2018/05/06/algorithm-bubble-sort.html</a>)</p>
<table>
<thead>
<tr>
<th align="center">알고리즘</th>
<th align="center">Best</th>
<th align="center">Worst</th>
<th align="center">Avg</th>
</tr>
</thead>
<tbody><tr>
<td align="center">버블 정렬</td>
<td align="center">n²</td>
<td align="center">n²</td>
<td align="center">n²</td>
</tr>
<tr>
<td align="center">선택 정렬</td>
<td align="center">n²</td>
<td align="center">n²</td>
<td align="center">n²</td>
</tr>
<tr>
<td align="center">삽입 정렬</td>
<td align="center">n</td>
<td align="center">n²</td>
<td align="center">n²</td>
</tr>
<tr>
<td align="center">퀵 정렬</td>
<td align="center">nlog₂n</td>
<td align="center">n²</td>
<td align="center">nlog₂n</td>
</tr>
<tr>
<td align="center">합병 정렬</td>
<td align="center">nlog₂n</td>
<td align="center">nlog₂n</td>
<td align="center">nlog₂n</td>
</tr>
<tr>
<td align="center">힙 정렬</td>
<td align="center">nlog₂n</td>
<td align="center">nlog₂n</td>
<td align="center">nlog₂n</td>
</tr>
</tbody></table>
<ul>
<li>단순(구현 간단)하지만 비효율적인 방법<ul>
<li>삽입 정렬, 선택 정렬, 버블 정렬</li>
</ul>
</li>
<li>복잡하지만 효율적인 방법<ul>
<li>퀵 정렬, 힙 정렬, 합병 정렬</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Model-View-Controller (3)]]></title>
            <link>https://velog.io/@grumpy-sw/Model-View-Controller-3</link>
            <guid>https://velog.io/@grumpy-sw/Model-View-Controller-3</guid>
            <pubDate>Wed, 30 Nov 2022 12:15:07 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 애플 공식문서를 통해 MVC 대해 정리합니다.</code></pre><h2 id="mvc-as-a-compound-design-pattern">MVC as a Compound Design Pattern</h2>
<p>Model-View-Controller 패턴은 몇 가지 기본적인 디자인 패턴들로 구성된 패턴이다. 전통적인 형태의 MVC패턴은 Cocoa에서 사용하는 것과 Controller 그리고 View에 부여된 역할에서 약간의 차이가 있다.</p>
<p>스몰 토크 개념에서 MVC는 <em>Composite</em>, <em>Strategy</em>, <em>Observer</em> 패턴으로 구성되었다. 각 디자인 콘셉트가 어떻게 결합되었는지 알아보자.</p>
<p><strong># Composite</strong></p>
<ul>
<li>애플리케이션에서 View 객체는 (View의 계층 구조처럼) 구조화된 방식으로 함께 작동하는 View들이 중첩된 Composite 형태를 띤다.</li>
<li>여러 요소들이 합쳐진 TableView, UILabel을 포함하고 있는 UIButton, 여러 요소들을 Subview들로 갖고 있는 View들이 그 예시이다.</li>
<li>User input 및 display는 Composite 구조의 모든 단계에서 발생할 수 있다.</li>
</ul>
<p><strong># Strategy</strong></p>
<ul>
<li>Controller 객체는 View 객체에 대한 전략을 구현한다. View 객체는 시각적인 면을 맡는 것으로 제한되며 인터페이스 동작과 같은 애플리케이션에서의 모든 결정을 컨트롤러에게 위임한다.</li>
</ul>
<p><strong># Observer</strong></p>
<ul>
<li>Model 객체는 관찰 관계에 있는 객체(일반적으로 View를 가리킴)에게 변경 사항에 대해 알려준다.</li>
</ul>
<p>복합 패턴으로서의 전통적인 MVC 패턴을 도식화하면 다음과 같다.</p>
<p><img src="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Art/traditional_mvc.gif" alt=""></p>
<p>Cocoa 애플리케이션에서의 MVC는 한 가지 개념이 추가되었는데 그것은 <em>View 객체와 Model 객체는 &quot;*</em>재사용**&quot;이 가능해야 한다*는 것이다.</p>
<p>먼저 View부터 보면, View는 애플리케이션의 &quot;look and feel&quot;을 결정하기 때문에 모양과 동작의 일관성이 필수적으로 요구되며 이를 위해서는 재사용성이 높은 객체가 필요하다.
Model은 정의에 따라 특정한 문제의 도메인과 관련된 데이터에 대한 작업을 수행하도록 설계되어야 한다. 동일한 문제 도메인에서 이를 재사용할 수 있도록 구성하는 것이 좋다.</p>
<p>또한 디자인 측면에서도 더 나은 재사용성을 위해 Model과 View를 서로 분리하여 유지하는 것이 유리하다.</p>
<p><img src="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Art/cocoa_mvc.gif" alt=""></p>
<p>복합 패턴으로서의 Cocoa MVC 패턴.</p>
<p>Controller 객체는 Mediator 패턴과 Strategy 패턴을 통합하여 사용한다. 가장 중요한 차이점으로, Model과 View 사이의 데이터 흐름을 양방향으로 조정하며 Model의 상태 변경사항은 Controller를 통해 View 객체에 전달된다.
View는 Command 패턴의 target-action 매커니즘의 구현을 사용한다.</p>
<p>또한 Cocoa MVC 패턴은 위의 이론적인 이유 외에도 보다 실용적인데, 다음 그림을 보자.</p>
<p><img src="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Art/cocoa_mvc_coord.gif" alt=""></p>
<p>Coordinating Controller는 Nib file로 묶인 Mediating Controller를 소유하고 있다.</p>
<p>Mediating Controller는 Mediator 패턴을 구현하는 것 외에도 애플리케이션의 많은 기능들을 구현해야 한다. 그러나 Model 객체에서 상태 변경사항을 보낼 때 Notification을 사용한다면, 그 알림에 대한 등록 및 처리를 하기 위해 커스텀한 View 클래스를 생성해야 한다.</p>
<p>Cocoa MVC 패턴은 Mediating Controller와 View를 Nib file로 묶어 이러한 Data flow를 실용적으로 관리할 수 있다.</p>
<blockquote>
<p>이 부분을 정리하면 다음과 같다.</p>
<ol>
<li>Controller를 Coordinating과 Mediating으로 분리한다. </li>
<li>각각의 모든 View에다 Notification 등록을 하는 것은 일일이 custom View를 만들어야하고 불필요한 작업의 반복이 일어나므로 Controller에서 Notification을 등록하고, Controller가 중재자로서 이를 View에다 전달한다. </li>
<li>따라서 그림과 같은 형태가 됨.</li>
</ol>
</blockquote>
<h2 id="design-guidelines-for-mvc-applications">Design Guidelines for MVC Applications</h2>
<p>애플에서는 애플리케이션 설계 시 odel-View-Controller에 대한 몇 가지 지침을 제공한다.</p>
<ul>
<li>Controller를 만들 때 이미 정의되어 있는 NSController의 서브 클래스들을 상속받아 사용하라. 굳이 NSObject의 서브 클래스 인스턴스를 사용하여 만들 필요가 없다.</li>
<li>Model, View, Controller의 역할을 분리하라. 셋 중 하나의 객체가 다른 객체의 역할을 포함하도록 구현할 수도 있지만, 역할 분리를 유지하는 것이 재사용성과 확장성을 향상시킨다.</li>
<li>MVC 애플리케이션의 목적은 재사용 가능한 개체를 최대한 많이 사용하는 것이다. View와 Model은 재사용성이 높아야 한다.</li>
<li>View가 Model을 직접 관찰하여 상태 변화를 감지할 수도 있지만 항상 Controller를 거치게 만드는 것이 좋다. 그 이유는 다음 두 가지가 있다.<ul>
<li>View가 Model을 직접 관찰하는 binding 메커니즘을 사용하는 경우 Controller가 있기에 얻는 모든 이점들을 잃게 된다.</li>
<li>binding 메커니즘을 사용하지 않는 경우 View들을 서브클래싱 하여 Model의 상태 변화 알림을 관찰하도록 한다.</li>
</ul>
</li>
<li>애플리케이션의 클래스들의 코드 종속성을 최대한 제한하라. 다른 클래스에 대한 종속성이 클수록 재사용 가능성이 줄어들기 때문이다.</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html#//apple_ref/doc/uid/TP40010810-CH14">Model-View-Controller: Concepts in Objective-C Programming</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Model-View-Controller (2)]]></title>
            <link>https://velog.io/@grumpy-sw/Model-View-Controller-2</link>
            <guid>https://velog.io/@grumpy-sw/Model-View-Controller-2</guid>
            <pubDate>Tue, 29 Nov 2022 12:03:40 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 애플이 제공하는 공식문서를 통해 MVC 대해 정리합니다.</code></pre><h2 id="역할-결합과-view-controller">역할 결합과 View Controller</h2>
<p>MVC에서 둘 이상을 병합하여 요구되는 역할을 모두 수행하도록 만들 수 있다. 예를 들어 View Controller는 View와 Controller의 결합으로 View와 Controller의 역할을 모두 수행한다. 마찬가지로 Model Controller는 Model과 Controller의 결합으로 Model과 Controller의 역할을 모두 담당한다.</p>
<h3 id="view-controller">View Controller</h3>
<p>View Controller는 View계층과 관련된 Controller로, 인터페이스 즉 View를 소유한다. View Controller의 주요 책임은 View를 관리하고 화면에 표시되는 View의 동기화 작업을 하는 것 등의 작업 메서드들은 이곳에서 구현된다. 또한 Model과 통신을 담당한다.</p>
<h3 id="model-controller">Model Controller</h3>
<p>Model Controller는 Model계층과 관련된 Controller로, Model 객체를 소유하여 Model을 관리하고 View와 통신하는 역할을 맡는다. Model에 적용되는 작업 메서드의 경우 Controller에서 구현된다.</p>
<h2 id="cocoa-controller-객체의-타입">Cocoa Controller 객체의 타입</h2>
<p>Cocoa에서는 두 가지 종류의 Controller 객체가 있다. 바로 <strong>Mediating Controller</strong>와 <strong>Coordinating Controller</strong>이다. 둘은 각각 다른 클래스들과 연결되며 다른 범위의 동작을 제공한다.</p>
<h3 id="1-coordinating-controller">(1) Coordinating Controller</h3>
<p><code>NSWindowController</code> 혹은 <code>NSDocumentController</code>의 서브클래스로, 혹은 직접 구현한 <code>NSObject</code>의 서브 클래스로, 애플리케이션에서 일부 기능을 감독하거나 조정하는 역할을 한다. Coordinating Controller가 제공하는 기능은 다음과 같다.</p>
<ul>
<li>Delegation 메시지 응답 및 Notification 관찰</li>
<li>메시지에 대한 응답</li>
<li>소유한 객체의 Life Cycle 관리(예: 적절한 시간에 해제)</li>
<li>객체 간 연결 설정 및 기타 설정 작업 수행</li>
</ul>
<h3 id="2-mediating-controller">(2) Mediating Controller</h3>
<p>Mediating Controller는 일반적으로 <code>NSController</code>에서 상속되는 객체로, View와 Model 사이의 데이터 흐름을 중재하는 역할을 한다. 이는 macOS 전용으로, iOS에서는 <strong>View Controller</strong>가 대신한다.</p>
<h2 id="view-controller-1">View Controller</h2>
<p>View Controller는 앱 내부 구조의 기초로, 사용자 인터페이스(View)의 일부와 View와 데이터 간의 상호 작용을 관리한다. View Controller의 역할은 크게 네 가지라고 볼 수 있다.</p>
<ul>
<li>Label의 Text와 같이 View를 구성하는 데이터의 변경에 대하여 View를 업데이트한다.</li>
<li>View와 사용자의 상호 작용에 응답한다.</li>
<li>View의 크기 조정을 포함한 전반적인 인터페이스 레이아웃을 관리한다.</li>
<li>다른 View Controller를 포함하여 앱 내에서 다른 객체와의 조정(Coordinating)을 담당한다.</li>
</ul>
<p>View Controller를 역할로 구분하자면 크게 두 종류로 구분할 수 있다. </p>
<h3 id="content-view-controller">Content View Controller</h3>
<p>Content View Controller는 <strong>화면에 데이터를 표시하기 위한 View Controller</strong>의 역할을 한다. 사용자가 주로 만드는 View를 관리하기 위한 View Controller가 여기에 포함되며 모든 앱은 하나 이상의 커스텀 View Controller를 포함하고 있다.</p>
<h3 id="container-view-controller">Container View Controller</h3>
<p>다음은 여러 요소들을 단일 인터페이스로 결합하는 Container View Controller이다. <strong>자신이 소유한 요소들의 콘텐츠 표시들을 관리</strong>하며 Container View Controller의 예로는 <code>UINavigationController</code>, <code>UITabBarController</code>, <code>UISplitViewController</code>가 있다.</p>
<h3 id="view-controller가-하는-일">View Controller가 하는 일</h3>
<h4 id="1-view-management">1. View Management</h4>
<p><img src="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/VCPG_ControllerHierarchy_fig_1-1_2x.png" alt=""></p>
<p>View Controller는 View를 관리하고 있으므로 View와 Model간의 상호작용에서 정확하게 이어주기 위해 View의 계층 구조를 관리하는 것이 중요하다. View Controller에는 모든 컨텐츠를 포함하는 root View가 있으며 이 root View에 필요한 View들을 추가한다. View Controller와 root View, 그리고 각 View와 하위 View들 사이에는 강한 참조로 연결되어 있다.</p>
<p><img src="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/VCPG_ContainerViewController_fig_1-2_2x.png" alt=""></p>
<p>Content View Controller는 모든 View들을 관리한다. Container View Controller는 자체 View와 하나 이상의 자식 View Controller의 root View를 관리한다. Container View Controller는 자식 컨텐츠를 관리하지 않는다.
위의 그림을 보면 알 수 있는데 Container View Controller에 해당하는 UISplitViewController는 root View를 관리하여 전체 크기와 위치를 관리하고, 실제 콘텐츠인 A와 B는 하위 View Controller들을 통해 관리된다.</p>
<h4 id="2-data-marshaling">2. Data Marshaling</h4>
<p>View Controller는 관리하는 View와 애플리케이션의 데이터(Model) 사이의 중개자 역할을 한다. UIViewController 클래스의 메서드와 프로퍼티를 사용하면 애플리케이션의 presentation을 관리할 수 있다.</p>
<p>View Controller를 구현하면서 데이터를 관리하는 데 필요한 프로퍼티를 추가하여 다음 그림과 같이 View Controller는 데이터에 대한 참조와 데이터를 표시하기 위한 View에 대한 참조 모두를 가질 수 있다.</p>
<p><img src="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/VCPG_CustomSubclasses_fig_1-3_2x.png" alt=""></p>
<p>주의할 점은, View Controller와 데이터 사이에서 항상 책임을 명확하게 분리해야 한다. 데이터 구조의 무결성을 보장하기 위한 대부분의 논리는 데이터 자체에 속해야 한다. View Controller가 모든 로직을 갖고 View에서 오는 입력의 유효성을 검사한 다음 해당 입력을 데이터에 필요한 형식으로 변환까지 처리할 수 있지만 실제 데이터를 관리하는 부분에 대한 View Controller의 역할을 최소화해야 한다.</p>
<h4 id="3-user-interactions">3. User Interactions</h4>
<p>View Controller는 <code>Responder Chain</code>에서 내려오는 이벤트를 수신하고 처리할 수 있는 Responder이다. 그러나 일반적으로 터치와 같은 이벤트를 View Controller가 직접 처리하는 경우는 드물다. 보통 View가 자체적으로 이벤트를 처리하고 View Controller를 <code>Delegation</code>으로 연결하여 결과를 <code>Delegate Method</code> 또는 <code>Action Method</code>에 전달한다. 따라서 View Controller에서는 이 메서드에 의해 이벤트가 처리된다.</p>
<h4 id="4-resource-management">4. Resource Management</h4>
<p>View Controller는 View를 포함하여 모든 하위 객체들에 대한 책임을 진다. 즉 객체들의 생명 주기를 관리하며 더 이상 필요하지 않은 경우 자원 확보를 위해 해제할 수도 있다. View Controller는 이러한 것들을 모두 자동으로 수행한다.</p>
<p>사용 가능한 여유 메모리가 부족할 경우, UIKit은 더 이상 필요하지 않은 리소스를 해제하도록 앱에 요청한다. View Controller는 <code>didReceiveMemoryWarning()</code> 메서드를 호출하여 더 이상 필요하지 않거나 또는 나중에 쉽게 다시 만들 수 있는 객체에 대한 참조를 제거하여 메모리를 확보한다.</p>
<h4 id="5-adaptivity적응성">5. Adaptivity(적응성)</h4>
<p>View Controller는 View를 표시하고 조정하는 역할을 한다. 모든 iOS 앱은 iPad 및 다양한 화면 크기의 iPhone에서 실행될 수 있어야 한다. 당연하게도 각 장치에 따른 View Controller를 만드는 대신 View를 조정하는 단일 View Controller를 사용하는 것이 더 낫다.</p>
<p>View Controller는 Device의 상태가 가로 혹은 세로로 변경될 때마다 표시 가능한 영역의 크기가 달라지므로 View의 배치 방법을 변경시킬 수 있다. 허용된 크기 내에서, <code>Auto Layout</code>을 사용하여 UIKit이 자동으로 View의 크기와 위치를 조정하여 적절하게 맞추거나 혹은 추가적인 조정을 할 수 있다.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html#//apple_ref/doc/uid/TP40010810-CH14">Model-View-Controller: Concepts in Objective-C Programming</a></li>
<li><a href="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/index.html">The Role of View Controllers</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Model-View-Controller (1)]]></title>
            <link>https://velog.io/@grumpy-sw/Model-View-Controller-1</link>
            <guid>https://velog.io/@grumpy-sw/Model-View-Controller-1</guid>
            <pubDate>Mon, 28 Nov 2022 09:48:20 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 애플 공식문서를 통해 MVC 대해 정리합니다.</code></pre><p>MVC(Model-View-Controller) 디자인 패턴은 모델, 뷰, 컨트롤러 세 가지 역할들을 각각 객체에 할당한다. 모델, 뷰, 컨트롤러 각각의 객체는 추상적 경계에 의해 다른 객체와 분리되며 이러한 경계를 넘나들며 다른 객체와 통신한다. 애플리케이션에서 특정 MVC 타입의 객체 모음을 계층(layer)이라고도 한다.</p>
<p>이 패턴을 채택하면 다음과 같은 이점이 있다. </p>
<ul>
<li>MVC 패턴으로 디자인된 애플리케이션에서의 개체는 재사용 가능성이 높다.</li>
<li>애플리케이션의 인터페이스가 더 명확하게 정의된다. </li>
<li>MVC 디자인을 가진 애플리케이션은 그렇지 않은 애플리케이션보다 확장성이 좋다. </li>
<li>많은 Cocoa 기술과 아키텍처는 MVC를 기반으로 하며 사용자 지정 개체가 MVC 역할 중 하나를 수행하도록 요구한다.</li>
</ul>
<p><img src="https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Art/model_view_controller_2x.png" alt=""></p>
<h2 id="model">Model</h2>
<p>Model은 애플리케이션에서 데이터를 담당한다. 필요한 데이터를 불러오고 해당 데이터를 조작하고 처리하는 논리 및 계산을 담당한다. 애플리케이션의 대부분의 데이터는 데이터가 애플리케이션에 로드된 후 Model 객체에 상주해야 한다. 이러한 캡슐화를 통해 Model 밖의 영역(View, Controller)에서는 Model 객체와의 상호작용을 통해 데이터를 사용하게 된다.</p>
<p>Model은 데이터를 조작하고 처리하는 특정한 역할을 담당하므로 유사한 영역에서 재사용할 수 있다. 이를 통해 얻는 장점 중 하나는 테스트의 용이함으로, 데이터와 관련된 로직을 Model로 분리하였기 때문에 실제 Model과 Mock을 간단하게 교체하여 테스트를 진행하기 수월하다.</p>
<p>이상적으로 User-Interface 및 Presentation과 전혀 관련이 없어야 한다. Model은 데이터를 표시하고 사용자가 해당 데이터를 편집할 수 있도록 하는 View와 직접적으로 연결되지 않는 것이 좋다. Model은 애플리케이션에서 데이터 사용할 수 있도록 이를 적절한 형태로 저장하는 것이 중요하지 UI적으로 어떻게 보여질 것인지에 대해서는 신경쓰지 않는 것이 좋다. 이것은 View가 전적으로 해야 할 일이다.</p>
<h2 id="view">View</h2>
<p>View는 사용자에게 보여지는 요소들을 가리킨다. View의 역할 중 하나는 사용자에게 Model의 데이터를 보여주는 것으로 데이터를 화면에서 어떻게 구성할 것인지 설명하는 코드들이 포함되어 있다.</p>
<p>당연하게도 사용자의 작업에 응답하는 것 또한 View의 역할로 사용자에게 Model 객체의 데이터를 보여주고 이를 편집하는 기능을 제공한다. 그럼에도 불구하고 MVC 애플리케이션에서 View 객체는 Model과 분리되어야 한다.</p>
<p>일반적으로 View에 대해서 항상 같이 오는 말이 재사용과 재구성이다. 이런 기능을 통해 애플리케이션에 대한 일관성을 제공하기도 한다.</p>
<h2 id="controller">Controller</h2>
<p>Controller 객체는 View와 Model 사이에서 중개자 역할을 한다. Model의 변경에 대해서 View는 Controller를 통해 알게 되고, 변경된 값을 출력하고 색상을 변경하는 등 이에 맞는 작업들을 수행한다. 반대의 경우로 View로부터 사용자의 작업이 입력된 경우, Controller는 Model에게 이를 알리고 Model의 내부 로직을 통해 변화가 일어나면 Controller는 다시 View에게 알려 화면을 갱신한다.</p>
<p>Controller는 애플리케이션에 대한 설정 및 조정 작업을 수행하고, 다른 객체들의 수명 주기를 관리하기도 한다.</p>
<h3 id="communication">Communication:</h3>
<p>데이터를 생성하거나 수정하는 뷰 계층에서의 사용자 작업은 컨트롤러 객체를 통해 전달되며 모델 객체의 생성 또는 업데이트로 이어진다. 
(사용자 작업 -&gt; View -&gt; Controller -&gt; Model)</p>
<p>네트워크 통신을 통해 새로운 데이터가 수신된다던지 모델 객체가 변경되면, 해당 뷰 객체를 업데이트하는 컨트롤러 객체에 모델 객체의 변화를 알린다.
(데이터의 변화 -&gt; Model -&gt; Controller -&gt; View)</p>
<p><img src="https://miro.medium.com/max/1170/1*RPOEHrwqQBrhz6H4xkWApA.png" alt=""></p>
<p>두 계층이 완전히 분리되어 있다고 가정했을 때, Model과 View 간의 소통은 모두 Controller를 통해서 일어난다. Controller의 경우 Model과 View에 어렵지 않게 접근할 수 있다. 그러나 반대로 Model과 View에서 Controller로 데이터를 전달해야 하는 경우에는 어떨까. </p>
<p><img src="https://miro.medium.com/max/1400/0*Kw7xAPCMlcmM3cOK.png" alt=""></p>
<p>우선 View -&gt; Controller를 살펴보면, 사용자 작업(버튼 터치, 텍스트 변경 등)을 전달하는 상황일 것이다. 사용자 작업을 Controller에 인지시키기 위해 흔히 <strong>Delegation</strong> 방식을 사용한다. 동작 원리는 View의 대리자를 Controller로 설정해놓고, View에서 어떤 이벤트가 발생하면 그 이벤트의 처리를 Controller에게 위임하는 방식이다.</p>
<p>Model -&gt; Controller도 알아보면, Model 영역의 데이터가 변화했다는 사실을 전달하는 내용일 것이다. iOS는 <strong>KVO</strong>(Key-Value-Observing)와 <strong>Notification</strong>을 사용한다. 해당 프로퍼티의 변화를 감지하거나, 특정 name의 Notification을 수신하고 이를 통해 최신의 Model 데이터를 가져올 수 있다.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html#//apple_ref/doc/uid/TP40008195-CH32-SW1">Model-View-Controller: Apple Documents</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC2015] - Building Responsive and Efficient Apps with GCD]]></title>
            <link>https://velog.io/@grumpy-sw/Building-Responsive-and-Efficient-Apps-with-GCD</link>
            <guid>https://velog.io/@grumpy-sw/Building-Responsive-and-Efficient-Apps-with-GCD</guid>
            <pubDate>Fri, 25 Nov 2022 13:44:45 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2015 &#39;Building Responsive and Efficient Apps with GCD&#39;를 읽고 정리한 글입니다.</code></pre><h1 id="building-responsive-and-efficient-apps-with-gcd">Building Responsive and Efficient Apps with GCD</h1>
 <img width="100%" alt="스크린샷 2022-08-17 오후 1 32 16" src="https://user-images.githubusercontent.com/63997044/185035204-99119bdf-6f8e-4a11-a98d-0d53daf8c35d.png">

<h3 id="gcdgrand-central-dispatch의-도입">GCD(Grand Central Dispatch)의 도입</h3>
<ul>
<li><p>OS X Snow Leopard에서 GCD가 도입되었다.</p>
</li>
<li><p>멀티코어 맥북에서 코어를 모두 활용하여 하나의 애플리케이션에서 서로 다른 부분을 동시에 실행하고, 스레딩을 쉽게 해준다.</p>
</li>
<li><p>더 나아가서 GCD는 앱이 효율적으로 실행하게끔 활용할 수 있다.</p>
<ul>
<li>멀티태스킹 기능을 갖춘 iOS 9 이상의 경우 앱이 말 그대로 다른 앱과 나란히 실행되어야 하는데, GCD는 시스템에 현재 어떤 종류의 작업을 수행하고 있는지 알려주고 두 앱 사이에 리소스 공유를 원활하게 해준다.</li>
</ul>
</li>
<li><p>또한 Watch OS처럼 크기가 작은 Device에서 실행해야 하는 코드 부분을 시스템이 알도록 도와준다.</p>
<h2 id="quality-of-service-introduction">Quality of Service Introduction</h2>
<h3 id="handling-events">Handling Events</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 33 05" src="https://user-images.githubusercontent.com/63997044/185035297-728eefa5-dc15-4afd-a93c-5b04f0e47533.png"></li>
<li><p>앱 실행 (기본적인 상황)</p>
<ul>
<li>앱을 실행하는 과정을 단계적으로 살펴보겠다. </li>
<li>앱이 실행되면 메인 스레드를 갖고 Run Loop를 불러온다. </li>
<li>이제 이 스레드는 이벤트를 기다린다. Delegate Method 호출이 일어나면 데이터베이스에서 데이터를 읽고 다시 돌아온다. </li>
<li>이 데이터를 가지고 UI 업데이트를 하고, 끝나면 프레임워크에 컨트롤을 반환하고 다시 이벤트를 기다리는 상태가 된다.</li>
</ul>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 33 26" src="https://user-images.githubusercontent.com/63997044/185035357-ff835ea0-5960-42ba-a2c5-52fde56aa635.png"></li>
<li><p>그러나 데이터베이스에서 값을 읽어오는 것은 다소 시간이 걸리는 작업이고 그림처럼 회전중인 커서를 보게 될 것이다.</p>
</li>
<li><p>이것은 좋지 못한 UX이다. </p>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 34 07" src="https://user-images.githubusercontent.com/63997044/185035421-0cf7ff5b-0ec7-4008-9df0-c5bee3b77bd2.png"></li>
<li><p>앱 실행(비동기적인 처리가 이루어지는 상황)</p>
<ul>
<li>GCD가 개입하여 이를 해결할 수 있다. </li>
<li>작업을 즉시 수행하는 것이 아니라 GCD 큐를 생성하고, dispatch_async를 사용하여 작업을 큐로 이동시킨다. </li>
<li>이 코드는 메인 스레드와 비동기적으로 실행되므로 해당 작업이 GCD 큐에서 진행되는 동안 메인 스레드는 계속해서 이벤트를 기다릴 수 있게 된다.</li>
</ul>
<h3 id="competing-threads">Competing Threads</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 36 13" src="https://user-images.githubusercontent.com/63997044/185035712-7632caed-7777-4910-9517-4e86112c8612.png"></li>
<li><p>GCD를 사용하는 경우에는 이전에는 생각하지 않았던 문제에 직면하게 된다. </p>
</li>
<li><p>우리의 Device는 싱글 코어인데, 코드를 실행하려는 스레드는 두 개가 되었다. </p>
</li>
<li><p>이 경우에 어떤 스레드를 실행해야 할까? </p>
<h3 id="qosquality-of-service-classes">QoS(Quality of Service) Classes</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 36 39" src="https://user-images.githubusercontent.com/63997044/185035763-ae1a4aa8-63fd-47ee-ab0d-c28a62b872b4.png"></li>
<li><p>iOS 8과 OS X Yosemite와 함께 공개된 API이다.</p>
</li>
<li><p>QoS는 시스템에 수행 중인 작업이 어떤 작업인지 작업의 종류를 알려주는 용도로 사용한다. </p>
</li>
<li><p>이를 통해서 시스템은 코드를 효과적으로 실행하도록 리소스 제어를 제공한다. </p>
</li>
<li><p>QoS는 작업의 의도와 분류를 나타낼 수 있는 단일 추상 매개변수로 설계되었다. </p>
<ul>
<li>그래서 특정 값을 모두 조절하는 대신 &quot;User Initiated 작업입니다.&quot; 라고 알리면 시스템이 적합한 값을 자동으로 선택한다.</li>
</ul>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 37 23" src="https://user-images.githubusercontent.com/63997044/185035841-723f0453-5558-4968-ad6c-28cb8fea4c59.png">
<img width="100%" alt="스크린샷 2022-08-17 오후 1 37 44" src="https://user-images.githubusercontent.com/63997044/185035881-03e7f512-0932-488e-ab87-ae9b936dd234.png">

<h4 id="1-uiuser-interactive">1. UI(User Interactive)</h4>
<blockquote>
<p>UI업데이트에 적극적으로 관련되어 있는지 고려해야 한다.
메인 스레드(사실은 아님: <a href="https://h4njun.tistory.com/m/entry/%EB%8F%99%EC%8B%9C%EC%84%B1-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D3-DispatchQueue">H4NJUN
님 블로그</a>)
사용자와 상호 작용이 필요한 이벤트들이 포함된다.
초당 60프레임의 애니메이션이 원활하게 실행되어야 한다.</p>
</blockquote>
<h4 id="2-inuser-initiated">2. IN(User Initiated)</h4>
<blockquote>
<p>사용자가 수행한 작업의 결과를 로드한다.
스크롤 뷰를 스크롤하여 다음 셀을 불러오거나, 이메일을 탭하여 이메일 내용을 로드하는 등의 액션
이러한 작업은 UI로 처리될 필요가 없으므로 IN이 적절하다.
사용자와 상호작용을 유지해야 하는지 고려해야 한다.</p>
</blockquote>
<h4 id="3-ututility">3. UT(Utility)</h4>
<blockquote>
<p>오래 실행되는 작업들이 포함된다.
사용자와의 상호작용을 방해하지 않는다.
사용자가 진행 상황을 알고 있는지 고려해야 한다.</p>
</blockquote>
<h4 id="4-bgbackground">4. BG(Background)</h4>
<blockquote>
<p>사용자가 진행 상황을 알 필요 없다.
유지 관리 작업, 정리 작업, 데이터베이스 청소 등이 포함된다.
언제 수행할 지 사용자가 인식하지 못한다.</p>
</blockquote>
<h3 id="멀티태스킹">멀티태스킹</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 5 03 50" src="https://user-images.githubusercontent.com/63997044/185067184-cb838e04-6844-4217-bdd3-c1b8b4781a80.png"></li>
<li><p>실행중인 앱 #1의 메인 스레드와 GCD 스레드, 멀티태스킹으로 실행중인 앱 #2의 메인 스레드, 그리고 PIP로 실행 중인 앱의 메인 스레드가 있는 상황.</p>
</li>
<li><p>CPU는 2개.</p>
</li>
<li><p>비디오를 디코딩하기 위해 PIP에서 하나를 사용한다면, 나머지는 어떻게 해야 할까?</p>
<ul>
<li>QoS를 활용하여 리소스를 관리를 통해 원활하게 앱을 실행시킬 수 있다.</li>
</ul>
<h2 id="gcd-design-patterns-with-qos">GCD Design Patterns with QoS</h2>
<h3 id="asynchronous-work">Asynchronous Work</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 38 38" src="https://user-images.githubusercontent.com/63997044/185035976-51417086-eccd-4eec-87b0-40fc8331e090.png"></li>
<li><p>이 예시에서는 어떤 QoS가 적합할 것인가? </p>
<ul>
<li>Update UI가 있으므로 - User Interactive</li>
<li>메인 스레드에서 발생하지 않는 비동기 작업은 UI 렌더링이 일어나지 않음 - User Initiated</li>
</ul>
</li>
<li><p>그렇다면 모든 작업들의 QoS를 알맞게 설정해주어야 할까? </p>
<ul>
<li>NOPE!</li>
</ul>
</li>
<li><p>dispatch_async()로 GCD 큐에 작업을 이동할 때, QoS가 자동으로 User Interactive를 User Initiated으로 변환한다.</p>
<ul>
<li>User Interactive는 메인 스레드 사용 그리고 UI 렌더링으로 제한되어야 하기 때문에 QoS를 실수로 과도하게 전파하지 않도록 이 작업을 수행한다.</li>
</ul>
<h3 id="qos-propagation추론된-qos">QoS Propagation(추론된 QoS)</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 39 45" src="https://user-images.githubusercontent.com/63997044/185036111-5b4cb9fb-71e7-4ab8-a36f-04be5cdb5fc7.png"></li>
<li><p>propagation: 전파. QoS의 상태를 전달하는 것을 의미한다.</p>
</li>
<li><p>블록이 큐로 이동할때 캡쳐된 QoS</p>
<ul>
<li>예시에서는 User Interactive에서 User Initiated로 변환된 것을 가리킨다.</li>
</ul>
</li>
<li><p>블록을 받는 큐가 지정된 QoS를 갖지 않을 때 어떤 QoS로 처리될 것인지 추론한다.</p>
</li>
<li><p>메인 스레드로 이동해도 QoS가 낮아지지 않습니다.</p>
<h3 id="long-running-job오래-지속되는-작업">Long Running Job(오래 지속되는 작업)</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 43 10" src="https://user-images.githubusercontent.com/63997044/185036462-3b76290b-2ba6-4b5f-8a48-0a914a5cd241.png"></li>
<li><p>UI를 방해하지 않도록 비동기적으로 처리하는 계산 작업이 있고, 작업이 끝나는 대로 비동기적으로 UI를 업데이트하는 상황</p>
</li>
<li><p>이 예시에서는 어떤 QoS가 적합할 것인가?</p>
<ul>
<li>오래 지속되는 작업이므로 - Utility</li>
</ul>
</li>
<li><p>사용자는 UI를 계속 사용할 수 있고, 결과를 기다리지 않지만 진행 상황을 볼 수 있으며 어떠한 작업을 시작할 수 있으나 그것이 즉각적인 진행을 방해하지 않는다.</p>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 45 01" src="https://user-images.githubusercontent.com/63997044/185036653-5f2bc9df-fadd-420d-83e0-6aaf7d2fb49e.png"></li>
<li><p>명시적으로 QoS를 Utility로 지정해준다.</p>
<h3 id="block-qos">Block QoS</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 45 45" src="https://user-images.githubusercontent.com/63997044/185036723-b50d0345-c903-408a-84fd-297b17db1b1e.png"></li>
<li><p>명시적으로 QoS 속성을 블록에 추가해서 생성하는 방법이다.</p>
</li>
<li><p>블록 객체가 생성될 때 QoS가 캡쳐된다.</p>
<ul>
<li>DISPATCH_BLOCK_CREATE_WITH_QOS_CLASS: 블록 객체를 생성하면서 QoS를 할당한다. DISPATCH_BLOCK_ASSIGN_CURRENT보다 우선순위가 높다.</li>
<li><a href="https://developer.apple.com/documentation/dispatch/dispatch_block_flags_t/dispatch_block_assign_current">DISPATCH_BLOCK_ASSIGN_CURRENT</a> 플래그를 사용하여 생성하면 나중에 해당 블록을 큐에 제출하는 시점에 할당된 QoS값으로 실행된다.</li>
</ul>
<h3 id="maintenance-task">Maintenance Task</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 46 26" src="https://user-images.githubusercontent.com/63997044/185036806-4cea56ff-13b8-42ac-8c39-586ed6689e0a.png"></li>
<li><p>이 예시에서는 어떤 QoS가 적합할 것인가?</p>
<ul>
<li>정리 작업 등 앱을 유지하기 위한 작업이므로 - Background</li>
</ul>
</li>
<li><p>UI 작업을 실행하는 응용 프로그램에서 유지 관리 작업, 정리 작업, 데이터베이스 청소 등 작업이 일어날 때 QoS가 Background인 큐를 만들어 둔다. </p>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 47 14" src="https://user-images.githubusercontent.com/63997044/185036892-bdb2abc5-c3c1-4192-bc87-c5197aa92111.png"></li>
<li><p>작업이 여기로 들어오게 되면 큐에 QoS가 할당되어 있기 때문에 블록의 QoS가 무시되고 백그라운드에서 실행된다.</p>
</li>
<li><p>이와 같이 실행 흐름과 관련없는 작업은 DISPATCH_BLOCK_DETACHED 플래그를 사용하여 이 작업이 실행 흐름과 관련이 없음을 운영체제에 알린다.</p>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 47 32" src="https://user-images.githubusercontent.com/63997044/185036931-22add3a7-ac9d-47e8-b28f-c7eb19db653a.png"></li>
<li><p>DISPATCH_BLOCK_ENFORCE_QOS_CLASS 플래그를 사용하면 블록의 QoS가 무시되지 않는다.</p>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 47 49" src="https://user-images.githubusercontent.com/63997044/185036951-258827d2-d49e-4211-b074-1a6a4b2582ef.png">

<h3 id="asynchronous-priority-inversion">Asynchronous Priority Inversion</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 49 02" src="https://user-images.githubusercontent.com/63997044/185037068-9f558717-b7e8-4e82-94e8-98bcae959781.png"></li>
<li><p>낮은 우선순위의 QoS를 가진 블록이 큐에 들어있는데 높은 우선순위의 QoS를 가진 블록이 직렬 큐에 들어온 상황. 즉 우선 순위가 반전된 상태</p>
</li>
<li><p>GCD가 자체적으로 우선 순위를 조정하여 해결해준다.</p>
<ul>
<li>현재 작업하고 있는 블록의 QoS를 재정의해서 해결한다.
(부연 설명: (naljin님 블로그)[<a href="https://sujinnaljin.medium.com/ios-%EC%B0%A8%EA%B7%BC%EC%B0%A8%EA%B7%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-gcd-15-3fef697f9aab%5D">https://sujinnaljin.medium.com/ios-차근차근-시작하는-gcd-15-3fef697f9aab]</a>)</li>
</ul>
<h3 id="queue-qosqos를-갖는-대기열">Queue QoS(QoS를 갖는 대기열)</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 49 20" src="https://user-images.githubusercontent.com/63997044/185037083-5d333a5a-1a65-45ce-b41a-824aa1040c41.png">
- 단일 목적의 큐에 적합하거나 제출된 블록의 우선순위가 중요하지 않을 때 사용한다.
- 특히 유지 관리나 청소 등 백그라운드 작업에 사용하기 적합하다.
- ENFORCE FLAG를 사용하여 예외를 둘 수 있다.

<h3 id="queue-as-locks">Queue as Locks</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 53 14" src="https://user-images.githubusercontent.com/63997044/185037555-443f092a-71a3-4dde-930d-bab6f7014e6c.png">
- 직렬 큐를 자물쇠 용도로 사용하는 것을 의미한다.
- 직렬 큐와 dispatch sync를 사용하여 해당 블록이 데이터구조에 독점적으로 접근하게 한다.
- 이 때 실행되는 블록의 QoS는 User Interactive, Utility 등 서로 다른 QoS를 가진 다른 블록이 dispatch sync로 접근하는 상황도 발생할 수 있다.

<img width="100%" alt="스크린샷 2022-08-17 오후 1 52 15" src="https://user-images.githubusercontent.com/63997044/185037473-c6ebc492-ce10-4f79-b9ba-268dd40193fb.png">

<h3 id="synchronous-priority-inversion">Synchronous Priority Inversion</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 54 04" src="https://user-images.githubusercontent.com/63997044/185037647-9031ec64-955e-461f-b91d-db75322fa0c2.png">
- 높은 우선순위의 작업이 낮은 우선순위의 작업을 기다리는 현상
- 우선순위의 상속으로 해결할 수 있다.
  - (우선순위의 상속 부연설명: [J.Bear님 블로그](https://jbear.tistory.com/entry/우선순위-전도-우선순위-상속-Priority-Inversion-Priority-Inheritance))


</li>
</ul>
<h2 id="queues-threads-and-run-loops">Queues, Threads, and Run Loops</h2>
<h3 id="run-loop-versus-queue">Run Loop Versus Queue</h3>
<ul>
<li><p>코드 블록의 수행이 완료되면 스레드가 사라진다.</p>
</li>
<li><blockquote>
<p>임시 스레드 풀의 스레드. 수명을 보장할 수 없다.</p>
</blockquote>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 55 16" src="https://user-images.githubusercontent.com/63997044/185037805-0d31e707-4301-404c-a701-de677a5ba7a2.png">

<h3 id="run-loop와-serial-queue의-비교">Run Loop와 Serial Queue의 비교</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 55 36" src="https://user-images.githubusercontent.com/63997044/185037840-344115c8-70b6-4864-8c47-7acda161ab8f.png"></li>
<li><p>Run Loop는 특정 스레드에 의존적이다. Serial Queue는 수명이 짧은 스레드를 사용한다.</p>
</li>
<li><p>Run Loop는 위임 메서드의 콜백을 받는다. Serial Queue는 블록을 콜백으로 사용한다.</p>
</li>
<li><p>Run Loop는 각각을 반복한 후 Autorelease pool을 통해 pop된다. Serial Queue는 스레드가 완전히 유휴 상태가 되었을 때 pop된다.</p>
</li>
<li><p>Run Loop는 재진입적으로 사용할 수 있다. Serial Queue는 재진입 구조가 아니므로 deadlock이 발생한다.</p>
<h3 id="thread-creation-and-pooling">Thread Creation and Pooling</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 56 48" src="https://user-images.githubusercontent.com/63997044/185037980-c09d0fd6-5068-4222-a03e-f8ba3a5226b1.png">

<ul>
<li>GCD는 임시 스레드를 사용한다고 했다.</li>
<li>많은 dispatch_async 호출이 발생하면 시스템은 스레드 풀에서 스레드를 가져와 각 블록에 제공한다. </li>
<li>만약 우리의 Device가 2개의 코어를 가졌다면, 두 블록이 스레드를 받은 것이 이상적인 상황이고, 하나의 블록의 작업이 끝나면 스레드가 반환되고 이를 다음 블록에 전달되는 식으로 진행될 것이다.</li>
</ul>
<h3 id="waiting">Waiting</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 57 28" src="https://user-images.githubusercontent.com/63997044/185038049-d5bb692b-2497-4514-9fb9-190489f59e81.png">

<ul>
<li>Waiting은 블록이 스레드를 할당 받았지만 자원에 접근을 할 수 없어서 기다리는 상태이다.</li>
<li>스레드가 대기 상태가 되면 GCD는 코어 당 하나의 스레드가 실행되도록 다른 스레드를 또 불러온다.</li>
</ul>
<h3 id="thread-creation-and-waiting--thread-explosion">Thread Creation and Waiting &amp; Thread Explosion</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 58 08" src="https://user-images.githubusercontent.com/63997044/185038145-48bfd485-b002-4136-90b2-05b1f9eed74e.png">

<img width="100%" alt="스크린샷 2022-08-17 오후 1 58 26" src="https://user-images.githubusercontent.com/63997044/185038176-73282457-3eb5-4358-a7e6-5e4b2de78cf1.png">

<ul>
<li>많은 블록들이 실행되어야 하고 이들이 모두 Waiting 상태가 되어 많은 스레드가 대기 상태가 되는 것 -&gt; Thread Explosion이 발생한다.</li>
<li>다량의 스레드가 대기를 멈추면 자원에 대한 경쟁이 발생한다.</li>
<li>또한 생성할 수 있는 스레드의 수도 무한적이지 않은데 새로운 작업이 또 들어오면? <ul>
<li>-&gt; 교착 상태를 유발한다.</li>
</ul>
</li>
</ul>
<h3 id="thread-explosion-causing-deadlock">Thread Explosion Causing Deadlock</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 1 59 47" src="https://user-images.githubusercontent.com/63997044/185038334-b7cb28ce-821f-4f4e-b663-ee6b370c432f.png">

<ul>
<li>사용할 수 있는 모든 스레드를 사용했기 때문에 스레드 풀에서 추가적인 스레드를 가져올 수 없는 상황이다.</li>
<li>메인 스레드의 어떤 작업이 직렬 큐에 dispatch async로 들어간다고 가정하면, 현재 사용가능한 스레드가 없기 때문에 아직 실행되지 않고 대기하는 상태가 된다.</li>
<li>그 다음, 메인 스레드가 같은 직렬 큐에 dispatch sync를 한다.</li>
<li>이렇게 되면 직렬 큐에 사용할 수 있는 스레드가 없기 때문에 호출은 영원히 차단되고, 교착 상태가 발생한다.</li>
</ul>
<h3 id="avoiding-thread-explosion">Avoiding Thread Explosion</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 01 35" src="https://user-images.githubusercontent.com/63997044/185038549-27187602-997b-489c-a125-60b1418316ee.png">

<ul>
<li>특히 입출력의 경우 가능하면 항상 비동기 API를 사용하는 것이 좋다.</li>
<li>직렬 큐를 사용한다.</li>
<li>NSOperationQueue 사용한다. - maxConcurrentOperationCount 옵션으로 조절 가능.</li>
<li>무제한 작업을 생성하지 말아야 한다.</li>
</ul>
<h3 id="잘못된-예제">잘못된 예제</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 01 50" src="https://user-images.githubusercontent.com/63997044/185038580-e491b8fa-4338-4c83-be25-091fb0147a6c.png">

<ul>
<li>Sync와 Async의 혼합. main thread에서 할 때 특히 주의</li>
</ul>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 02 07" src="https://user-images.githubusercontent.com/63997044/185038614-847e3ac1-fe27-43e2-ab01-ea7baea1ff0b.png"></li>
<li><p>너무 많은 블록을 생성했기 때문에 Explosion 및 Deadlock의 위험이 있다. </p>
<ul>
<li>dispatch_apply: GCD가 병렬 처리를 관리해줌.</li>
</ul>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 02 19" src="https://user-images.githubusercontent.com/63997044/185038628-64e1d6e8-c104-40db-b70b-afbef7362c3a.png">

<ul>
<li>dispatch semaphore: 실행하려는 동시 작업 수를 지정하여 작업 시작하고 나머지는 차단하여 대기 상태로 만든다.</li>
</ul>
<h2 id="gcd-and-crash-reports">GCD and Crash Reports</h2>
<h3 id="manager-thread">Manager thread</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 03 10" src="https://user-images.githubusercontent.com/63997044/185038723-e4afe1ae-6d62-480b-9067-e5e741416bce.png">
- 응용 프로그램을 사용하는 거의 모든 GCD에 존재한다.
- Dispatch Manager Thread가 Root Frame이다.
- 일반적으로 무시한다

<h3 id="idle-gcd-thread">Idle GCD thread</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 2 03 29" src="https://user-images.githubusercontent.com/63997044/185038753-ceb3bc8b-87ae-426a-9996-c2a755bfd449.png">
- 스택의 맨 아래에서 작업 대기열 시작 스레드를 볼 수 있다. 
- GCD 스레드라는 표시가 있다. 

<h3 id="active-gcd-thread">Active GCD thread</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 3 37 08" src="https://user-images.githubusercontent.com/63997044/185051262-4a3b47de-156d-4759-8b87-1b8b67ced0d3.png">
- 작업 대기열 스레드를 시작하지만 디스패치 클라이언트 호출 및 디스패치 호출 블록 및 릴리스와 같은 항목이 표시되고 그 뒤에 코드가 표시된다. 
- 대기열을 생성할 때 전달한 디스패치 대기열 이름도 함께 표시된다. 설명적인 대기열 이름을 지정하는 것이 중요하다.

<h3 id="idle-main-thread">Idle main thread</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 3 37 35" src="https://user-images.githubusercontent.com/63997044/185051316-169f3622-4e82-458c-8c60-52d277b313e8.png">
- 메인 스레드가 유휴 상태일 때 모의 메시지 트랩, CF 실행 루프 포트 및 CF 실행 루프 실행에 앉아 있는 것을 볼 수 있으며 com.apple.main.thread를 볼 수 있다.

<h3 id="main-queue">Main queue</h3>
<img width="100%" alt="스크린샷 2022-08-17 오후 3 37 46" src="https://user-images.githubusercontent.com/63997044/185051336-ac9b8160-fc10-4ae9-8d8c-71b40853f6e2.png">
- 메인 큐가 활성화 상태인 경우 메인 큐의 GCD 큐로 인해 CF Run loop가 메인 디스패치 큐를 서비스한다.
- 우리가 호출하는 NSBlock 작업이 존재한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC2020] - Unsafe Swift]]></title>
            <link>https://velog.io/@grumpy-sw/Unsafe-Swift</link>
            <guid>https://velog.io/@grumpy-sw/Unsafe-Swift</guid>
            <pubDate>Tue, 22 Nov 2022 13:16:51 GMT</pubDate>
            <description><![CDATA[<pre><code>👨‍💻 WWDC 2020 Unsafe Swift를 읽고 정리한 글입니다.</code></pre><img width="713" alt="스크린샷 2022-11-22 오후 10 05 24" src="https://user-images.githubusercontent.com/63997044/203321124-12b269e9-b983-40a1-8dcf-ca1fa8f663f9.png">

<blockquote>
<p><strong>Overview</strong></p>
</blockquote>
<p>Swift 팀과 함께 프로그래밍 언어의 안전 예방 조치를 살펴보자.</p>
<ul>
<li>코드가 &quot;<strong>안전하지 않다</strong>&quot;는 의미는 무엇일까?</li>
<li>예기치 않은 상태와 동작을 방지하기 위해 보다 구체적으로 코드를 작성하는 방법에 대해서.</li>
</ul>
<h2 id="-unsafe-api"># Unsafe API</h2>
<img width="715" alt="스크린샷 2022-11-22 오후 1 12 29" src="https://user-images.githubusercontent.com/63997044/203220969-dc8fcf43-9d86-46be-a884-ffc3f6c11f37.png">

<p>표준 라이브러리 중 몇 가지는 <strong>Unsafe</strong>로 표시되어 있다.
Safe와 Unsafe의 차이점은 실제로 제공하는 인터페이스상으로는 명확하지 않지만 <strong>유효하지 않은 입력을 처리하는 방식</strong>에서 발생한다.</p>
<img width="716" alt="스크린샷 2022-11-22 오후 1 13 44" src="https://user-images.githubusercontent.com/63997044/203220994-fa80a446-6f3f-4956-8126-7110da75d2ee.png">

<p>한 가지 예는 Optional에 대한 강제 언래핑 연산자(<code>!</code>)이다. 
이 코드의 예상되는 결과는 당연히 런타임 에러와 함께 앱이 실행 중지될 것이다.
당연히 잘못된 시도를 하고 있지만, 중요한 것은 <u>우리가 결과를 알고 있다는 것.</u></p>
<p>강제 언래핑 연산자는 요구 사항을 충족하지 않는 입력을 포함하여 가능한 모든 입력에 대한 동작을 완전히 설명할 수 있기 때문에 &quot;<strong>Safe</strong>&quot;하다고 말할 수 있다.</p>
<img width="714" alt="스크린샷 2022-11-22 오후 1 15 03" src="https://user-images.githubusercontent.com/63997044/203221049-3f9bfce4-0aa8-4162-83fb-9f3ab90b2326.png">

<p><code>.unsafelyUnwrapped</code> 프로퍼티를 통해 강제 언래핑이 가능하다. 이 동작의 결과를 예상할 수 있을까?</p>
<img width="712" alt="스크린샷 2022-11-22 오후 1 14 15" src="https://user-images.githubusercontent.com/63997044/203220996-cc884d68-2f4d-4abb-a071-5ec9febac978.png">

<p>컴파일러가 런타임에 이를 확인하고 요구사항을 충족하지 않는 입력이 들어오면 오류를 발생시킨다. 이런 예측가능하고 정의되어 있는 것을 <code>Safe</code>하다고 표현한다.</p>
<img width="715" alt="스크린샷 2022-11-22 오후 1 14 24" src="https://user-images.githubusercontent.com/63997044/203220998-f936187d-4d6b-45b4-8fa4-6dd71a1b5241.png">

<p>이와 반대로 <code>Unsafe</code>한 작업은 문서화된 기대치를 위반하는, 말 그대로 정의되지 않은 동작을 해야 한다.</p>
<p>안전하지 않은 동작은 컴파일러가 이러한 확인 과정을 거치지 않고 진행하기 때문에 어떤 동작을 할 지 예상할 수 없다. 임의의 상황에 따라 크래시를 발생시키거나 쓰레기 값을 반환할 수도 있다. 매번 같은 결과를 반환할 수도 있고, 실행할 때마다 결과가 다를 수도 있다.</p>
<p>따라서 디버깅이 매우 어렵기 때문에 해당 요구 사항을 충족할 전적인 책임을 개발자가 지게 된다. </p>
<img width="713" alt="스크린샷 2022-11-22 오후 1 15 51" src="https://user-images.githubusercontent.com/63997044/203221425-6eb3e9ff-eee7-4c13-9ba8-aba973f77501.png">

<p>이것은 모든 Unsafe 타입에 대한 전형적인 특징이다.
<code>Unsafe</code> 접두사는 위험 기호와 같은 명명 규칙으로 이 사용에 대한 내재적인 위험에 대해 경고한다. 그러나 실제로 일부 작업은 이를 통해서만 수행할 수 있기 때문에 사용에 각별한 주의가 필요하며 사용 조건을 명확히 이해하고 있어야 한다.</p>
<img width="717" alt="스크린샷 2022-11-22 오후 1 16 46" src="https://user-images.githubusercontent.com/63997044/203221390-891e8313-cec0-4d25-abba-f5b83d4fda65.png">

<p>일반적으로 Unsafe를 사용하는 사례는 다음 두 종류 중 하나이다.</p>
<ul>
<li>C, Objective-C와의 상호 운용성 제공</li>
<li>런타임 성능이나 프로그램 실행에서의 세밀한 제어</li>
</ul>
<p>옵셔널의 <code>unsafelyUnerapped</code> 속성은 두 번째 범주에 속한다. 옵셔널 검사와 같은 이런 작은 비용들이 성능 측정에 악영향을 미치기 때문이다. <code>nil</code> 값에 대해 검사가 불필요한 경우 검사하지 않고 넘어갈 수 있다. </p>
<img width="713" alt="스크린샷 2022-11-22 오후 1 17 39" src="https://user-images.githubusercontent.com/63997044/203221394-6c2b627d-1896-4ddd-80a8-c6618b833be5.png">

<p>따라서 Safe한 API가 no crashes를 의미하는 것이 아니라는 점에 유의해야 한다. 오히려 그 반대이다.</p>
<h2 id="-unsafe-pointer"># Unsafe Pointer</h2>
<img width="713" alt="스크린샷 2022-11-22 오후 1 18 36" src="https://user-images.githubusercontent.com/63997044/203222702-318c8706-f721-4747-b8f3-8257a5d6f735.png">

<p>Swift 표준 라이브러리는 C언어의 포인터와 거의 동일한 수준의 (Unsafe한) 포인터 타입을 제공한다.
포인터에 대해 이야기하기 전에 먼저 메모리에 대해 알아보자.</p>
<img width="714" alt="스크린샷 2022-11-22 오후 1 20 34" src="https://user-images.githubusercontent.com/63997044/203222687-afe6b226-9d35-4378-b9bb-3f63b1691433.png">

<p>Swift는 플랫 메모리 모델을 사용한다. 메모리를 개별적으로 주소 지정이 가능한 8바이트의 플랫한 주소 공간으로 취급한다. 그리고 이러한 각 바이트에는 일반적으로 16진수 값으로 주어진 고유한 주소가 있다.</p>
<img width="716" alt="스크린샷 2022-11-22 오후 1 44 43" src="https://user-images.githubusercontent.com/63997044/203250615-6b3de6ff-92c8-45fa-98a3-f16f107bbbb1.png">

<p>이제 런타임에서, 주소 공간은 앱의 실행 상태를 나타내는 데이터들로 채워진다. 여기에 저장되는 것들은 다음과 같다.</p>
<ul>
<li>앱의 실행 가능한 바이너리</li>
<li>import하고 있는 모든 라이브러리와 프레임워크</li>
<li>임시 변수, 함수의 파라미터 등에 대한 저장공간인 스택</li>
<li>클래스 인스턴스 스토리지를 포함한 동적 할당 메모리</li>
<li>이미지 파일 등 읽기 전용 리소스 파일</li>
</ul>
<p>각 개별 항목은 연속 메모리 영역이 할당되고, 앱이 실행되면서 메모리 상태는 계속 변화한다. 새 항목이 할당되고, 기존 항목이 사라지면서 메모리가 해제되고... 일반적으로 Swift에서는 수동으로 메모리를 관리할 필요가 없다.</p>
<p>그러나 만약 수동으로 메모리를 관리할 필요가 생기면, Unsafe 포인터가 필요한 모든 low-level한 작업들을 제공한다. 물론 Unsafe한 API이기 때문에 모든 제어에는 책임이 따른다. 주의해서 사용하지 않으면 포인터 작업이 주소 공간을 망쳐 잘 유지되고 있는 앱의 상태를 망칠 수 있다.</p>
<img width="713" alt="스크린샷 2022-11-22 오후 3 50 28" src="https://user-images.githubusercontent.com/63997044/203250948-11d3f43b-4ea2-4665-b736-dbb3bca7e3b8.png">

<p>정수 값에 대한 저장 공간을 동적으로 할당하면 이에 대한 직접적인 포인터가 제공된다. 포인터는 메모리에 대한 완전한 제어를 제공하지만 관리하지는 않는다. 즉 해당 메모리 위치에 나중에 어떤 일이 발생하는지 추적이 불가능하며 단지 사용자가 지시한 작업을 실행할 뿐이다.</p>
<img width="716" alt="스크린샷 2022-11-22 오후 3 51 01" src="https://user-images.githubusercontent.com/63997044/203251500-1c451a33-a324-4d33-ac9b-8fa6fcc88e4c.png">


<p>메모리를 초기화하고 할당 해제함에 따라 포인터가 무효화된다. 그러나 포인터가 무효화되었는지 유효화되었는지는 알 수 없다. 포인터 자체는 자신이 무효화되었다는 것을 알지 못하므로 이러한 댕글링 포인터를 역참조하는 오류를 범할 수 있다.</p>
<blockquote>
<p><strong>댕글링 포인터(Dangling Pointer)</strong>
💡 해제된 메모리 영역을 여전히 가리키고 있는 포인터</p>
</blockquote>
<p>운이 좋다면 할당 해제에 의해 완전히 접근할 수 없게 되어 즉시 충돌이 일어날 수도 있다. 그러나 포인터의 동작은 Unsafe하기 때문에 후속 동작이 보장되지 않는다. 후속 할당을 통해 동일한 주소를 재사용하여 다른 값을 저장했을 수도 있고 이 경우 앱의 전혀 관련없는 부분의 상태가 손상될 수 있다.
따라서 앱의 데이터 손상 혹은 사용자 데이터의 손실 등 <strong>임의의 영향</strong>을 미칠 수 있다는 위험이 있다.</p>
<img width="710" alt="스크린샷 2022-11-22 오후 3 51 28" src="https://user-images.githubusercontent.com/63997044/203251568-97bd0f81-56c3-43e2-a46b-b9a59dc4a17a.png">

<p>Xcode의 Address Sanitizer라는 도구를 통해 이런 문제들을 디버깅할 수 있다.</p>
<img width="713" alt="스크린샷 2022-11-22 오후 3 52 25" src="https://user-images.githubusercontent.com/63997044/203251814-4e2b60bb-eb62-4f8f-80a6-7df3fc23130e.png">

<p>포인터가 그렇게 위험하다면서 굳이 포인터를 고집하는 이유는 무엇일까? 
가장 큰 이유는 C 혹은 Objective-C와 같은 언어와의 상호 운용성을 제공하기 때문이다. 이런 언어에서는 포인터 인자를 사용하기 때문에 Swift에서 호출할 수 있으려면 Swift 값에 대한 포인터를 생성하는 방법을 알아야 한다. C언어 API는 이러한 매핑을 통해 Swift로 번역된다.</p>
<img width="715" alt="스크린샷 2022-11-22 오후 3 52 51" src="https://user-images.githubusercontent.com/63997044/203251947-a562784c-24df-4e05-a5d1-c5f6b3f3e4ae.png">

<p>예를 들면 정수 값 버퍼를 처리하는 이런 C언어의 함수를 Swift로 가져온다면 이렇게 구현할 수 있다.</p>
<img width="717" alt="스크린샷 2022-11-22 오후 3 53 31" src="https://user-images.githubusercontent.com/63997044/203251960-d10a960a-c0ea-4fd4-88c2-fdff75fcb2a4.png">

<p>이어서 사용 예시를 보자. static 메서드인 <code>allocate()</code>를 통해 동적 버퍼를 생성하고 버퍼의 요소를 특정 값으로 설정한다. 설정이 끝나면 C 함수에 대응하는 <code>process_integers</code>를 사용하여 초기화된 버퍼에 대한 포인터를 전달할 수 있다. 이후 소멸과 할당 해제 과정까지 모든 단계가 Unsafe하다. </p>
<p>모든 단계는 확인되지 않은 전제 조건들이 있으며 그 중 하나라도 잘못되면 정의되지 않은 동작이 발생한다. 단계별로 나눠서 잠재적인 위험은 다음과 같다.</p>
<ul>
<li>할당 단계<ul>
<li>버퍼의 생명주기는 관리되지 않으므로 수동으로 할당을 해제해야 한다. 그렇지 않으면 메모리 누수가 발생하여 영원히 남아있게 된다.</li>
</ul>
</li>
<li>초기화 단계<ul>
<li>지정된 위치의 주소가 할당한 버퍼 내에 있는지 자동으로 확인하지 않는다. 만약 주소를 잘못 지정한다면 정의되지 않은 동작이 발생한다.</li>
</ul>
</li>
<li>함수 호출 단계<ul>
<li>버퍼의 소유권을 가져갈 것인지 여부를 알아야 한다. 여기서는 함수 호출 기간동안만 액세스하고 포인터를 유지하거나 할당 해제 등을 시도하지 않는다고 가정한다</li>
</ul>
</li>
<li>소멸(초기화 해제) 단계<ul>
<li>메모리가 이전에 올바르게 초기화된 경우여야 한다.</li>
</ul>
</li>
<li>할당 해제 단계<ul>
<li>이전에 메모리 공간이 할당되어 있고 deinitialized 상태에 있는 메모리만 할당 해제할 수 있다.</li>
</ul>
</li>
</ul>
<img width="720" alt="스크린샷 2022-11-22 오후 3 56 14" src="https://user-images.githubusercontent.com/63997044/203252204-28a031b0-cd54-40a1-9f5e-ca115135e105.png">

<p>이것이 표준 라이브러리가 <code>UnsafeBufferPointer</code>를 제공하는 이유이다. 개별 값에 대한 포인터가 아니라 메모리 영역으로 작업해야 할 때 유용하다. 버퍼를 <code>(시작위치, 길이)</code> 값의 쌍으로 모델링한다. 이를 통해 버퍼의 경계를 쉽게 사용할 수 있어 <code>Out of Bounds</code> 문제를 쉽게 확인할 수 있다.</p>
<img width="713" alt="스크린샷 2022-11-22 오후 4 11 16" src="https://user-images.githubusercontent.com/63997044/203252312-1d18de67-ee3f-41c9-8fe6-2d5a28984c18.png">

<p>Swift의 표준 연속 컬렉션은 이러한 Unsafe 메서드를 사용한 버퍼 포인터를 사용하여 저장 공간 버퍼에 임시적인 직접 접근을 제공한다. </p>
<img width="713" alt="스크린샷 2022-11-22 오후 4 12 22" src="https://user-images.githubusercontent.com/63997044/203252334-416f60f8-9f90-491c-bee4-0fecf7a33950.png">

<p>또한 개별 값에 대한 임시 포인터를 얻을 수 있으며 C 함수에 매개변수로 전달할 수 있다.</p>
<img width="714" alt="스크린샷 2022-11-22 오후 4 19 46" src="https://user-images.githubusercontent.com/63997044/203252489-909065ae-3700-4375-b746-a625429e5e74.png">

<p>Swift 코드에서 C 함수로 포인터를 전달하는 예시</p>
<img width="714" alt="스크린샷 2022-11-22 오후 4 20 06" src="https://user-images.githubusercontent.com/63997044/203252595-cfb7915a-324c-4709-be0b-df28b49e25c0.png">

<p>이러한 행위가 너무 자주 발생하기 때문에 이런 코드도 지원한다. 그러나 이런 방법들이 여전히 나중에 메모리 액세스에서 정의되지 않은 동작이 발생할 수 있다는 것을 기억해야 한다.</p>
<img width="714" alt="스크린샷 2022-11-22 오후 4 20 32" src="https://user-images.githubusercontent.com/63997044/203252609-c39f8f32-ed98-4065-a7f7-0f4834578b28.png">

<p>Swift에서 지원하는 암시적인 값-포인터 변환 목록은 다음과 같다. </p>
<img width="714" alt="스크린샷 2022-11-22 오후 4 20 50" src="https://user-images.githubusercontent.com/63997044/203252913-ee90d8d8-9ee5-4186-ac31-add4beb04db4.png">

<p>다음은 더 복잡한 C언어 인터페이스의 예시이다. <code>sysctl</code> 함수는 여러 포인터와 값들을 인자로 사용한다.</p>
<img width="718" alt="스크린샷 2022-11-22 오후 4 21 17" src="https://user-images.githubusercontent.com/63997044/203252921-269ae8f7-e3f4-415c-ab56-14cc8262ffde.png">

<p>대응되는 Swift 코드를 사용하여 함수를 호출하는 모습. 여기서 중요한 것은 모두 Unsafe한 함수를 사용하기 때문에 이들을 각각의 단일 함수 호출로 분리하여 간단하게 유효성 검사를 하는 것만으로도 위험을 방지하는데 많은 도움이 된다. </p>
<img width="712" alt="스크린샷 2022-11-22 오후 4 22 07" src="https://user-images.githubusercontent.com/63997044/203252926-bec85ffa-44d0-4a39-9ee1-70220c459af3.png">

<p>이런 형태의 클로저 기반 디자인은 포인터의 수명을 더 명확하게 알 수 있다. 댕글링 포인터의 값에 액세스하는 것은 정의되지 않은 동작이므로 Swift 5.3 컴파일러는 이러한 경우를 감지하여 경고를 생성한다.</p>
<img width="716" alt="스크린샷 2022-11-22 오후 4 22 24" src="https://user-images.githubusercontent.com/63997044/203252929-2a15261d-980a-4798-9849-fe229761a156.png">

<p>또 다른 개선 중 하나는 새로운 초기화 구문을 제공한다는 것이다. 이 방법을 사용하면 초기화되지 않은 저장 공간에 데이터를 직접 복사하여 생성하기 때문에 임시 버퍼를 할당할 필요가 없다.</p>
<img width="716" alt="스크린샷 2022-11-22 오후 4 22 48" src="https://user-images.githubusercontent.com/63997044/203252931-52494e6c-771e-478b-b616-5e2b33be2d1b.png">

<h3 id="요약">요약</h3>
<ul>
<li>Unsafe API를 사용하려면 요구 사항을 파악하고 항상 이를 충족하도록 구현해주어야 한다. 그렇지 않으면 예기치 못한 동작을 하게 될 것이다.</li>
<li>Unsafe API 사용을 최소한의 단위로 유지하면 이를 제어하기 쉽다.</li>
<li>포인터 값을 직접 사용하기보다는 UnsafeBufferPointer를 사용하여 경계를 추적하는 것이 좋다.</li>
<li>Xcode는 Address Sanitizer라는 도구를 제공하므로 이를 사용하면 디버깅에 유용하다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>