<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>KimBaechu</title>
    <link>https://baechukim.tistory.com/</link>
    <description>김배추의 배추밭</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 19:32:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Kim_Baechu</managingEditor>
    <item>
      <title>[Swift] Concurrency 와 GCD</title>
      <link>https://baechukim.tistory.com/159</link>
      <description>&lt;h1&gt;Concurrency&lt;/h1&gt;
&lt;p&gt;Swift Concurrency는 비동기 프로그래밍을 지원하기 위한 도구로, 코드를 간결하고 효율적으로 작성할 수 있게 도와줍니다. 이 기능은 &lt;code&gt;async&lt;/code&gt;와 &lt;code&gt;await&lt;/code&gt; 키워드를 사용하여 비동기 작업을 선언하고 처리할 수 있습니다.&lt;/p&gt;
&lt;p&gt;Swift Concurrency의 동작 방식에 대해 자세히 알아보면 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Structured Concurrency:&lt;ul&gt;
&lt;li&gt;Swift Concurrency는 구조적 동시성(Structured Concurrency)을 지원합니다. 이는 비동기 작업 간의 종속성을 명시적으로 관리하여 프로그램의 실행 흐름을 제어하는 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;async let&lt;/code&gt; 구문을 사용하여 여러 비동기 작업을 동시에 실행하고, &lt;code&gt;await&lt;/code&gt;를 사용하여 해당 작업들이 완료될 때까지 대기할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Actors:&lt;ul&gt;
&lt;li&gt;Actors는 동시성을 위한 새로운 개념으로 도입되었습니다. Actor는 동시에 실행되는 여러 작업에 안전하게 접근할 수 있는 독립된 단위입니다.&lt;/li&gt;
&lt;li&gt;Actor는 데이터를 보호하고, 동기화 없이 안전하게 공유 상태를 업데이트할 수 있습니다. Actor 메서드는 자동으로 스레드 안전하게 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;actor&lt;/code&gt; 키워드를 사용하여 Actor를 정의하고, 다른 코드에서는 해당 Actor의 메서드와 속성에 &lt;code&gt;await&lt;/code&gt;를 사용하여 접근할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Async/Await:&lt;ul&gt;
&lt;li&gt;Swift Concurrency에서 가장 중요한 키워드는 &lt;code&gt;async&lt;/code&gt;와 &lt;code&gt;await&lt;/code&gt;입니다. &lt;code&gt;async&lt;/code&gt; 키워드는 비동기 작업을 선언하는 데 사용되며, &lt;code&gt;await&lt;/code&gt; 키워드는 비동기 작업이 완료될 때까지 기다리는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;async&lt;/code&gt; 키워드로 선언된 함수는 비동기 작업을 반환하고, &lt;code&gt;await&lt;/code&gt; 키워드로 해당 작업이 완료될 때까지 기다릴 수 있습니다. 이를 통해 비동기 코드를 동기 코드처럼 작성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Task:&lt;ul&gt;
&lt;li&gt;Swift Concurrency에서는 &lt;code&gt;Task&lt;/code&gt;라는 개념이 도입되었습니다. &lt;code&gt;Task&lt;/code&gt;는 비동기 작업의 실행을 나타내는 타입입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Task&lt;/code&gt;를 사용하여 비동기 작업을 생성하고, 실행 중인 작업을 취소하거나 대기하고 있는 작업을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Swift Concurrency는 비동기 프로그래밍을 더욱 쉽고 직관적으로 만들어줍니다. 구조적 동시성과 Actors를 통해 동시성 버그를 방지하고 안전하게 코드를 작성할 수 있으며, &lt;code&gt;async/await&lt;/code&gt;를 사용하여 비동기 작업을 보다 직관적으로 다룰 수 있습니다. 이러한 기능들은 Swift 언어 자체에 통합되어 있어 개발자들이 비동기 코드를 더욱 쉽게 작성하고 관리할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;h2&gt;GCD와 비교&lt;/h2&gt;
&lt;p&gt;Swift Concurrency와 GCD는 CPU와 스레드를 다루는 방식에서 차이가 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Swift Concurrency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Swift Concurrency는 비동기 작업을 처리하기 위한 언어 수준의 기능입니다.&lt;/li&gt;
&lt;li&gt;Swift Concurrency는 스레드와 CPU를 직접 다루지 않습니다. 대신, 비동기 작업을 실행하고 관리하기 위해 구조적 동시성(Structured Concurrency)과 Actors를 활용합니다.&lt;/li&gt;
&lt;li&gt;구조적 동시성은 비동기 작업 간의 종속성을 명시적으로 관리하고, 작업의 실행을 스케줄링합니다. 이를 통해 코드의 가독성과 유지보수성을 높이고 동시성 버그를 사전에 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Actors는 안전한 데이터 공유를 보장하고 동시 접근을 관리하기 위한 메커니즘입니다. 각 &lt;code&gt;Actor&lt;/code&gt;는 독립된 스레드에서 실행되며, 메시지 전달을 통해 안전하게 데이터를 공유합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GCD (Grand Central Dispatch):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GCD는 저수준의 C 기반 API로, 다중 스레딩 및 비동기 작업을 처리하기 위한 기술입니다.&lt;/li&gt;
&lt;li&gt;GCD는 CPU 코어와 스레드를 활용하여 작업을 처리합니다. CPU 코어는 실제 작업을 실행하는 처리 장치이고, 스레드는 작업의 실행을 담당하는 실행 컨텍스트입니다.&lt;/li&gt;
&lt;li&gt;GCD는 여러 개의 큐를 사용하여 작업을 스케줄링하고, 각 큐는 하나 이상의 스레드를 가질 수 있습니다. GCD는 작업을 큐에 추가하고, 적절한 스레드에서 해당 작업을 실행합니다. 작업은 FIFO(First-In-First-Out) 방식으로 실행될 수 있습니다.&lt;/li&gt;
&lt;li&gt;GCD는 시스템 자원을 효율적으로 활용하기 위해 작업의 실행을 동적으로 조절하며, 스레드 풀을 사용하여 스레드를 재사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Swift Concurrency는 CPU와 스레드를 직접 다루지 않고 구조적 동시성과 Actors를 활용하여 비동기 작업을 다룹니다.&lt;br&gt;GCD는 CPU와 스레드를 사용하여 다중 스레딩과 비동기 작업을 처리합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;GCD 사용 사례:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;병렬 처리: 시스템 자원을 효율적으로 활용하여 병렬 처리를 수행해야 할 때, GCD는 큐와 작업 스케줄링 기능을 제공하여 다중 스레드 작업을 관리할 수 있습니다.&lt;ul&gt;
&lt;li&gt;저수준 작업 스케줄링: 스레드와 큐를 직접 관리하고 싶거나, 특정 작업을 세밀하게 제어하고자 할 때, GCD는 저수준의 API를 제공하여 작업 스케줄링과 동시성 관리를 자유롭게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;기존 GCD 코드와의 호환성: 기존에 작성된 GCD 코드를 유지하고 있는 경우나, 특정 프레임워크가 GCD를 요구하는 경우에는 GCD를 계속 사용하는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Swift Concurrency와 GCD는 상호 배타적이지 않습니다.&lt;br&gt;Swift Concurrency를 사용하면서 GCD도 여전히 필요한 경우도 있을 수 있습니다.&lt;br&gt;예를 들어, Swift Concurrency 내에서 GCD를 사용하여 특정 작업을 처리하거나, Swift Concurrency로 작성된 코드를 기존 GCD 기반의 프로젝트에 통합하는 등의 상황이 있을 수 있습니다.&lt;/p&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <category>GCD</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/159</guid>
      <comments>https://baechukim.tistory.com/159#entry159comment</comments>
      <pubDate>Mon, 29 May 2023 13:12:01 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] DiffableDatasource 와 reconfigureItems</title>
      <link>https://baechukim.tistory.com/158</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;iOS15에서부터 &lt;code&gt;reconfigureItems&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;To update the contents of existing (including prefetched) cells without replacing them with new cells&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cell을 update하기 위해서 &lt;code&gt;reloadItems(\\_:)&lt;/code&gt;대신 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 셀로 바꾸는 것이 아니기 때문에 퍼포먼스가 더 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Because this method reconfigures existing cells, the collection view or table view doesn&amp;rsquo;t call prepareForReuse for each cell dequeued.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 prepareForReuse가 불리지 않는 다는 점을 주의해야합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reconfigure -&amp;gt; collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item) -&amp;gt; UICollectionView.CellRegistration&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;self-Sizing 셀일 경우, reconfiguring 이후에 셀을 리사이즈합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animatingDifferences로 애니메이션을 결정하게 되는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 프로퍼티를 설정할 때 애니메이션을 피하고 싶으면 cell configuration logic에 &lt;code&gt;performWithoutAnimation(\\_:)&lt;/code&gt; 를 사용하세요.&lt;/p&gt;</description>
      <category>iOS</category>
      <category>DiffableDataSource</category>
      <category>reconfigureItems</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/158</guid>
      <comments>https://baechukim.tistory.com/158#entry158comment</comments>
      <pubDate>Thu, 30 Mar 2023 23:27:38 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Types - some,  .Type, .Protocol, Self, .self / 스위프트 타입</title>
      <link>https://baechukim.tistory.com/157</link>
      <description>&lt;h1&gt;&lt;b&gt;Opaque Type&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불투명 유형(opaque type)은 기본 구체적인 유형을 지정하지 않고 프로토콜 또는 프로토콜 구성을 준수하는 유형을 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불투명&amp;nbsp;유형은 함수 또는 서브크립트의 반환 타입 또는 프로퍼티의 타입으로 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불투명&amp;nbsp;유형은 튜플 유형 또는 제네릭 유형(배열의 요소 유형 또는 옵셔널 타입의 래핑된 유형과 같은)의 일부로 표시될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Opaque types have the following form:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;some&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;constraint&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constraint은 class 유형, protocol 유형, protocol composition 유형 또는 Any입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값은 listed 프로토콜 또는 프로토콜 구성을 준수하거나 listed 클래스에서 상속되는 형식의 인스턴스인 경우에만 불투명&amp;nbsp;형식의 인스턴스로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불투명&amp;nbsp;값과 상호 작용하는 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;constraint&lt;/b&gt;에 의해 정의된 인터페이스의 일부인 방식으로만 값을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜 선언은 불투명&amp;nbsp;유형을 포함할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 불투명&amp;nbsp;유형을 nonfinal 메서드의 반환 유형으로 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불투명 형식을 반환 형식으로 사용하는 함수는 단일 기본 유형(underlying type)을 공유하는 값을 반환해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환 유형은 함수의 제네릭 유형 파라미터의 부분을 포함할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;b&gt;someFunction&amp;lt;T&amp;gt;()&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;T&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Dictionary&amp;lt;String, T&amp;gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입의 값을 반환할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Metatype Type&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타타입 유형(metatype type)은 클래스 유형, 구조 유형, 열거 유형 및 프로토콜 유형을 포함한 모든 유형의 타입을 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스, 구조체 또는 열거 유형의 메타 유형은 해당 유형의 이름 뒤에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.Type&lt;/b&gt;이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임에 프로토콜을 준수해서 구체적인 유형이 아닌 프로토콜 유형의 메타타입은 해당 프로토콜의 이름 다음에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.Protocol&lt;/b&gt;이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeClass&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스 타입의 메타타입은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeClass .Type&lt;/b&gt;입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SomeProtocol&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로토콜의 메타타입은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeProtocol.Protocol&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;self&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;접미사를 사용해서 type을 값으로 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeClass.self&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeClass&lt;/b&gt;의 인스턴스가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeClass&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자신을 리턴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SomeProtocol.self&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeProtocol&lt;/b&gt;을 런타임에 준수하는 유형의 인스턴스가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SomeProtocol&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자신을 리턴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예에서 알 수 있듯이 유형의 인스턴스로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;type(of:)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 호출하여 해당 인스턴스의 동적 런타임 유형에 값으로 액세스할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class SomeBaseClass {
    class func printClassName() {
        print(&quot;SomeBaseClass&quot;)
    }
}
class SomeSubClass: SomeBaseClass {
    override class func printClassName() {
        print(&quot;SomeSubClass&quot;)
    }
}
let someInstance: SomeBaseClass = SomeSubClass()
// The compile-time type of someInstance is SomeBaseClass,
// and the runtime type of someInstance is SomeSubClass
type(of: someInstance).printClassName()
// Prints &quot;SomeSubClass&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Initializer 식을 사용하여 해당 형식의 메타 형식 값으로 형식의 인스턴스를 구성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 인스턴스의 경우 호출되는 이니셜라이저를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;required&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드로 표시하거나 전체 클래스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;final&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드로 표시해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;class AnotherSubClass: SomeBaseClass {
    let string: String
    required init(string: String) {
        self.string = string
    }
    override class func printClassName() {
        print(&quot;AnotherSubClass&quot;)
    }
}
let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: &quot;some string&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Self Type&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Self&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;유형은 구체적인 유형이 아닙니다. 해당 유형의 이름을 알거나 반복하지 않고 현재 유형을 편리하게 지시할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜 선언 내에서 혹은 프로토콜 멤버 선언에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Self&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입은 그 프로토콜을 준수하는 타입(eventual type)을 지시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체, 클래스, 열거형 선언에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Self&lt;/b&gt;타입은 선언에서 소개한 타입을 지시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입의 멤버를 위한 정의의 내부에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Self&lt;/b&gt;는 그 타입을 지시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 선언의 멤버의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Self&lt;/b&gt;는 다음을 나타냅니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드의 리턴 타입&lt;/li&gt;
&lt;li&gt;읽기 전용 서브스크립트의 리턴 타입&lt;/li&gt;
&lt;li&gt;읽기 전용 computed 프로퍼티의 타입&lt;/li&gt;
&lt;li&gt;메서드의 바디&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 리턴 타입이 Self인 인스턴스 메서드를 다음에서 보여줍니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;class Superclass {
    func f() -&amp;gt; Self { return self }
}
let x = Superclass()
print(type(of: x.f()))
// Prints &quot;Superclass&quot;

class Subclass: Superclass { }
let y = Subclass()
print(type(of: y.f()))
// Prints &quot;Subclass&quot;

let z: Superclass = Subclass()
print(type(of: z.f()))
// Prints &quot;Subclass&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;z의 타입이 컴파일 타임에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Superclass&lt;/b&gt;가 아니라 런타임의 타입인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Subclass&lt;/b&gt;임을 마지막 예시에서 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nested 타입 선언에서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Self&lt;/b&gt;는 가장 안쪽 타입을 가리킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Self타입은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;type(of:)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수의 타입꽈 같은 유형입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Self.someStaticMember&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 써서 현재 타입의 멤버에 접근 하는 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;type(of: self).someStaticMember&lt;/b&gt;와 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/ReferenceManual/Types.html#grammar_opaque-type&quot;&gt;https://docs.swift.org/swift-book/ReferenceManual/Types.html#grammar_opaque-type&lt;/a&gt;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>Metatype</category>
      <category>opaque</category>
      <category>Protocol</category>
      <category>Self</category>
      <category>some</category>
      <category>Type</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/157</guid>
      <comments>https://baechukim.tistory.com/157#entry157comment</comments>
      <pubDate>Wed, 21 Dec 2022 00:27:34 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(6) - Sendable Types</title>
      <link>https://baechukim.tistory.com/156</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;태스크 및 액터를 통해 프로그램을 안전하게 동시에 실행할 수 있는 부분으로 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크 및 액터의 인스턴스 내부에서 변수 및 프로퍼티 같이 가변 상태를 포함하는 프로그램 부분을 동시성 도메인(&lt;i&gt;&lt;b&gt;concurrency domain&lt;/b&gt;&lt;/i&gt;)이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 유형의 데이터는 데이터가 가변 상태를 포함하기 때문에 동시성 도메인들 사이에서 공유될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 오버래핑 접근에 대해서는 보호되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 동시성 도메인에서 다른 도메인으로 공유할 수 있는 타입을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;sendable&lt;/b&gt;&lt;/i&gt;이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 액터 메서드를 호출할 때 인수로 전달되거나 작업의 결과로 반환될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장의 앞부분에 있는 예제에서는 동시성 도메인 간에 전달되는 데이터에 대해 항상 안전하게 공유할 수 있는 단순한 값 유형을 사용하기 때문에 전송 가능성(sendability)에 대해 설명하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대조적으로, 일부 유형은 동시성 도메인을 통과하는 것이 안전하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 변경 가능한 프로퍼티를 포함하고 이러한 프로퍼티에 대한 액세스를 직렬화하지 않는 클래스는 다른 태스크 간에 해당 클래스의 인스턴스를 전달할 때 예측할 수 없고 잘못된 결과를 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;sendable&lt;/b&gt;&lt;/i&gt;프로토콜을 선언하여 유형을 전송 가능하도록 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 프로토콜에는 코드 요구 사항이 없지만 Swift가 적용하는 의미론적(semantic) 요구 사항이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 유형을 전송할 수 있는 세 가지 방법이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 유형이며 변경 가능한 상태는 다른 전송 가능한 데이터(예: sendable 저장 프로퍼티가 있는 구조체 또는 sendable 관련 값이 있는 열거형)로 구성됩니다.&lt;/li&gt;
&lt;li&gt;변경 가능한 상태가 없으며 변경 불가능한 상태는 다른 sendable 데이터(예: 읽기 전용 프로퍼티만 있는 구조체 또는 클래스)로 구성됩니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;&lt;b&gt;@MainActor&lt;/b&gt;&lt;/i&gt;로 표시된 클래스 또는 특정 스레드 또는 대기열에서 해당 프로퍼티에 대한 액세스를 직렬화하는 클래스와 같이 변경 가능한 상태의 안전을 보장하는 코드를 가지는 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sendable 프로퍼티만 가진 구조체나 sendable 연관 값만 가지는 열거형 처럼 몇몇 타입은 항상 sendable 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;struct TemperatureReading: Sendable {
    var measurement: Int
}

extension TemperatureLogger {
    func addReading(from reading: TemperatureReading) {
        measurements.append(reading.measurement)
    }
}

let logger = TemperatureLogger(label: &quot;Tea kettle&quot;, measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;TemperatureReading&lt;/b&gt;&lt;/i&gt;은 전송 가능한 속성만 있는 구조체이며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;public&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;@usableFromInline&lt;/b&gt;&lt;/i&gt;으로 표시되지 않으므로 암시적으로 전송 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 전송 가능 프로토콜 준수를 암시하는 구조체 버전입니다.&lt;/p&gt;
&lt;pre class=&quot;capnproto&quot;&gt;&lt;code&gt;struct TemperatureReading {
    var measurement: Int
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <category>Sendable</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/156</guid>
      <comments>https://baechukim.tistory.com/156#entry156comment</comments>
      <pubDate>Tue, 20 Dec 2022 01:05:18 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(5) - Actors</title>
      <link>https://baechukim.tistory.com/155</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;태스크를 사용하여 프로그램을 분리된 동시성 조각으로 분할할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크는 서로 격리되어 있으므로 태스크를 동시에 실행하는 것이 안전하지만 때로는 태스크 간에 일부 정보를 공유해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Actor&lt;/b&gt;&lt;/i&gt;를 사용하면 동시 코드 간에 정보를 안전하게 공유할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 마찬가지로 &lt;i&gt;&lt;b&gt;Actor&lt;/b&gt;&lt;/i&gt;도 참조 유형이므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html#ID89&quot;&gt;Classes Are Reference Types&lt;/a&gt;의 값 유형과 참조 유형 비교는 클래스뿐만 아니라 액터도 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 달리 액터는 한 번에 하나의 작업만 변경 가능한 상태에 액세스할 수 있으므로 여러 작업의 코드가 액터의 동일한 인스턴스와 상호 작용하는 것이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 다음은 온도를 기록하는 액터입니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;actor&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드를 사용하여 액터를 소개한 다음, 괄호 쌍으로 액터를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;TemperatureLogger&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;액터에는 액터 외부의 다른 코드가 액세스할 수 있는 속성이 있으며, 액터 내부의 코드만 최대값을 업데이트할 수 있도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;max&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성을 제한합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체 및 클래스와 동일한 초기화 구문을 사용하여 액터의 인스턴스를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터의 프로퍼티 또는 메서드에 액세스할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;await&lt;/b&gt;&lt;/i&gt;를 사용하여 잠재적인 일시 중단 지점을 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;let logger = TemperatureLogger(label: &quot;Outdoors&quot;, measurement: 25)
print(await logger.max)
// Prints &quot;25&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;logger.max&lt;/b&gt;&lt;/i&gt;에 액세스하는 것이 가능한 일시 중단 지점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터는 한 번에 하나의 태스크만 변경 가능한 상태에 액세스할 수 있도록 허용하므로 다른 태스크의 코드가 이미 로거와 상호 작용하는 경우 이 코드는 프로퍼티에 액세스하기 위해 기다리는 동안 일시 중단됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대조적으로, 액터의 일부인 코드는 액터의 프로퍼티에 액세스할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;await&lt;/b&gt;&lt;/i&gt;를 작성하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;TemperatureLogger&lt;/b&gt;&lt;/i&gt;를 새 온도로 업데이트하는 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement &amp;gt; max {
            max = measurement
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;update(with:)&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드가 이미 액터에서 실행되고 있으므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;max&lt;/b&gt;&lt;/i&gt;와 같은 속성에 대한 액세스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;await&lt;/b&gt;&lt;/i&gt;로 표시하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 메서드는 액터들이 한 번에 하나의 작업만 그들의 가변 상태와 상호작용하도록 허용하는 이유 중 하나를 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터의 상태에 대한 일부 업데이트는 일시적으로 불변량을 깨트립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;TemperatureLogger&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;액터는 온도와 최대 온도 리스트를 추적하고 새 측정값을 기록하면 최대 온도를 업데이트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트 도중에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;max&lt;/b&gt;&lt;/i&gt;를 업데이트하기 전에 새로운 측정값을 추가하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;TemperatureLogger&lt;/b&gt;&lt;/i&gt;가 일시적으로 일관되지 않은 상태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 태스크가 동일한 인스턴스와 동시에 상호 작용하지 못하도록 하면 다음 이벤트 시퀀스와 같은 문제가 발생하지 않습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;update(with:)&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 호출합니다. 먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;measurements&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;배열을 업데이트합니다.&lt;/li&gt;
&lt;li&gt;코드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;max&lt;/b&gt;&lt;/i&gt;를 업데이트하기 전에 다른 곳에서 코드가 최대값과 온도 배열을 읽습니다.&lt;/li&gt;
&lt;li&gt;코드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;max&lt;/b&gt;&lt;/i&gt;를 변경하여 업데이트를 마칩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 데이터가 일시적으로 유효하지 않은 상태에서 액터에 대한 접근이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;update(with:)&lt;/b&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출 도중 끼어넣어졌기 때문에 다른곳에서 실행되는 코드는 잘못된 정보를 읽을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 액터를 사용할 때 이 문제를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 그들은 한 번에 하나의 상태에 대한 작업만 허용하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;await&lt;/b&gt;&lt;/i&gt;가 일시 중단 지점을 표시하는 곳에서만 코드가 중단될 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;update(with:)&lt;/b&gt;&lt;/i&gt;에 일시 중단 지점이 없기 때문에 다른 코드는 업데이트 도중 데이터에 액세스할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 인스턴스에서와 같이 액터 외부에서 이러한 프로퍼티에 액세스하려고 하면 컴파일 오류가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(logger.max)  // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터의 프로퍼티 해당 액터의 격리된 로컬 상태의 일부이기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;await&lt;/b&gt;&lt;/i&gt;를 쓰지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;b&gt;logger.max&lt;/b&gt;&lt;/i&gt;에 액세스하는 것은 실패합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위프트는 액터 내부의 코드만이 액터의 로컬 상태에 접근할 수 있음을 보장합다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;i&gt;actor&lt;/i&gt; &lt;i&gt;isolation&lt;/i&gt;&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음글&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://baechukim.tistory.com/156&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://baechukim.tistory.com/156&lt;/a&gt;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>actor</category>
      <category>Concurrency</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/155</guid>
      <comments>https://baechukim.tistory.com/155#entry155comment</comments>
      <pubDate>Thu, 15 Dec 2022 20:32:21 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(4) - Tasks and Task Groups</title>
      <link>https://baechukim.tistory.com/154</link>
      <description>&lt;h1&gt;&lt;strong&gt;Tasks and Task Groups&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;task&lt;/strong&gt;는 프로그램의 일부(part)로 비동기적으로 실행될 수 있는 작업 단위입니다.&lt;/p&gt;
&lt;p&gt;모든 비동기 코드는 일부 작업의 일부로 실행됩니다.&lt;/p&gt;
&lt;p&gt;이전 섹션에서 설명한 &lt;strong&gt;async-let&lt;/strong&gt;구문을 사용하면 하위 작업이 생성됩니다.&lt;/p&gt;
&lt;p&gt;또한 태스크 그룹을 생성하고 해당 그룹에 하위 태스크를 추가하여 우선 순위 및 취소를 보다 효과적으로 제어하고 동적인 수의 태스크를 생성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;태스크는 계층 구조로 정렬됩니다.&lt;/p&gt;
&lt;p&gt;태스크 그룹의 각 태스크에는 동일한 상위 태스크가 있으며 각 태스크에는 하위 태스크가 있을 수 있습니다.&lt;/p&gt;
&lt;p&gt;태스크와 태스크 그룹 간의 명시적인 관계 때문에 이 접근 방식을 구조화된 동시성(&lt;strong&gt;structured concurrency&lt;/strong&gt;)이라고 합니다.&lt;/p&gt;
&lt;p&gt;작업 간의 명시적인 상위-하위 관계를 통해 Swift는 사용자를 위해 취소 전파와 같은 일부 동작을 처리하고 컴파일 시 일부 오류를 탐지할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;await withTaskGroup(of: Data.self) { taskGroup in
    let photoNames = await listPhotos(inGallery: &amp;quot;Summer Vacation&amp;quot;)
    for name in photoNames {
        taskGroup.addTask { await downloadPhoto(named: name) }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;Unstructured Concurrency&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Swift는 이전 섹션에서 설명한 동시성에 대한 구조화된 접근 방식 외에도 구조화되지 않은 동시성도 지원합니다.&lt;/p&gt;
&lt;p&gt;태스크 그룹의 일부인 태스크와 달리 구조화되지 않은 태스크(&lt;strong&gt;unstructured task&lt;/strong&gt;)에는 상위 태스크가 없습니다.&lt;/p&gt;
&lt;p&gt;프로그램에 필요한 모든 방식으로 비정형 작업을 관리할 수 있는 완벽한 유연성을 갖추고 있지만, 정확성에 대해서도 전적으로 책임을 져야 합니다.&lt;/p&gt;
&lt;p&gt;현재 액터에서 실행되는 구조화되지 않은 작업을 만들려면 &lt;strong&gt;Task.init(priority:operation:)&lt;/strong&gt; 이니셜라이저를 호출합니다.&lt;/p&gt;
&lt;p&gt;현재 작업자의 일부가 아닌 구조화되지 않은 작업, 특히 분리된 작업을 만들려면 &lt;strong&gt;Task.detached(priority:operation:)&lt;/strong&gt; 클래스 메서드를 호출합니다.&lt;/p&gt;
&lt;p&gt;이러한 작업은 결과를 기다리거나 취소하는 등 상호 작용할 수 있는 태스크를 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let newPhoto = // ... some photo data ...
let handle = Task {
    return await add(newPhoto, toGalleryNamed: &amp;quot;Spring Adventures&amp;quot;)
}
let result = await handle.value&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;Task Cancellation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Swift 동시성은 cooperative cancellation model을 사용합니다.&lt;/p&gt;
&lt;p&gt;각 태스크는 실행 시 적절한 시점에 취소되었는지 확인하고 적절한 방법으로 취소에 응답합니다.&lt;/p&gt;
&lt;p&gt;작업에 따라 일반적으로 다음 중 하나를 의미합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;#39;CancellationError&amp;#39; 같은 오류를 throw&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nil&lt;/code&gt; 또는 빈 컬렉션을 return&lt;/li&gt;
&lt;li&gt;부분적으로 완료된 작업을 return&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;취소 여부를 확인하려면 작업이 취소된 경우 &lt;code&gt;CancelationError&lt;/code&gt;를 발생시키는 &lt;code&gt;Task.checkCancelation()&lt;/code&gt;을 호출하거나 &lt;code&gt;Task.isCancelled&lt;/code&gt;의 값을 확인하고 자신의 코드로 취소를 처리합니다.&lt;/p&gt;
&lt;p&gt;예를 들어 갤러리에서 사진을 다운로드하는 작업은 부분 다운로드를 삭제하고 네트워크 연결을 닫아야 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;다음글&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;/155&quot;&gt;[iOS] Concurrency(5) - Actors&lt;/a&gt;&lt;/h3&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <category>task</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/154</guid>
      <comments>https://baechukim.tistory.com/154#entry154comment</comments>
      <pubDate>Thu, 15 Dec 2022 00:53:00 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(3) - 비동기 시퀀스, 비동기 함수 병렬 호출</title>
      <link>https://baechukim.tistory.com/153</link>
      <description>&lt;h1&gt;&lt;b&gt;Asynchronous Sequences&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 섹션의 listPhotos(inGallery:) 함수는 배열의 모든 요소가 준비된 후 전체 배열을 한 번에 비동기식으로 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 접근 방식은 비동기 시퀀스(asynchronous sequence)를 사용하여 한 번에 하나의 컬렉션 요소를 대기하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 비동기 시퀀스를 반복(iterating)하는 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예는 일반적인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;for-in&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;루프를 사용하는 대신&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;await&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를&lt;b&gt;for&lt;/b&gt;뒤에 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수 또는 메서드를 호출할 때와 마찬가지로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;await&lt;/b&gt;는 가능한 일시 중단 지점을 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기 루프는 잠재적으로 다음 요소가 사용 가능하기를 기다리는 각 반복의 시작 부분에서 실행을 중단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Sequence&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로토콜을 추가하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;for-in&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;루프를 사용할 수 있는 것과 마찬가지로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;AsyncSequence&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로토콜을 추가하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;for-await-in&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;루프를 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Calling Asynchronous Functions in Parallel&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;await&lt;/b&gt;와 함께 호출하면 코드가 한 번에 하나만 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 코드가 실행되는 동안 호출자는 코드가 완료될 때까지 기다렸다가 다음 코드 행을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 갤러리에서 처음 세 장의 사진을 가져오려면 다음과 같이 downloadPhoto(named:) 함수에 대한 세 번의 호출을 기다릴 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])

let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 접근 방식에는 다음과 같은 중요한 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드가 비동기식이므로 다른 작업이 진행되는 동안에도 downloadPhoto(named:) 는 한 번에 하나만 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 사진은 다음 사진 다운로드가 시작되기 전에 완전히 다운로드됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 각 사진을 개별적으로 다운로드하거나 동시에 다운로드할 수 있으므로 작업을 기다릴 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수를 호출하여 주변 코드와 병렬로 실행하려면 상수를 정의할 때 &lt;b&gt;let&lt;/b&gt; 앞에 &lt;b&gt;async&lt;/b&gt;를 씁니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예에서는 이전 호출이 완료될 때까지 기다리지 않고 downloadPhoto(named:) 세 가지 호출이 모두 시작됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 가능한 시스템 리소스가 충분하면 리소스를 동시에 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 함수의 결과를 대기하기 위해 일시 중단되지 않기 때문에 이러한 함수 호출 중 어떤 것도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;await&lt;/b&gt;로 표시되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;photos&lt;/b&gt;가 정의된 라인까지 실행이 계속됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시점에서 프로그램은 이러한 비동기 호출의 결과를 필요로 하므로 세 개의 사진이 모두 다운로드될 때까지 실행을 일시 중지하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;await&lt;/b&gt;를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 이러한 두 가지 접근 방식의 차이점을 생각해 볼 수 있는 방법입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 줄의 코드가 해당 함수의 결과에 따라 달라지는 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;await를 사용하여 비동기 함수를 호출합니다. 이것은 순차적으로 수행되는 작업을 만듭니다.&lt;/li&gt;
&lt;li&gt;코드에서 나중까지 결과가 필요하지 않을 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;async-let&lt;span&gt;&amp;nbsp;&lt;/span&gt;비동기 함수를 호출합니다. 이것은 병렬로 수행될 수 있는 작업을 만듭니다.&lt;/li&gt;
&lt;li&gt;await&lt;span&gt;&amp;nbsp;&lt;/span&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;async-let&lt;span&gt;&amp;nbsp;&lt;/span&gt;모두 일시 중단된 동안 다른 코드를 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;두 경우 모두 가능한 일시 중단 지점을&lt;span&gt;&amp;nbsp;&lt;/span&gt;await로 표시하여 필요한 경우 비동기 함수가 돌아올 때까지 실행이 일시 중지됨을 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 코드에서 이 두 가지 접근 방식을 혼합할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음글&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;/154&quot;&gt;[iOS] Concurrency(4) - Tasks and Task Groups&lt;/a&gt;&lt;/h3&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/153</guid>
      <comments>https://baechukim.tistory.com/153#entry153comment</comments>
      <pubDate>Thu, 15 Dec 2022 00:11:40 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(2) - 비동기함수 정의와 호출</title>
      <link>https://baechukim.tistory.com/152</link>
      <description>&lt;h1&gt;&lt;b&gt;Defining and Calling Asynchronous Functions&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수 또는 비동기 메서드는 실행 도중 일시 중단될 수 있는 특수한 종류의 함수 또는 메서드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 완료될 때까지 실행되거나 오류를 발생시키거나 절대 return하지 않는 일반적인 동기화 함수 및 메서드와는 대조적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수 또는 메서드는 여전히 이 세 가지 중 하나를 수행하지만, 무언가를 기다리고 있을 때 중간에 일시 중지할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수 또는 메서드의 바디 안에서 실행이 일시 중단될 수 있는 각 위치를 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 또는 메서드가 비동기임을 나타내려면 &lt;b&gt;throws&lt;/b&gt;를 사용하여 던지기 함수를 표시하는 방법과 유사하게 매개 변수 뒤에 &lt;b&gt;async&lt;/b&gt; 키워드를 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 또는 메서드가 값을 반환하는 경우 반환 화살표(-&amp;gt;) 앞에 &lt;b&gt;async&lt;/b&gt;를 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 갤러리에서 사진 이름을 가져오는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;func listPhotos(inGallery name: String) async -&amp;gt; [String] {
    let result = // ... some asynchronous networking code ...
    return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기식 및 던지기 함수나 메서드의 경우 &lt;b&gt;throws&lt;/b&gt; 전에 &lt;b&gt;async&lt;/b&gt;를 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 메서드를 호출하면 해당 메서드가 반환될 때까지 실행이 일시 중단됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 앞에 &lt;b&gt;await&lt;/b&gt;라고 적으면 중단 가능 지점을 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 던지기 함수를 호출할 때 쓰는 &lt;b&gt;try&lt;/b&gt;와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 있을 경우 프로그램 흐름에 변경 가능성을 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기식 메소드 내에서는 다른 비동기식 메소드(서스펜션은 절대 암시적이거나 선제적이지 않음)를 호출할 때만 실행 흐름이 일시 중단됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 가능한 모든 중단 포인트가 &lt;b&gt;await&lt;/b&gt;로 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래 코드는 갤러리에 있는 모든 사진의 이름을 가져온 다음 첫 번째 사진을 표시합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;let photoNames = await listPhotos(inGallery: &quot;Summer Vacation&quot;)
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;listPhotos(inGallery:)와 downloadPhoto(named:) 함수는 모두 네트워크 요청을 수행해야 하므로 완료하는 데 비교적 오랜 시간이 걸릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴 화살표 앞에 &lt;b&gt;async&lt;/b&gt;를 작성하여 둘 다 비동기 상태로 만들면 이 코드가 사진이 준비될 때까지 기다리는 동안 앱의 나머지 코드가 계속 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제의 동시 특성을 이해하기 위해 가능한 실행 순서는 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드는 첫 번째 줄부터 실행되기 시작하여 첫 번째 &lt;b&gt;await&lt;/b&gt;까지 실행됩니다. listPhotos (inGallery:) 함수를 호출하고 해당 함수가 반환될 때까지 기다리는 동안 실행을 일시 중단합니다.&lt;/li&gt;
&lt;li&gt;이 코드의 실행이 일시 중단되는 동안 동일한 프로그램의 일부 다른 동시 코드가 실행됩니다. 예를 들어, 장기간 실행되는 백그라운드 태스크가 새 사진 갤러리 목록을 계속 업데이트할 수 있습니다. 또한 이 코드는 &lt;b&gt;await&lt;/b&gt;로 표시된 다음 일시 중단 지점까지 실행되거나 완료될 때까지 실행됩니다.&lt;/li&gt;
&lt;li&gt;listPhotos(inGallery:)가 반환된 후 이 코드는 해당 지점부터 실행을 계속합니다. &lt;b&gt;photoNames&lt;/b&gt;로 반환된 값을 할당합니다.&lt;/li&gt;
&lt;li&gt;sortedNames과 name을 정의하는 줄은 일반적인 동기 코드입니다. 이 라인에는 await로 표시된 것이 없기 때문에 정지 지점이 없습니다.&lt;/li&gt;
&lt;li&gt;다음 &lt;b&gt;await&lt;/b&gt;는 downloadPhoto(named:) 함수에 대한 호출을 표시합니다. 이 코드는 해당 함수가 반환될 때까지 실행을 다시 일시 중지하여 다른 동시 코드를 실행할 수 있는 기회를 제공합니다.&lt;/li&gt;
&lt;li&gt;downloadPhoto(named:)이 반환되면 반환 값이 사진에 할당된 다음 show(_:)를 호출할 때 인수로 전달됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;await&lt;/b&gt;로 표시된 코드의 일시 중단 가능한 지점은 비동기 함수 또는 메서드가 반환될 때까지 기다리는 동안 현재 코드 조각이 실행을 일시 중지할 수 있음을 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 *yielding the thread(*스레드 넘겨주기)이라고도 하는데, 백그라운드에서는 Swift가 현재 스레드에서 코드 실행을 일시 중단하고 대신 해당 스레드에서 다른 코드를 실행하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;await&lt;/b&gt; 코드는 실행을 일시 중단할 수 있어야 하므로 프로그램의 특정 위치에서만 비동기 함수 또는 메서드를 호출할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기 함수, 메서드 또는 프로퍼티에 있는 코드&lt;/li&gt;
&lt;li&gt;@main으로 표시된 구조체, 클래스, 열거형의 static main() 메서드의 코드&lt;/li&gt;
&lt;li&gt;Unstructed Concurrency에서 보게 될 unstructured child task에 있는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일시 중단 가능한 지점 사이의 코드는 다른 동시 코드의 방해 없이 순차적으로 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래 코드는 한 갤러리에서 다른 갤러리로 사진을 이동합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;let firstPhoto = await listPhotos(inGallery: &quot;Summer Vacation&quot;)[0]
add(firstPhoto toGallery: &quot;Road Trip&quot;)
// At this point, firstPhoto is temporarily in both galleries.
remove(firstPhoto fromGallery: &quot;Summer Vacation&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add(: to Gallery:) 호출과 remove(: from Gallery:) 호출 사이에 다른 코드가 실행될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기간 동안 첫 번째 사진이 두 갤러리에 모두 나타나 앱의 불변량 중 하나를 일시적으로 깨뜨립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드 덩어리가 나중에 await를 절대 가지지 않아야 한다는 것을 명확하게 하기 위해 해당 코드를 동기화 함수로 리팩터링할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;func move(_ photoName: String, from source: String, to destination: String) {
    add(photoName, to: destination)
    remove(photoName, from: source)
}
// ...
let firstPhoto = await listPhotos(inGallery: &quot;Summer Vacation&quot;)[0]
move(firstPhoto, from: &quot;Summer Vacation&quot;, to: &quot;Road Trip&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예에서는 move(_:from:to:)함수가 동기화되어 있으므로 중단점을 절대 포함할 수 없음을 보장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 이 함수에 동시 코드를 추가하여 일시 중단 가능성이 있는 지점을 도입하려고 하면 버그를 도입하는 대신 컴파일 타임 오류가 발생합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NOTE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task.sleep(until:tolerance:clock:) 메서드는 동시성이 어떻게 작동하는지 배우기 위해 간단한 코드를 작성할 때 유용합니다. 이 메서드는 아무 작업도 수행하지 않지만 지정된 시간(나노초) 동안 기다렸다가 다시 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sleep(until:tolerance:clock:) 을 사용하여 네트워크 작업 대기를 시뮬레이션하는 listPhotos(inGallery:) 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;func listPhotos(inGallery name: String) async throws -&amp;gt; [String] {
    try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
    return [&quot;IMG001&quot;, &quot;IMG99&quot;, &quot;IMG0404&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;/153&quot;&gt;[iOS] Concurrency(3) - 비동기 시퀀스, 비동기 함수 병렬 호출&lt;/a&gt;&lt;/h3&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/152</guid>
      <comments>https://baechukim.tistory.com/152#entry152comment</comments>
      <pubDate>Wed, 14 Dec 2022 23:50:50 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Concurrency(1) - Concurrency란</title>
      <link>https://baechukim.tistory.com/151</link>
      <description>&lt;h1&gt;&lt;b&gt;Concurrency(1) - Concurrency란&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift는 비동기 및 병렬 코드를 작성할 수 있도록 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 코드는 한 번에 하나의 프로그램만 실행되지만 나중에 일시 중단했다가 다시 시작할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램에서 코드를 일시 중단했다가 다시 시작하면 네트워크를 통해 데이터 가져오기 또는 파일 파싱과 같은 장기 실행 작업을 계속하면서 UI 업데이트와 같은 단기 작업을 계속 진행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 코드는 여러 코드 조각이 동시에 실행되는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 4코어 프로세서가 있는 컴퓨터는 4개의 코드 조각을 동시에 실행할 수 있으며 각 코어는 작업 중 하나를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 및 비동기 코드를 사용하는 프로그램은 한 번에 여러 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로그램은 외부 시스템을 기다리는 작업을 일시 중단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 메모리 안전한 방식으로 이 코드를 쉽게 작성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 또는 비동기 코드의 추가적인 스케줄링 유연성은 또한 복잡성 증가의 비용을 수반합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift를 사용하면 컴파일 타임 체킹을 사용할 수 있는 방식으로 의도를 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 액터를 사용하여 변경 가능한 상태에 안전하게 액세스할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 속도가 느리거나 버그가 많은 코드에 동시성을 추가한다고 해서 그것이 빠르거나 정확해진다는 보장은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 동시성을 추가하면 코드를 디버깅하기가 더 어려워질 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Swift의 언어 수준에서 동시성이 필요한 코드를 위한 concurrency는 컴파일 시 문제를 파악하는 데 Swift가 도움을 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장의 나머지 부분에서는 비동기 및 병렬 코드의 일반적인 조합을 나타내기 위해 동시성(concurrency)이라는 용어를 사용합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 동시 코드를 작성한 적이 있는 경우 스레드 작업에 익숙해질 수 있습니다. Swift의 동시성 모델은 스레드 위에 구축되지만 사용자는 스레드와 직접 상호 작용하지 않습니다. Swift의 비동기 함수는 실행 중인 스레드를 포기할 수 있으므로 첫 번째 함수가 차단되는 동안 해당 스레드에서 다른 비동기 함수를 실행할 수 있습니다. 비동기 함수가 다시 시작될 때 Swift는 해당 함수가 실행될 스레드에 대해 어떠한 보증도 하지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift의 언어 지원을 사용하지 않고 동시 코드를 작성할 수 있지만, 코드를 읽기가 더 어려운 경향이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 다음 코드는 사진 이름 목록을 다운로드하고 해당 목록의 첫 번째 사진을 다운로드한 다음 사용자에게 해당 사진을 표시합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;listPhotos(inGallery: &quot;Summer Vacation&quot;) { photoNames in
    let sortedNames = photoNames.sorted()
    let name = sortedNames[0]
    downloadPhoto(named: name) { photo in
        show(photo)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 간단한 경우에도 코드를 일련의 completion handlers로 작성해야 하므로 중첩된 클로저를 작성하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스타일에서는 중첩이 더 많은 복잡한 코드를 빠르게 다루기 어려워질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;/152&quot;&gt;[iOS] Concurrency(2) - 비동기함수 정의와 호출&lt;/a&gt;&lt;/h3&gt;</description>
      <category>Swift</category>
      <category>Concurrency</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/151</guid>
      <comments>https://baechukim.tistory.com/151#entry151comment</comments>
      <pubDate>Wed, 14 Dec 2022 23:47:36 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] UIViewPropertyAnimator / iOS 애니메이션 및 주의사항</title>
      <link>https://baechukim.tistory.com/150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UIViewPropertyAnimator 변수를 만듭니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223110624&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var animator: UIViewPropertyAnimator? = nil&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223198439&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
    self.aView.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
    self.aView.backgroundColor = .systemRed
}

animator?.addCompletion { position in
    position.printPosition(#function)
}
animator?.startAnimation()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addCompletion 을 이용해서 완료 후 동작을 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;startAnimation 을 이용해서 애니메이션을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션이 동작하는 중에 애니메이터를 다시 선언하면 애니메이션이 이어서 재생됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEKCXy/btrP3lgzP61/KIrlV0vZnjhyQ57gcyHq7k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEKCXy/btrP3lgzP61/KIrlV0vZnjhyQ57gcyHq7k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEKCXy/btrP3lgzP61/KIrlV0vZnjhyQ57gcyHq7k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bEKCXy/btrP3lgzP61/KIrlV0vZnjhyQ57gcyHq7k/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;825&quot; data-filename=&quot;ezgif.com-gif-maker.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223258921&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.addAnimations {
    self.aView.frame = CGRect(origin: self.aView.frame.origin, size: CGSize(width: 90, height: 90))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addAnimations를 이용해서 애니메이션을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 되돌릴 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223292836&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.isReversed = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker-2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdpD4u/btrP44q6yzK/eQ7eMKUpkKGA5CeR488bkK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdpD4u/btrP44q6yzK/eQ7eMKUpkKGA5CeR488bkK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdpD4u/btrP44q6yzK/eQ7eMKUpkKGA5CeR488bkK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bdpD4u/btrP44q6yzK/eQ7eMKUpkKGA5CeR488bkK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;825&quot; data-filename=&quot;ezgif.com-gif-maker-2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 중단할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223341172&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.pauseAnimation()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 계속합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223371441&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.continueAnimation(withTimingParameters: nil, durationFactor: 3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timing파라미터와 시간을 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 멈춥니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223561584&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.stopAnimation(true)
animator?.stopAnimation(false)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;true 일 때는 애니메이션 state가&amp;nbsp;&lt;b&gt;inactive&lt;/b&gt;&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;false 일 때는 애니메이션&amp;nbsp;state가&lt;span&gt;&amp;nbsp;&lt;b&gt;stopped&lt;/b&gt;&amp;nbsp;됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 종료합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667223403537&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.finishAnimation(at: .start)
animator?.finishAnimation(at: .current)
animator?.finishAnimation(at: .end)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UIViewAnimatingPosition을 사용해서 종료합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667223934321&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animator?.pausesOnCompletion = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은&amp;nbsp;NO입니다.&amp;nbsp;애니메이터가&amp;nbsp;완료&amp;nbsp;시&amp;nbsp;.inactive&amp;nbsp;상태로&amp;nbsp;전환하지&amp;nbsp;않고&amp;nbsp;일시&amp;nbsp;중지할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;기능을&amp;nbsp;제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(addCompletion 이 불리지 않습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의사항 / crash 발생하는 경우&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;pausesOnCompletion = true 이후에 animator를 release 하는 경우&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread 1: &quot;It is an error to release a property animator that has paused on completion. Property animators must either finish animating or be explicitly stopped and finished before they can be released.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 시 일시 중지된 property animator를 해제하는 것은 오류입니다. property animator를 해제하려면 먼저 애니메이션을 완료(finished)하거나 명시적으로 중지(stopped)하고 완료해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;pauseAnimation() 이후에 animator를 release 하는 경우&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;stopAnimation(&lt;b&gt;false&lt;/b&gt;) 이후에 animator를 release 하는 경우&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread 1: &quot;It is an error to release a paused or stopped property animator. Property animators must either finish animating or be explicitly stopped and finished before they can be released.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일시 중지되거나 중지된 property animator를 해제하는 것은 오류입니다. property animator는 애니메이션을 끝내(finished)거나 명시적으로 중지(stopped)하고 끝내야 릴리스할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;animator가 runnging 중일 때,&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;finishAnimation(at: ) 호출 하는 경우&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread 1: &quot;finishAnimationAtPosition: should only be called on a stopped animator!&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;finishAnimationAtPosition: 는 중지(stopped)된 animator에서만 호출되어야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>iOS</category>
      <category>UIViewPropertyAnimator</category>
      <author>Kim_Baechu</author>
      <guid isPermaLink="true">https://baechukim.tistory.com/150</guid>
      <comments>https://baechukim.tistory.com/150#entry150comment</comments>
      <pubDate>Mon, 31 Oct 2022 22:52:38 +0900</pubDate>
    </item>
  </channel>
</rss>