<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>^(코딩캣)^ = @&amp;quot;코딩&amp;quot;하는 고양이;</title>
    <link>https://codingcatkr.tistory.com/</link>
    <description>개발괴발로 작성하는 개발자 블로그.</description>
    <language>ko</language>
    <pubDate>Mon, 20 Jul 2026 04:29:38 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코딩집사</managingEditor>
    <image>
      <title>^(코딩캣)^ = @&amp;quot;코딩&amp;quot;하는 고양이;</title>
      <url>https://tistory1.daumcdn.net/tistory/2939845/attach/f6f65885fed349008a522ae890266018</url>
      <link>https://codingcatkr.tistory.com</link>
    </image>
    <item>
      <title>[Classic ASP] SubFolders, Files : 로컬 내 파일과 폴더 목록 얻기</title>
      <link>https://codingcatkr.tistory.com/165</link>
      <description>&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;h1&gt;Scripting.FileSystemObject 객체 할당 및 해제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Scripting.FileSystemObject&lt;/code&gt; 객체는 컴퓨터(서버) 내 파일 시스템에 접근할 수 있는 메소드들을 제공하는 객체이다(출처: &lt;a href=&quot;https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/filesystemobject-object&quot;&gt;https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/filesystemobject-object&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;asp routeros&quot;&gt;&lt;code&gt;&amp;lt;%
 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
 ' ... '
 Set fileSystemObject = Nothing
 %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;GetFolder 메소드로 폴더에 대한 객체 얻기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;[Scripting.FileSystemObject].GetFolder&lt;/code&gt; 메소드는 폴더를 특정하는 경로를 받아서 이에 대한 객체를 반환한다.&lt;/p&gt;
&lt;pre class=&quot;asp routeros&quot;&gt;&lt;code&gt;&amp;lt;%
 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
 Set folder           = fileSystemObject.GetFolder(경로)
 ' ... '
 Set folder           = Nothing
 Set fileSystemObject = Nothing
 %&amp;gt;&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;code&gt;Server.MapPath&lt;/code&gt; 메소드를 조합하면 다음과 같이 ASP 파일이 위치한 로컬 경로를 기준으로 한 상대 경로도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;asp routeros&quot;&gt;&lt;code&gt;&amp;lt;%
 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
 Set folder           = fileSystemObject.GetFolder(Server.MapPath(&quot;.&quot;))
 ' ... '
 Set folder           = Nothing
 Set fileSystemObject = Nothing
 %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;현재 지정된 폴더의 절대 경로 얻기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 얻은 폴더 객체의 절대 경로는 다음과 같이 &lt;code&gt;Path&lt;/code&gt; 프로퍼티로 구할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asp gcode&quot;&gt;&lt;code&gt;&amp;lt;%=folder.Path%&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;현재 지정된 폴더 속 하위 폴더의 개수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 얻은 폴더 객체에서 하위 폴더가 몇 개인지를 알기 위해 다음과 같이 &lt;code&gt;SubFolders.Count&lt;/code&gt; 프로퍼티를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asp mel&quot;&gt;&lt;code&gt;&amp;lt;%=folder.SubFolders.Count%&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;현재 지정된 폴더 속 파일의 개수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 얻은 폴더 객체에서 파일이 몇 개인지를 알기 위해 다음과 같이 &lt;code&gt;SubFolders.Count&lt;/code&gt; 프로퍼티를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asp mel&quot;&gt;&lt;code&gt;&amp;lt;%=folder.Files.Count%&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;폴더 속 폴더를 하나씩 순회하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SubFolders&lt;/code&gt; 프로퍼티는 그 자체가 Collection 객체이기도 하므로 다음과 같이 &lt;code&gt;For Each&lt;/code&gt; 루프를 돌려서 하위 폴더들을 하나씩 순회할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asp vbscript&quot;&gt;&lt;code&gt;&amp;lt;%
 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
 Set folder           = fileSystemObject.GetFolder(Server.MapPath(&quot;.&quot;))
 
 For Each f in folder.SubFolders ' f is also Folder object
 	' ... '
 Next
 
 Set folder           = Nothing
 Set fileSystemObject = Nothing
 %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;폴더 속 파일을 하나씩 순회하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 &lt;code&gt;Files&lt;/code&gt; 프로퍼티도 그 자체가 Collection 객체이기도 하므로 다음과 같이 &lt;code&gt;For Each&lt;/code&gt; 루프를 돌려서 폴더 속 파일들을 하나씩 순회할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asp vbscript&quot;&gt;&lt;code&gt;&amp;lt;%
 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
 Set folder           = fileSystemObject.GetFolder(Server.MapPath(&quot;.&quot;))
 
 For Each f in folder.Files ' f is File object
 	' ... '
 Next
 
 Set folder           = Nothing
 Set fileSystemObject = Nothing
 %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;서버 로컬 경로 내 파일 및 폴더 순회 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 asp 파일 자신이 위치한 폴더 내 파일과 하위 폴더들을 순회하여 클라이언트에게 보여주는 예이다.&lt;/p&gt;
&lt;pre class=&quot;asp vbscript-html&quot;&gt;&lt;code&gt;&amp;lt;%@ language=&quot;VBScript&quot; codepage=&quot;65001&quot; %&amp;gt;
&amp;lt;%
 Session.CodePage = &quot;65001&quot;
 Response.ContentType = &quot;text/html&quot;
 Response.AddHeader &quot;Content-Type&quot;, &quot;text/html;charset=UTF-8&quot;
 Response.Charset = &quot;UTF-8&quot;
 %&amp;gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&amp;gt;
&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;codingCat.kr&amp;lt;/title&amp;gt;
		&amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html;charset=UTF-8&quot;&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;%
		 fileSystemPath = Server.MapPath(&quot;.&quot;)
		 
		 Dim fileSystemObject
		 Dim fileSystemFolder
		 
		 Set fileSystemObject = Server.CreateObject(&quot;Scripting.FileSystemObject&quot;)
		 Set fileSystemFolder = fileSystemObject.GetFolder(fileSystemPath)
		 %&amp;gt;
		&amp;lt;p&amp;gt;Local Path: &amp;lt;code&amp;gt;&amp;lt;%=fileSystemFolder.Path%&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
		&amp;lt;p&amp;gt;FOLDER(S): &amp;lt;code&amp;gt;&amp;lt;%=fileSystemFolder.SubFolders.Count%&amp;gt;&amp;lt;/code&amp;gt; item(s).&amp;lt;/p&amp;gt;
		&amp;lt;ul&amp;gt;
			&amp;lt;% For Each f in fileSystemFolder.SubFolders %&amp;gt;&amp;lt;li&amp;gt;[&amp;lt;%=f.Name%&amp;gt;]&amp;lt;/li&amp;gt;&amp;lt;% Next %&amp;gt;
		&amp;lt;/ul&amp;gt;
		&amp;lt;p&amp;gt;FILE(S): &amp;lt;code&amp;gt;&amp;lt;%=fileSystemFolder.Files.Count%&amp;gt;&amp;lt;/code&amp;gt; item(s).&amp;lt;/p&amp;gt;
		&amp;lt;ul&amp;gt;
			&amp;lt;% For Each f in fileSystemFolder.Files %&amp;gt;&amp;lt;li&amp;gt;&amp;lt;%=f.Name%&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;% Next %&amp;gt;
		&amp;lt;/ul&amp;gt;
		&amp;lt;%
		 Set fileSystemFolder = Nothing
		 Set fileSystemObject = Nothing
		 %&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Windows 2000 Server-2023-03-19-09-56-16.png&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vGDjC/btr4vHC704c/Sz6pPp2ymyIzLenmWj6sNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vGDjC/btr4vHC704c/Sz6pPp2ymyIzLenmWj6sNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vGDjC/btr4vHC704c/Sz6pPp2ymyIzLenmWj6sNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvGDjC%2Fbtr4vHC704c%2FSz6pPp2ymyIzLenmWj6sNK%2Fimg.png&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;781&quot; height=&quot;300&quot; data-filename=&quot;edited_Windows 2000 Server-2023-03-19-09-56-16.png&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Windows Me-2023-03-19-09-56-33.png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tDT8f/btr4LIfOfrP/cL8fi24xz1jVwaMDe0NaW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tDT8f/btr4LIfOfrP/cL8fi24xz1jVwaMDe0NaW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tDT8f/btr4LIfOfrP/cL8fi24xz1jVwaMDe0NaW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtDT8f%2Fbtr4LIfOfrP%2FcL8fi24xz1jVwaMDe0NaW1%2Fimg.png&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;1152&quot; height=&quot;858&quot; data-filename=&quot;edited_Windows Me-2023-03-19-09-56-33.png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Common Gateway Interface/ASP &amp;amp; ASP.NET</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/165</guid>
      <comments>https://codingcatkr.tistory.com/165#entry165comment</comments>
      <pubDate>Sun, 19 Mar 2023 08:41:03 +0900</pubDate>
    </item>
    <item>
      <title>[Classic ASP] Charset, CodePage: 웹 페이지를 UTF-8 인코딩으로 내보내기</title>
      <link>https://codingcatkr.tistory.com/164</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ASP(Classic ASP)에서 UTF-8 인코딩으로 웹 페이지를 내보내도록 다음과 같이 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고: CodePage 65001과 UTF-8 인코딩은 같은 말이다(출처: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers&quot;&gt;https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers&lt;/a&gt;)&lt;/p&gt;
&lt;pre class=&quot;asp erb&quot;&gt;&lt;code&gt;&amp;lt;%@ language=&quot;VBScript&quot; codepage=&quot;65001&quot; %&amp;gt;
&amp;lt;%
 ' 세션에서 사용할 코드 페이지도 UTF-8(65001)로 세팅한다. '
 Session.CodePage = &quot;65001&quot;
 ' Response 객체에서 UTF-8 웹 페이지임을 세팅한다. '
 Response.ContentType = &quot;text/html&quot;
 Response.Charset = &quot;UTF-8&quot;
 ' HTTP 패킷 헤더 부분에서 이 페이지는 UTF-8(65001)로 인코드되었음을 세팅한다. '
 Response.AddHeader = &quot;Content-Type&quot;, &quot;text/html;charset=UTF-8&quot;
 %&amp;gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&amp;gt; 
&amp;lt;html&amp;gt; 
	&amp;lt;head&amp;gt; 
		&amp;lt;title&amp;gt; codingCat.kr&amp;lt;/title&amp;gt; 
        &amp;lt;!-- HTML 파일 내 헤더에서도 UTF-8 인코딩임을 한 번 더 표시 --&amp;gt;
		&amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html;charset=UTF-8&quot; /&amp;gt;
	&amp;lt;/head&amp;gt; 
	&amp;lt;body&amp;gt; 
		&amp;lt;!-- HTML 본문 --&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Common Gateway Interface/ASP &amp;amp; ASP.NET</category>
      <category>ASP</category>
      <category>ClassicASP</category>
      <category>VBScript</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/164</guid>
      <comments>https://codingcatkr.tistory.com/164#entry164comment</comments>
      <pubDate>Sun, 19 Mar 2023 08:16:19 +0900</pubDate>
    </item>
    <item>
      <title>[Classic ASP] Server.MapPath : ASP 파일을 기준으로 한 로컬 경로 반환</title>
      <link>https://codingcatkr.tistory.com/163</link>
      <description>&lt;h1&gt;Server.MapPath&lt;/h1&gt;
&lt;pre class=&quot;asp arduino&quot;&gt;&lt;code&gt;Server.MapPath(경로)&lt;/code&gt;&lt;/pre&gt;
&lt;dl&gt;
&lt;dt&gt;경로&lt;/dt&gt;
&lt;dd&gt;로컬 상대경로 또는 URL 가상경로&lt;/dd&gt;
&lt;dt&gt;* 반환값 *&lt;/dt&gt;
&lt;dd&gt;상대경로 또는 URL 가상경로에 해당하는 서버 내(로컬) 절대경로를 반환&lt;/dd&gt;
&lt;/dl&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예: asp 파일의 서버 내 로컬 경로를 얻기&lt;/h2&gt;
&lt;pre class=&quot;asp vbscript-html&quot;&gt;&lt;code&gt;&amp;lt;%@ language=&quot;VBScript&quot; codepage=&quot;65001&quot; %&amp;gt;
&amp;lt;%
 Session.CodePage = &quot;65001&quot;
 Response.ContentType = &quot;text/html&quot;
 Response.AddHeader &quot;Content-Type&quot;, &quot;text/html;charset=UTF-8&quot;
 Response.Charset = &quot;UTF-8&quot;
 %&amp;gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&amp;gt;
&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;codingCat.kr&amp;lt;/title&amp;gt;
		&amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;% 'Finding the physical path to a file %&amp;gt;
		&amp;lt;p&amp;gt;&amp;lt;%=Server.MapPath(&quot;.&quot;)%&amp;gt;&amp;lt;/p&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Windows Me-2023-03-18-19-28-46.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lTiZD/btr4vIIzjex/Ji6wk0WaIsYbg9yCp8RRm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lTiZD/btr4vIIzjex/Ji6wk0WaIsYbg9yCp8RRm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lTiZD/btr4vIIzjex/Ji6wk0WaIsYbg9yCp8RRm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlTiZD%2Fbtr4vIIzjex%2FJi6wk0WaIsYbg9yCp8RRm1%2Fimg.png&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;768&quot; height=&quot;319&quot; data-filename=&quot;edited_Windows Me-2023-03-18-19-28-46.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Windows Me-2023-03-18-19-30-03.png&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDXy9w/btr4xLYQHeI/vji0r6GN4OUs2KM0xFkIgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDXy9w/btr4xLYQHeI/vji0r6GN4OUs2KM0xFkIgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDXy9w/btr4xLYQHeI/vji0r6GN4OUs2KM0xFkIgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDXy9w%2Fbtr4xLYQHeI%2Fvji0r6GN4OUs2KM0xFkIgK%2Fimg.png&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;769&quot; height=&quot;168&quot; data-filename=&quot;edited_Windows Me-2023-03-18-19-30-03.png&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Common Gateway Interface/ASP &amp;amp; ASP.NET</category>
      <category>ASP</category>
      <category>ASPClassic</category>
      <category>VBScript</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/163</guid>
      <comments>https://codingcatkr.tistory.com/163#entry163comment</comments>
      <pubDate>Sat, 18 Mar 2023 19:47:41 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Thread-safe한 싱글톤(singleton) 패턴과 그 사용법(3) [完]</title>
      <link>https://codingcatkr.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 Sachithra Siriwardhane 님의 글 &lt;a href=&quot;https://sachithrasiriwardhane.medium.com/thread-safe-singletons-and-their-usage-in-swift-c992d34d85dd&quot;&gt;Thread-safe singletons and their usage in Swift&lt;/a&gt;를 바탕으로 작성되었습니다.&lt;/p&gt;
&lt;h1&gt;Thread-safe한 싱글톤(singleton)과 그 사용법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자라면 다들 &amp;lsquo;싱글톤 패턴(singleton pattern)&amp;rsquo;에 대해 알고 있을 것이고 사용해 보셨을 것이며, iOS 어플리케이션에서 구현이 가능하실 것입니다. 이 글은 입문자 또는 싱글톤 디자인 패턴(design pattern)을 적절하게 사용하기 위한 배경 지식 향상을 원하는 분들에게 도움이 되어 드리고자 작성되었습니다. 이 글에서는 다음과 같은 주제들을 다뤄볼 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;./143&quot;&gt;싱글톤 패턴의 사용: 해야 할 것과 하지 말아야 할 것&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./144&quot;&gt;Swift/Objective-C에서 싱글톤 패턴의 작성&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./145&quot;&gt;&lt;strong&gt;동시성 문제 해결 및 thread-safe하게 싱글톤 패턴을 작성하기&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동시성 문제 및 thread-safe하게 싱글톤 패턴을 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서 &lt;code&gt;let&lt;/code&gt; 키워드로 선언되는 식별자는 상수입니다. 그러므로 읽기 전용이면서 thread-safe입니다. &lt;code&gt;var&lt;/code&gt;로 선언되는 식별자는 수정이 가능하지만 그 데이터 타입이 특별히 thread-safe하면서 수정 가능하게 지정되지 않았다면 thread-safe하지 않습니다. Swift에서 &lt;code&gt;Array&lt;/code&gt;나 &lt;code&gt;Dictionary&lt;/code&gt; 같은 데이터 타입들은 수정 가능하더라도 thread-safe하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 &lt;code&gt;Dictionary&lt;/code&gt;는 수정 가능합니다. 또한 여러 쓰레드에서 동시에 읽기만 할 때는 아무 문제가 없습니다. 하지만 다른 쓰레드들이 읽기만 하고 있는데 한 쓰레드가 수정하려 한다면 그 구조가 안전하지 않게 됩니다. 위의 예제에서 보인 싱글톤은 그러한 문제가 발생하는 것을 막아주지 못합니다. 이에 대한 해답은 &lt;code&gt;DispatchQueue&lt;/code&gt;에 있습니다. Apple에 따르면 &lt;code&gt;DispatchQueue&lt;/code&gt;의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;여러분의 앱의 메인 쓰레드 또는 백그라운드 쓰레드에서 작업의 동기 또는 동시적 실행을 관리하는 객체(An object that manages the execution of tasks serially or concurrently on your app's main thread or on a background thread).&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DispatchQueue&lt;/code&gt;에 대한 자세한 정보는 다음 Apple 문서를 참조하시기 바랍니다: &lt;a href=&quot;https://developer.apple.com/documentation/dispatch/dispatchqueue&quot; target=&quot;_new&quot;&gt;https://developer.apple.com/documentation/dispatch/dispatchqueue&lt;/a&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;Concurrent Dispatch Queue&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrent Dispatch Queue는 여러 개의 작업들을 한 번에 실행하는데, 호출되는 순서는 작업이 큐(queue)에 추가된 순서에 따릅니다. 하지만 각 작업은 병렬로 실행 중이기 때문에 종료되는 순서는 제각각이 될 수 있습니다. &lt;code&gt;DispatchQueue&lt;/code&gt;는 작업들이 실행되고 있는 쓰레드(thread)들을 관리하는 역할을 합니다.&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;// Swift
func testConcurrentUsage throws {
    let concurrentLabel = &quot;concurrentQueue&quot;
    let concurrentQueue = DispatchQueue(label: concurrentLabel, attributes: .concurrent)
    let iterations = 100
    
    for currentIteration in 1 ... iterations {
        concurrentQueue.async {
            let key = &quot;\(currentIteration)&quot;
            EventLogger.shared.writeToLog(key: key, content: &quot;iteration = \(currentIteration)&quot;)
        }
    }
    
    while EventLogger.shared.readLog(for: &quot;\(iterations)&quot;) != String(&quot;iteration = \(currentIteration)&quot;) {
        // a blocker
    }
    
    expect.fulfill()
    
    waitForExpectations(timeout: 5) { (error) in
        XCTAssertNil(error, &quot;Concurrent expectation failed&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 이 글에서 앞서 작성한 싱글톤을 동시적으로 사용해보기 위해 작성된 단위 테스트를 보여주고 있습니다. 먼저 &lt;code&gt;concurrentQueue&lt;/code&gt;는 &lt;code&gt;writeToLog&lt;/code&gt; 함수를 동시에 병렬로 여러 번 호출할 수 있도록 만들어졌습니다. 그리고 &lt;code&gt;for&lt;/code&gt; 반복문에서 &lt;code&gt;writeToLog&lt;/code&gt;에 대한 작업을 큐에 50회 추가하는데, 그 결과 &lt;code&gt;EventLogger&lt;/code&gt; 내부의 &lt;code&gt;Dictionary&lt;/code&gt;에는 항목들이 추가됩니다. 이와 동시에 &lt;code&gt;readLog&lt;/code&gt; 메소드가 반복적으로 호출되면서 읽기와 쓰기 작업이 동시적으로 적절하게 실행되고 있는지 확인하게 됩니다. 하지만...&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
final public class EventLogger {
    // ...
    public func readLog(for key: String) -&amp;gt; String? {
        return eventsFired[key] as? String // Thread 1: EXC_BAD_ACCESS
    }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱이 실행 중에 충돌합니다. 현재 작업이 읽기인지 쓰기인지와는 무관하게 &lt;code&gt;writeToLog&lt;/code&gt;나 &lt;code&gt;readLog&lt;/code&gt; 중 하나에서 충돌이 계속됩니다. 근본적인 문제는 &lt;code&gt;Dictionary&lt;/code&gt;라는 데이터 타입 자체가 thread-safe하지 않기 때문입니다. 그리고 현재와 같은 구현은 동시 접근에 대해 올바르게 방호해주지 못하고 있습니다.&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;싱글톤을 thread-safe하게 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Serial Dispatch Queue는 오직 한 번에 한 작업만을 실행시킵니다. 대부분의 경우 시리얼 큐(Serial Queue)는 데이터 폭주가 발생하는 것을 막으려고 특정 값이나 자원을 동기적으로 접근하기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬이면서 동시적인 읽기와 쓰기 작업은 데이터 충돌을 일그이고 결과적으로 앱이 충돌하게 만듭니다. 그래서 읽기와 쓰기 작업이 병렬로 발생하지 않도록 확인해 주는 것이 해결책이 될 수 있습니다. 다시 표현하면, 읽기와 쓰기가 동시에 발생하는 것을 막는 것이 이 문제 해결책으로 보입니다. 그런 의미에서 serial queue는 한 번에 한 작업만이 호출되는 것을 보장해 줍니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
final public class EventLogger {
    // ...
    private func readLog(for key: String) -&amp;gt; String? {
        var value: String? = nil
        serialQueue.sync {
            value = eventsFired[key] as? String
        }
        return value
    }
    
    private func writeLog(key: String, content: Any) {
        serialQueue.sync {
            eventFired[key] = content
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
@implementation EventLogger {
    // ...
    dispatch_queue_t serialQueue;
}
// ...

- (instancetype)init {
    if (self = [super init]) {
        // ...
        self-&amp;gt;serialQueue = dispatch_queue_create(&quot;kr.codingcat.EventLoggerExample&quot;, NULL);
    }
    return self;
}


- (NSString *)readLogForKey:(nonnull NSString *)key {
    __block NSString * value = nil;
    
    dispatch_sync(serialQueue, ^{
        value = [self-&amp;gt;eventFired valueForKey:key];
    });
    
    return value;
}

- (void)writeLogForKey:(nonnull NSString *)key andContent:(nullable id)content {
    dispatch_sync(serialQueue, ^{
        [self-&amp;gt;eventFired setValue:content forKey:key];
    });
}
// ...
@end&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;읽기-쓰기 잠금&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위키백과 &amp;ldquo;&lt;a href=&quot;https://en.wikipedia.org/wiki/Readers&amp;ndash;writer_lock&quot; target=&quot;_new&quot;&gt;Readers-Writer lock&lt;/a&gt;&amp;rdquo;에 따르면, Readers-Writer Lock이란 읽기 전용 작업에 대해서는 동시 접근을 허용하고, 쓰기 작업을 할 때는 배타적인 접근 권한을 부여하는 방식을 뜻합니다. 즉 어떤 데이터가 있을 때 그것을 읽는 것이라면 여러 쓰레드에서 동시에 읽어가도 상관 없지만, 그 데이터를 수정하거나 기록하는 작업이라면 배타적인 권한을 얻을 때까지 기다려야 한다는 것입니다. 쓰기 작업이 데이터를 기록하고자 할 때 그 외 쓰기가 필요한 다른 쓰레드 및 읽기 작업만 하려 했던 쓰레드들까지도 이 쓰기 작업이 완료될 때까지 일시 중지 됩니다. 여기서 중요한 개념 두 가지가 등장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 경쟁(data race)&lt;/b&gt; 여러 쓰레드에서 동일한 메모리를 동기화 메커니즘도 갖춰지지 않은 채 접근하려 할 때 발생합니다. 그리고 접근하려는 쓰레드 중 최소한 하나는 쓰기 작업이 포함될 때 발생합니다. 예를 들어 메인 쓰레드에서 어떤 &lt;code&gt;Dictionary&lt;/code&gt;로부터 값을 읽고 있을 때, 동시에 백그라운드 쓰레드에서는 이 &lt;code&gt;Dictionary&lt;/code&gt;를 수정하려 드는 경우입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장벽(barrier)&lt;/b&gt; 장벽은 동기적으로 쓰기 작동을 하기 위해 사용되는 concurrent queue입니다. barrier 플래그(flag)를 지시함으로써 특정 자원이나 값을 thread-safe한 방식으로 접근하는 것이 가능합니다. 그러므로 동시적으로 읽기 작업을 하면서 얻는 이점을 유지하면서도 동기적으로 쓰기 작업도 가능합니다.&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;데이터 경쟁을 예방하면서도 병렬 처리도 수행하기 위해 concurrent queue를 사용하면 이점을 얻는 경우들이 있습니다. 장벽은 여기서 필요하게 됩니다. concurrent queue를 사용하면 &lt;code&gt;Dictionary&lt;/code&gt;에 대한 읽기 및 쓰기에 대한 작업이 병렬로 발생할 수 없습니다. 때문에 동시적인 읽기 작업은 성능 향상으로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 작업의 호출을 직렬화하는 코드는 제거될 수 있지만 &lt;code&gt;Dictionary&lt;/code&gt;의 내용이 이미 수정되고 있을 때 동시에 다른 쓰레드에서 읽기 및 쓰기될 수는 없고, &lt;code&gt;Dictionary&lt;/code&gt; 내용의 수정 작업은 다른 쓰레드에서 읽기 및 쓰기를 시도하기 전에 완료되어야만 합니다. &lt;code&gt;Dispatch&lt;/code&gt; 장벽 및 Grand Central Dispatch를 사용하면 이를 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 장벽 또는 &amp;lsquo;배리어(barrier)&amp;rsquo;는 동시적 큐와 비동시적 큐 사이를 전환하면서 &amp;lsquo;배리어 블록(barrier block)&amp;rsquo;에 있는 코드 실행이 끝날 때까지 비동시적(serial) 큐로 작동되다가 배리어 블록을 벗어나면 동시적 큐로 전환되는 특징이 있습니다. 위 싱글톤 클래스에서 비동시적 큐는 동시적 큐로 대체되고 &lt;code&gt;writeToLog&lt;/code&gt; 메소드가 &lt;code&gt;Dictionary&lt;/code&gt;를 수정할 때 배리어 블록이 설정됩니다. 여기서 &lt;code&gt;writeToLog&lt;/code&gt; 메소드에 있을 배리어 블록은 이전에 호출되었던 모든 블록들이 다 종료될때까지 기다리게 되는데, 이렇게 하면 데이터 경쟁이나 데이터 충돌이 예방됩니다. &lt;code&gt;readLog&lt;/code&gt; 메소드는 읽기만을 할 것이므로 동시적 큐에 의해 작동되며 병렬로 작동됩니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
import Foundation

final public class EventLogger {
    public static let shared = EventLogger()
    
    private var eventFired: [String:Any] = [&quot;initialization&quot;:&quot;Property initialized&quot;]
    private let concurrentLabel = &quot;concurrentQueue&quot;
    private let concurrentQueue = DispatchQueue(label: concurrentLabel, attribute: .concurrent)
    
    private init() {
    }
    
    public func readLog(for key: String) -&amp;gt; String? {
        var value: String?
        concurrentQueue.sync {
            // 동시에 실행될 수 있는 부분
            value = eventFired[key] as? string
        }
        return value
    }
    
    public func writeToLog(key: String, content: Any) {
        concurrentQueue.async(flags: .barrier) {
            // 동기적으로 실행되어야 하는 부분
            self.eventFired[key] = content
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 싱글톤 패턴은 thread-saf하면서도 성능도 최적화되었습니다.&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/145</guid>
      <comments>https://codingcatkr.tistory.com/145#entry145comment</comments>
      <pubDate>Fri, 24 Sep 2021 09:41:25 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Thread-safe한 싱글톤(singleton) 패턴과 그 사용법(2)</title>
      <link>https://codingcatkr.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 Sachithra Siriwardhane 님의 글 &lt;a href=&quot;https://sachithrasiriwardhane.medium.com/thread-safe-singletons-and-their-usage-in-swift-c992d34d85dd&quot;&gt;Thread-safe singletons and their usage in Swift&lt;/a&gt;를 바탕으로 작성되었습니다.&lt;/p&gt;
&lt;h1&gt;Thread-safe한 싱글톤(singleton)과 그 사용법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자라면 다들 &amp;lsquo;싱글톤 패턴(singleton pattern)&amp;rsquo;에 대해 알고 있을 것이고 사용해 보셨을 것이며, iOS 어플리케이션에서 구현이 가능하실 것입니다. 이 글은 입문자 또는 싱글톤 디자인 패턴(design pattern)을 적절하게 사용하기 위한 배경 지식 향상을 원하는 분들에게 도움이 되어 드리고자 작성되었습니다. 이 글에서는 다음과 같은 주제들을 다뤄볼 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;./143&quot;&gt;싱글톤 패턴의 사용: 해야 할 것과 하지 말아야 할 것&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./144&quot;&gt;&lt;strong&gt;Swift/Objective-C에서 싱글톤 패턴의 작성&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./145&quot;&gt;동시성 문제 해결 및 thread-safe하게 싱글톤 패턴을 작성하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swift/Objective-C에서 싱글톤 패턴의 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 소스는 데이터를 수집하여 관련된 호출자에게 이 데이터를 반환하기 위해 준비된 &lt;code&gt;EventLogger&lt;/code&gt; 클래스를 싱글톤으로 작성한 기본적인 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
import Foundation

final public class EventLogger {
    public static let shared = EventLogger()
    
    private var eventFired: [String: Any] = [&quot;initialization&quot;:&quot;Property initialized&quot;]
    
    private init() {
    }
    
    private func readLog(for key: String) -&amp;gt; String? {
        return eventFired[key] as? String
    }
    
    private func writeLog(key: String, content: Any) {
        eventFired[key] = content
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Swift 코드에 해당하는 Objective-C 코드이지만 완전히 일치하지는 않을 것입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
@interface EventLogger : NSObject
@end

@implementation EventLogger {
    NSDictionary * eventFired;
}

+ (instancetype)sharedEventLogger {
    static dispatch_once_t once = 0;
    static id sharedInstance = nil;
    
    dispatch_once(&amp;amp;once, ^{
        sharedInstance = [[self alloc] init];
    });
    
    return sharedInstance;
}

- (instancetype)init {
    if (self = [super init]) {
        self-&amp;gt;eventFired = [[NSDictionary alloc] initWithObjectsAndKeys:@&quot;initialization&quot;,@&quot;Property initialized&quot;, nil];
    }
    return self;
}

- (void)dealloc {
    // ...
}

- (NSString *)readLogForKey:(nonnull NSString *)key {
    return [self-&amp;gt;eventFired valueForKey:key];
}

- (void)writeLogForKey:(nonnull NSString *)key andContent:(nullable id)content {
    [self-&amp;gt;eventFired setValue:content forKey:key];
}
@end&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;EventLogger&lt;/code&gt;는 파생 클래스가 생겨나지 않도록 &lt;code&gt;final&lt;/code&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;public static let shared = EventLogger()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;shared&lt;/code&gt;라고 이름이 적힌 이 상수는 &lt;code&gt;EventLogger&lt;/code&gt; 클래스형 인스턴스를 참조하는 싱글톤입니다. 여기서 &lt;code&gt;static&lt;/code&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;private init()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 생성자는 클래스 외부에서 새 인스턴스가 만들어지지 못하도록 &lt;code&gt;private&lt;/code&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;code&gt;readLog&lt;/code&gt; 메소드가 옵셔널 문자열을 반환함에 따라 값을 &amp;lsquo;언랩(unwrap)&amp;rsquo;하여 사용하기 위해 옵셔널 바인딩이 사용되었습니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
if let contents = EventLogger.shared.readLog(for: &quot;initialization&quot;) {
    print(contents)
} else {
    print(&quot;File not found&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
NSString * contents = [[EventLogger sharedEventLogger] readLogForKey:@&quot;initialization&quot;];

if (contents != nil) {
    NSLog(contents);
} else {
    NSLog(@&quot;File not found&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/144</guid>
      <comments>https://codingcatkr.tistory.com/144#entry144comment</comments>
      <pubDate>Fri, 24 Sep 2021 09:40:04 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Thread-safe한 싱글톤(singleton) 패턴과 그 사용법(1)</title>
      <link>https://codingcatkr.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 Sachithra Siriwardhane 님의 글 &lt;a href=&quot;https://sachithrasiriwardhane.medium.com/thread-safe-singletons-and-their-usage-in-swift-c992d34d85dd&quot;&gt;Thread-safe singletons and their usage in Swift&lt;/a&gt;를 바탕으로 작성되었습니다.&lt;/p&gt;
&lt;h1&gt;Thread-safe한 싱글톤(singleton)과 그 사용법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자라면 다들 &amp;lsquo;싱글톤 패턴(singleton pattern)&amp;rsquo;에 대해 알고 있을 것이고 사용해 보셨을 것이며, iOS 어플리케이션에서 구현이 가능하실 것입니다. 이 글은 입문자 또는 싱글톤 디자인 패턴(design pattern)을 적절하게 사용하기 위한 배경 지식 향상을 원하는 분들에게 도움이 되어 드리고자 작성되었습니다. 이 글에서는 다음과 같은 주제들을 다뤄볼 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;./143&quot;&gt;&lt;strong&gt;싱글톤 패턴의 사용: 해야 할 것과 하지 말아야 할 것&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./144&quot;&gt;Swift/Objective-C에서 싱글톤 패턴의 작성&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./145&quot;&gt;동시성 문제 해결 및 thread-safe하게 싱글톤 패턴을 작성하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글톤 패턴의 사용: 해야 할 것과 하지 말아야 할 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴의 주된 목적은 주어진 타입의 인스턴스를 단 하나만 생성하여 여럿이 돌려 쓰는 경우를 확실하게 하기 위함입니다. 해당 어플리케이션 전반에서 접근되고 관리될 필요가 있는 싱글톤 자원이 있을 때 싱글톤 패턴은 유용하게 사용될 것입니다. 싱글톤 패턴은 &lt;code&gt;UIApplication&lt;/code&gt;, &lt;code&gt;FileManager&lt;/code&gt;, &lt;code&gt;UserDefaults&lt;/code&gt;, &lt;code&gt;URLSession&lt;/code&gt; 및 &lt;code&gt;UIAccelerometer&lt;/code&gt; 등 Apple의 플랫폼에서 흔히 쓰입니다. 예를 들어 어떤 앱에 대한 &lt;code&gt;UIApplication&lt;/code&gt; 클래스 인스턴스는 반드시 존재해야 하지만, 그것이 또한 여러 개가 존재해서는 안 됩니다. 다음은 Apple의 플랫폼에서 싱글톤으로 구현되는 몇 가지 예입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;UIApplication.shared&lt;/code&gt;, &lt;code&gt;UserDefaults.standard&lt;/code&gt;, &lt;code&gt;FileManager.default&lt;/code&gt;, &lt;code&gt;URLSession.shared&lt;/code&gt; 및 &lt;code&gt;OperationQueue.main&lt;/code&gt; 등...&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;싱글톤 패턴의 잘못된 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 디자인 패턴에 비해 싱글톤 패턴은 상대적으로 구현이 쉽기는 하나 실무에서 잘못 사용될 가능성도 있습니다. 뷰 컨트롤러(View Controller) 사이에서 데이터를 교환하거나, 전역 다목적 컨테이너로서 싱글톤 패턴을 사용하는 경우가 개발자들 사이에서 흔히 있는 일인데, 이는 싱글톤 디자인 패턴 남용의 대표적 사례이며 single responsibility principle(SOLID principle)에 반하는 코딩 스타일입니다(SOLID principle이란 어떤 프로그램에 있는 모든 클래스들은 하나의 기능만을 담당해야 한다는 원칙입니다).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 싱글톤 패턴이 일단 한 번 구현되었다면, 싱글톤에 의돈하는 객체들의 추적을 어렵게 만들어서 결과적으로 상호 의존성(coupling)을 높이는 결과가 됩니다. 더 나아가서는 이 싱글톤을 수정하기 위해 상당한 비용이 소요될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 문제를 피하고자 어떤 대상을 보호하기 위해 thread-safe한 싱글톤으로 작성할 경우, 다중 스레드로 병렬처리가 될 때 해당 객체를 접근하는 부분에서 병목현상이 발생할 수도 있음을 염두에 두시기 바랍니다. 그러므로 꼭 필요할 때 성능을 고려해가면서 싱글톤 패턴을 사용하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/143</guid>
      <comments>https://codingcatkr.tistory.com/143#entry143comment</comments>
      <pubDate>Wed, 22 Sep 2021 20:49:00 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Objectiv-C 블록(block)으로 작업하기(3) [完]</title>
      <link>https://codingcatkr.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 애플 측 개발자 문서인 &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW1&quot;&gt;Working with Blocks&lt;/a&gt;를 바탕으로 작성한 것입니다. Swift의 &amp;lsquo;클로저(closure)&amp;rsquo;에 대응하는 Objective-C의 &amp;lsquo;블록(block)&amp;rsquo;에 대한 내용입니다.&lt;/p&gt;
&lt;h1&gt;Objectiv-C 블록(block)으로 작업하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Objective-C 클래스는 데이터 및 이에 관계된 행동으로 구성된 객체를 정의합니다. 때때로 Objective-C에서는 일련의 메소드들보다는 하나의 작업 또는 단위 행동을 표현하는 다른 방법을 찾는 것이 더 나은 경우도 있습니다. 블록(block)은 C, Objective-C 및 C++에서 마치 변수처럼 다른 곳으로 어떤 메소드나 함수 조각을 전달하기 위한 언어 수준에서 정의된 기능입니다. 블록은 Objective-C에서 객체인데 이는 &lt;code&gt;NSArray&lt;/code&gt;나 &lt;code&gt;NSDictionary&lt;/code&gt; 같은 콜렉션으로 추가될 수 있음을 뜻합니다. 블록은 또한 자신을 감싸고 있는 스코프에서 값을 캡쳐할 수 있는 능력을 가지고 있는데 이는 타 프로그래밍 언어에서 &amp;lsquo;클로저(closure)&amp;rsquo;나 &amp;lsquo;람다(lambda)&amp;rsquo;와 비슷합니다. 이 장에서는 블록을 선언하고 참조하는 구문에 대해 설명하고 콜렉션 열거와 같은 통상적인 작업에서 블록을 사용하는 방법에 대해 보일 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;./140&quot;&gt;블록 기본 구문; 블록의 매개변수 및 반환 형식&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./141&quot;&gt;블록의 스코프 캡쳐와 strong 순환 참조 해결&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./142&quot;&gt;&lt;strong&gt;블록 응용 예&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록은 열거 작업을 간단하게 할 수 있다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;completion handler 뿐만 아니라 많은 Cocoa 및 Cocoa Touch API는 공통의 작업을 단순화시키는 데 블록 구문을 쓰기도 합니다. 예를 들어 콜렉션 열거와 같은 작업들이 있습니다. &lt;code&gt;NSArray&lt;/code&gt; 클래스는 세 가지의 블록 기반 메소드를 가지고 있습니다. 예를 들어,&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL * stop))block;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메소드는 한 개의 매개변수를 받는데 이는 배열 내 각 원소에 도달할 때마다 호출될 블록입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
NSArray * array = /* ... */;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
    NSLog(@&quot;array[%lu] = \&quot;%@\&quot;&quot;, idx, obj);
}];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 자체는 세 개의 매개변수를 받습니다. 그 중 처음 두 개는 현재 참조된 객체 및 배열에서 그 원소의 인덱스입니다. 세 번째 매개변수는 boolean 변수에 대한 포인터로서, 열거 작업을 중지시키고 싶을 때 사용될 수 있습니다. 예를 들어 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
    if (/* ... */) {
        *step = YES;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 &lt;code&gt;enumerateObjectsWithOptions:usingBlock:&lt;/code&gt; 메소드를 사용하여 열거 작업에 대한 옵션을 지정할 수도 있는데 예를 들어, &lt;code&gt;NSEnumerationReverse&lt;/code&gt;를 사용하면 배열에서 각 원소들을 역순으로 참조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거 작업을 하는 블록이 processor-intensive 하면서 동시 실행을 해도 안전한 경우라면 NSEnumerationConcurrent 옵션을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
[array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 플래그를 지정할 경우 블록의 실행이 여러 쓰레드에 걸쳐거 분신될 수 있습니다. 만일 블록 내의 코드가 processor-intensive하다면 이 속성은 잠재적인 성능 향상을 가져다 줄 것입니다. 다만 주의할 것은 이 옵션을 사용하면 각 원소들의 접근 순서가 일정하지 않게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;NSDictionary&lt;/code&gt; 클래스도 블록 기반의 메소드를 다음과 같이 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
NSDictionary * dictionary = /* ... */;
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop) {
    NSLog(@&quot;dictionary[\&quot;%@\&quot;] = \&quot;%@\&quot;&quot;, key, obj);
}];&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록은 동시 수행될 작업을 단순화시킬 수 있다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록은 &amp;ldquo;실행 가능한 코드와 스코프로부터 캡쳐된 옵셔널 상태들로 이루어진 구분되는 단위 작업(a distinct unit of work, combining executable code with optional state captured from the surrounding scope)&amp;rdquo;입니다. 이것은 OS X이나 iOS에서 지원하는 동시성 옵션 중 하나를 사용하게 되는 비동기 호출과도 잘 맞습니다. 쓰레드(thread)처럼 저 수준 메커니즘을 가지고 어떻게 작업해야 하나 찾아봐야 하는 대신, 여러분은 블록을 사용하여 여러분의 작업을 간단하게 정의하고 그 작업을 프로세서 자원이 가능하게 되는대로 시스템에게 수행하도록 맡기면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS X이나 iOS는 태스크 스케줄링 메커니즘인 Operation Queue와 Grand Central Dispatch를 포함하여 다양한 동시성 기술을 제공하는데, 이들 메커니즘은 호출되어야 할 작업들이 순서를 기다리고 있는 큐(queue)의 형태로부터 구현된 것입니다. 호출시키고자 하는 블록을 큐에 추가하면 시스템은 프로세서 시간과 자원이 사용 가능해졌을 때 순서대로 큐에서 블록들을 꺼내게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시리얼 큐(serial queue)는 한 번에 하나의 오직 하나의 작업만을 실행합니다. 이전에 실행해 두었던 작업이 종료되기 전까지는 큐에 대기 중인 다음 작업이 호출되지 않습니다. 동시성 큐(concurrent queue)는 이전 작업이 종료되기를 기다리지 않고 가능한 많은 작업들을 한꺼번에 호출합니다.&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;오퍼레이션 큐를 가지고 블록 오퍼레이션 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오퍼레이션 큐(operation queue)는 작업 스케줄링을 하기 위한 Cocoa 및 Cocoa Touch의 접근법입니다. 여러분은 실행할 작업과 이에 수반되는 각종 데이터들을 &lt;code&gt;NSOperation&lt;/code&gt; 클래스형 인스턴스로 캡슐화할 수 있습니다. 이렇게 해서 만들어진 오퍼레이션을 실행을 위해 &lt;code&gt;NSOperationQueue&lt;/code&gt;로 전달할 수 있습니다. 물론 여러분은 복잡한 작업들을 구현하기 위해 &lt;code&gt;NSOperation&lt;/code&gt;을 상속받아서 새로운 클래스를 만들 수도 있기는 하나 다음과 같이 미리 준비된 &lt;code&gt;NSBlockOperation&lt;/code&gt;을 사용하면 블록을 생성해서 작업을 기술할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{
    // ...
}];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 오퍼레이션은 여러분이 직접 호출할 수도 있지만 대개는 이미 존재하는 오퍼레이션 큐, 또는 여러분이 새로 만든 오퍼레이션 큐에 추가하여 실행되도록 대기시킵니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
// main queue에서 작업 스케줄링
[[NSOperationQueue mainQueue] addOperation:operation];

// background queue에서 작업 스케줄링
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분이 오퍼레이션 큐를 사용한다면 여러분은 큐에 있는 오퍼레이션 사이의 우선 순위라든가 의존성을 설정할 수도 있습니다. 예를 들어 몇 개의 오퍼레이션들로 이루어진 오퍼레이션 그룹이 완전이 종료될 때까지 어느 하나의 오퍼레이션 실행을 보류하도록 지시할 수도 있습니다. 또한 key-value observing을 통해 여러분의 오퍼레이션의 상태 변화를 관찰할 수도 있는데, 이는 예를 들어 작업이 완료될 때마다 진행율 표시기에 나타날 숫자를 변경하는데 유용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/142</guid>
      <comments>https://codingcatkr.tistory.com/142#entry142comment</comments>
      <pubDate>Wed, 22 Sep 2021 15:07:08 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Objectiv-C 블록(block)으로 작업하기(2)</title>
      <link>https://codingcatkr.tistory.com/141</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 애플 측 개발자 문서인 &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW1&quot;&gt;Working with Blocks&lt;/a&gt;를 바탕으로 작성한 것입니다. Swift의 &amp;lsquo;클로저(closure)&amp;rsquo;에 대응하는 Objective-C의 &amp;lsquo;블록(block)&amp;rsquo;에 대한 내용입니다.&lt;/p&gt;
&lt;h1&gt;Objectiv-C 블록(block)으로 작업하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Objective-C 클래스는 데이터 및 이에 관계된 행동으로 구성된 객체를 정의합니다. 때때로 Objective-C에서는 일련의 메소드들보다는 하나의 작업 또는 단위 행동을 표현하는 다른 방법을 찾는 것이 더 나은 경우도 있습니다. 블록(block)은 C, Objective-C 및 C++에서 마치 변수처럼 다른 곳으로 어떤 메소드나 함수 조각을 전달하기 위한 언어 수준에서 정의된 기능입니다. 블록은 Objective-C에서 객체인데 이는 &lt;code&gt;NSArray&lt;/code&gt;나 &lt;code&gt;NSDictionary&lt;/code&gt; 같은 콜렉션으로 추가될 수 있음을 뜻합니다. 블록은 또한 자신을 감싸고 있는 스코프에서 값을 캡쳐할 수 있는 능력을 가지고 있는데 이는 타 프로그래밍 언어에서 &amp;lsquo;클로저(closure)&amp;rsquo;나 &amp;lsquo;람다(lambda)&amp;rsquo;와 비슷합니다. 이 장에서는 블록을 선언하고 참조하는 구문에 대해 설명하고 콜렉션 열거와 같은 통상적인 작업에서 블록을 사용하는 방법에 대해 보일 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;./140&quot;&gt;블록 기본 구문; 블록의 매개변수 및 반환 형식&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./141&quot;&gt;&lt;strong&gt;블록의 스코프 캡쳐와 strong 순환 참조 해결&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./142&quot;&gt;블록 응용 예&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록은 자신을 감싸는 스코프로부터 값들을 캡쳐할 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통상적인 실행 구문 뿐만 아니라 블록도 자신을 감싸는 스코프에 있는 상태를 캡쳐할 수 있는 능력을 갖고 있습니다. 여러분이 어떤 메소드 안에서 블록을 선언했다면, 그 메소드의 스코프 안에서 접근할 수 있는 모든 변수들은 블록에서도 접근이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)testMethod {
    int anInteger = 42;
    
    void (^testBlock)(void) = ^{
        NSLog(@&quot;Integer is %i&quot;, anInteger);
    };
    
    testBlock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서 &lt;code&gt;anInteger&lt;/code&gt;는 블록의 바깥에 선언되어 있지만 블록을 정의하고 있는 그 스코프 내에서 캡쳐가 되었기에 블록 내에서도 사용이 가능합니다. 여러분이 명시하지 않는 이상 블록이 정의될 당시의 값만이 캡쳐되는데, 다시 표현하면 여러분이 블록을 선언하고 그 블록을 호출하는 도중에 값이 변경되었다면 이는 반영되지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)testMethod {
    int anInteger = 42;
    
    void (^testBlock)(void) = ^{
        NSLog(@&quot;Integer is %i&quot;, anInteger);
    };
    
    anInteger = 84;
    
    testBlock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록이 정의되고 변수들이 캡쳐된 이후 그 변수들의 값이 변경되었어도 이는 블록 내에 다시 반영되지 않습니다. 이것은 다음과 같이 출력 결과로 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;477&quot; data-filename=&quot;무제 3.png&quot; width=&quot;640&quot; height=&quot;365&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MSYLh/btrftmI77qH/OZANaWDTwcFkbaQHqDCLf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MSYLh/btrftmI77qH/OZANaWDTwcFkbaQHqDCLf1/img.png&quot; data-alt=&quot;캡쳐된 변수의 값 변화가 없는 Block 예제 소스.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MSYLh/btrftmI77qH/OZANaWDTwcFkbaQHqDCLf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMSYLh%2FbtrftmI77qH%2FOZANaWDTwcFkbaQHqDCLf1%2Fimg.png&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;477&quot; data-filename=&quot;무제 3.png&quot; width=&quot;640&quot; height=&quot;365&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;캡쳐된 변수의 값 변화가 없는 Block 예제 소스.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;244&quot; data-filename=&quot;무제 4.png&quot; width=&quot;640&quot; height=&quot;129&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c50cK6/btrfIY0tmZs/Bk7SeEr17kmPqEE3XOnFO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c50cK6/btrfIY0tmZs/Bk7SeEr17kmPqEE3XOnFO1/img.png&quot; data-alt=&quot;값 변화가 없는 Block 예제 소소의 실행 결과.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c50cK6/btrfIY0tmZs/Bk7SeEr17kmPqEE3XOnFO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc50cK6%2FbtrfIY0tmZs%2FBk7SeEr17kmPqEE3XOnFO1%2Fimg.png&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;244&quot; data-filename=&quot;무제 4.png&quot; width=&quot;640&quot; height=&quot;129&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;값 변화가 없는 Block 예제 소소의 실행 결과.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록은 변수의 원 값을 바꿀 수 없다는 것을 의미하기도 합니다. 마치 &lt;code&gt;const&lt;/code&gt; 속성으로 캡쳐되기 때문입니다.&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;저장소를 공유하기 위해 __block 변수를 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 안에서 캡쳐된 변수의 값을 변경할 필요가 있을 때 여러분은 변수 선언문에서 &lt;code&gt;__block&lt;/code&gt; 저장소 지정자를 사용할 수 있습니다. 이것은 저장소의 변수가 원 변수 및 그 스코프 내에서 선언된 블록들 사이에서도 공유됨을 의미합니다. 앞서 적었던 예제를 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;plain&quot;&gt;&lt;code&gt;// Objective-C
- (void)testMethod {
    __block int anInteger = 42;
    
    void (^testBlock)(void) = ^{
        NSLog(@&quot;Integer is: %i&quot;, anInteger);
    };
    
    anInteger = 84;
    
    testBlock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;anInteger&lt;/code&gt; 변수는 &lt;code&gt;__block&lt;/code&gt; 지시자로 선언되었기 때문에 이 변수의 저장소는 블록이 정의되는 곳과 공유됩니다. 이것은 다음의 출력 결과로써 확인이 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;477&quot; data-filename=&quot;무제 5.png&quot; width=&quot;640&quot; height=&quot;372&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZGx4k/btrfIYTHTjK/w2gkRPJuor5gRKVxR3q28K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZGx4k/btrfIYTHTjK/w2gkRPJuor5gRKVxR3q28K/img.png&quot; data-alt=&quot;캡쳐된 변수의 값 변화가 있는 Block 예제 소스.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZGx4k/btrfIYTHTjK/w2gkRPJuor5gRKVxR3q28K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZGx4k%2FbtrfIYTHTjK%2Fw2gkRPJuor5gRKVxR3q28K%2Fimg.png&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;477&quot; data-filename=&quot;무제 5.png&quot; width=&quot;640&quot; height=&quot;372&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;캡쳐된 변수의 값 변화가 있는 Block 예제 소스.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;246&quot; data-filename=&quot;무제 6.png&quot; width=&quot;640&quot; height=&quot;131&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1qUfl/btrfA2oVFwd/UelKd5IQomq5ZZgbkODFak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1qUfl/btrfA2oVFwd/UelKd5IQomq5ZZgbkODFak/img.png&quot; data-alt=&quot;값 변화가 있는 Block 예제 소소의 실행 결과.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1qUfl/btrfA2oVFwd/UelKd5IQomq5ZZgbkODFak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1qUfl%2FbtrfA2oVFwd%2FUelKd5IQomq5ZZgbkODFak%2Fimg.png&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;246&quot; data-filename=&quot;무제 6.png&quot; width=&quot;640&quot; height=&quot;131&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;값 변화가 있는 Block 예제 소소의 실행 결과.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;__block&lt;/code&gt; 지시자는 블록 내에서 다음과 같이 원 변수의 값을 수정할 수 있음을 뜻합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)testMethod {
    __block int anInteger = 42;
    
    void (^testBlock)(void) = ^{
        NSLog(@&quot;Integer is: %i&quot;, anInteger);
        anInteger = 100;
    };
    
    testBlock();
    NSLog(@&quot;Value of original variable is now: %i&quot;, anInteger);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력은 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;476&quot; data-filename=&quot;무제 7.png&quot; width=&quot;640&quot; height=&quot;276&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0H7eo/btrfreRBLJe/KPYNKYXUqGUCyiKkuyd77K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0H7eo/btrfreRBLJe/KPYNKYXUqGUCyiKkuyd77K/img.png&quot; data-alt=&quot;블록 내에서 값 변화가 있는 예제 소스.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0H7eo/btrfreRBLJe/KPYNKYXUqGUCyiKkuyd77K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0H7eo%2FbtrfreRBLJe%2FKPYNKYXUqGUCyiKkuyd77K%2Fimg.png&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;476&quot; data-filename=&quot;무제 7.png&quot; width=&quot;640&quot; height=&quot;276&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;블록 내에서 값 변화가 있는 예제 소스.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;244&quot; data-filename=&quot;무제 8.png&quot; width=&quot;640&quot; height=&quot;129&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blBhVW/btrfDh7ISUY/BCv6rCiKwZukKQ73MYuV10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blBhVW/btrfDh7ISUY/BCv6rCiKwZukKQ73MYuV10/img.png&quot; data-alt=&quot;블록 내 값 변화가 있는 예제의 실행 결과.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blBhVW/btrfDh7ISUY/BCv6rCiKwZukKQ73MYuV10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblBhVW%2FbtrfDh7ISUY%2FBCv6rCiKwZukKQ73MYuV10%2Fimg.png&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;244&quot; data-filename=&quot;무제 8.png&quot; width=&quot;640&quot; height=&quot;129&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;블록 내 값 변화가 있는 예제의 실행 결과.&lt;/figcaption&gt;
&lt;/figure&gt;
&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;블록은 함수나 메소드의 매개변수로서 전달될 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 매개변수들은 블록을 정의한 즉시 호출합니다. 실무에서는 블록을 다른 곳에서 호출시키기 위해 함수나 메소드의 매개변수로 넘기는 경우가 일반적입니다. 예를 들어 블록을 백그라운드에서 호출시키기 위해, 또는 반복적으로 호출되는 작업(예를 들어 콜렉션의 각 원소들을 열거하는 것)을 나타내는 블록을 정의하기 위해 Grand Central Dispatch를 사용하게 될 수도 있습니다. 이 장의 후반부에서는 동시성과 열거에 대해 좀 더 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록은 또한 어떤 작업이 완료되었을 때 호출될 코드 조각을 정의하는 &amp;lsquo;콜백(callback)&amp;rsquo; 목적으로 사용되기도 합니다. 예를 들어 여러분의 앱은 복잡한 작업을 수행하는 객체를 생성할 때 또한 사용자의 행동에도 반응해야 할 수 있습니다. 웹 서비스에 정보를 요청하는 경우가 그런 예이지요. 특히 웹과 관련된 작업은 상대적으로 소요 시간이 길기 때문에 여러분은 작업이 진행되는 동안 진행율 표시기(progress indicator) 같은 것을 사용자에게 보일 필요가 있습니다. 그리고 작업이 완료되면 진행율 표시기를 숨겨야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 직업은 델리게이션(delegation)으로써 구현할 수 있습니다. 이를 위해서 여러분은 적절한 델리게이트 프로토콜을 만들어야 하고, 필요한 메소드들을 구현해야 하며, 오래 걸리는 작업에 대한 델리게이트로서 여러분의 객체를 설정해야 하고, 작업이 완료되어 여러분에 객체 안에 만들어 놓은 델리게이트 메소드가 호출될 때까지 기다려야 할 것입니다. 블록은 이와 같은 조치들을 간단하게 만듭니다. 여러분은 작업을 초기화할 때 콜백의 행동을 다음과 같이 정의할 수 있기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (IBAction)fetchRemoteInformation:(id)sender {
    [self showProgressIndicator];
    
    XYZWebTask * task = /* ... */;
    
    [task beginTaskWithCallbackBlock:^{
        [self hideProgressIndicator];
    |;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 진행율 표시기를 나타내는 메소드를 호출한 다음에 작업을 생성하고 시작합니다. 콜백 블록은 작업이 완료된 후 실행될 코드를 지정하고 있는데, 이 경우 진행율 표시기를 숨깁니다. 블록 내에서 &lt;code&gt;hideProgressIndicator&lt;/code&gt; 메소드가 호출되기 위하여 &lt;code&gt;self&lt;/code&gt;가 캡쳐되고 있음을 주목하기 바랍니다. 이 글의 후반부에서 다시 설명하겠지만 strong 참조 순환이 발생하기 쉽기 때문에 &lt;code&gt;self&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;이 예제에서 &lt;code&gt;beginTaskWithCallbackBlock&lt;/code&gt; 메소드의 선언은 다음과 같이 생겼을 것입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;(void (^)(void))&lt;/code&gt; 타입은 매개변수가 없고 반환 형식도 없는 블록을 뜻합니다. 이 메소드의 내부 구현에서 블록 호출은 다음과 같을 것입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock {
    // ...
    callbackBlock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 이상의 매개변수를 받는 블록에 대한 타입도 다음과 같이 지정 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)doSomethingWithBlock:(void (^)(double, double))block {
    // ...
    block(21.0, 2.0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록은 항상 메소드의 마지막 매개변수가 되어야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드에서 블록형 매개변수는 하나만 받는 것이 가장 좋은 실무입니다. 메소드가 블록형이 아닌 매개변수도 함께 받을 경우 블록 매개변수는 맨 마지막에 와야 합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;- (void)beginTaskWithName:(NSString *)name completion:(void (^)(void))callback;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 선언된 메소드는 호출 시 블록을 인라인으로 작성할 때 그 문장을 읽기 좋게 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
[self beginTaskWithName:@&quot;MyTask&quot; completion:^{
    NSLog(@&quot;The task is complete.&quot;);
}];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록 구문을 단순화하기 위하여 타입 재정의를 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 시그니처를 갖는 블록을 여러 번 사용한다면 여러분은 그 시그니처에 대해 여러분만의 타입으로 재정의할 수 있습니다. 예를 들어 다음과 같이 매개변수도 없고 반환 형식도 없는 타입을 재정의합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;typedef void (^XYZSimpleBlock)(void);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 여러분은 블록 변수나 블록 매개변수를 사용할 때 이 타입을 적을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
XYZSimpleBlock anotherBlock = ^{
    // ...
};

- (void)beginFetchWithCallbackBlock:(XYZSimpleBlock)callbackBlock {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 재정의된 블록 타입은 블록 자체를 반환하거나 블록이 매개변수로서 또 다른 어떤 블록을 받을 때 유용합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
void (^(^complexBlock)(void (^)(void)))(void) = ^(void ^(aBlock)(void)) {
    // ...
    return ^{
        // ...
    };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;complexBlock&lt;/code&gt; 변수는 불록을 매개변수로 받으면서 그 반환 형식 또한 블록인 어떤 블록을 참조하고 있습니다. 타입 재정의를 사용하면 이러한 소스 코드를 보다 가독성 있게 작성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^(XYZSimpleBlock){
    // ...
    return ^{
        // ...
    };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;객체는 블록을 기억해 두기 위해 프로퍼티를 사용한다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록을 기억해 둘(keep track of) 목적으로 프로퍼티를 정의하는 구문은 블록 변수를 정의하는 구문과 유사합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt; 스코프를 벗어나도 그 블록이 캡쳐하고 있던 상태를 유지하기 위해서는 프로퍼티 속성으로 &lt;code&gt;copy&lt;/code&gt;를 지정해야 합니다. 이것은 여러분이 ARC를 사용할 때 염려하던 것과는 경우가 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 프로퍼티도 다른 블록 변수들과 마찬가지로 대입되고 호출됩니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
[self setBlockProperty:^{
    // ...
}];
[self blockProperty]();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 다음과 같이 재정의된 타입을 사용하는 것도 됩니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
typedef void (^XYZSimpleBlock)(void);

@interface XYZObject : NSObject@
@property (copy) XYZSimpleBlock blockProperty;
@end&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;self를 캡쳐할 때 strong 순환 참조를 피하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 블록과 같이 여러분이 블록 안으로 &lt;code&gt;self&lt;/code&gt;를 캡쳐할 때, 메모리 관리 구현 방법을 고려하는 것이 중요합니다. 블록은 캡쳐된 객체에 대해 strong 참조를 유지합니다. 이는 &lt;code&gt;self&lt;/code&gt;도 마찬가지입니다. 예를 들어 &lt;code&gt;self&lt;/code&gt;를 캡쳐하는 어떤 블록에 대한 프로퍼티가 &lt;code&gt;copy&lt;/code&gt; 속성을 가지고 있다면 결국 strong 순환 참조가 발생하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
@interface XYZBlockKeepter : NSObject
@property (copy) void (^block)(void);
@end
@implementation XYZBlockKeeper
@synthesize block;
- (void)configureBlock {
    [self setBlock:^{
        [self doSomething]; // self에 대한 strong 참조
        // 이후 strong 순환 참조 발생
    }];
}
@end&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 간단한 소스 코드라면 컴파일러는 여러분에게 경고해 줄 것입니다. 그러나 복잡한 예제라면 객체들 사이에 여러 strong 참조가 얽히게 되어서 컴파일러의 진단이 어려울 수 있습니다. 이런 문제를 피하기 위해 &lt;code&gt;self&lt;/code&gt;에 대해서는 다음과 같이 &lt;code&gt;weak&lt;/code&gt; 참조를 하도록 지정합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    [self setBlock:^{
        [weakSelf doSomething]; // weak 참조를 캡쳐해 왔음
        // 순환 참조를 피할 수 있음
    }];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;self&lt;/code&gt;를 weak 포인터로 참조한 변수를 캡쳐함으로써 블록은 XYZBlockKeeper 객체를 거꾸로 strong 참조하지 않게 됩니다. 블록이 호출되기 전에 객체가 소멸되었다면 &lt;code&gt;weakSelf&lt;/code&gt; 포인터는 단순히 &lt;code&gt;nil&lt;/code&gt;을 가리키게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/141</guid>
      <comments>https://codingcatkr.tistory.com/141#entry141comment</comments>
      <pubDate>Wed, 22 Sep 2021 15:01:28 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Objectiv-C 블록(block)으로 작업하기(1)</title>
      <link>https://codingcatkr.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 애플 측 개발자 문서인 &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW1&quot;&gt;Working with Blocks&lt;/a&gt;를 바탕으로 작성한 것입니다. Swift의 &amp;lsquo;클로저(closure)&amp;rsquo;에 대응하는 Objective-C의 &amp;lsquo;블록(block)&amp;rsquo;에 대한 내용입니다.&lt;/p&gt;
&lt;h1&gt;Objectiv-C 블록(block)으로 작업하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Objective-C 클래스는 데이터 및 이에 관계된 행동으로 구성된 객체를 정의합니다. 때때로 Objective-C에서는 일련의 메소드들보다는 하나의 작업 또는 단위 행동을 표현하는 다른 방법을 찾는 것이 더 나은 경우도 있습니다. 블록(block)은 C, Objective-C 및 C++에서 마치 변수처럼 다른 곳으로 어떤 메소드나 함수 조각을 전달하기 위한 언어 수준에서 정의된 기능입니다. 블록은 Objective-C에서 객체인데 이는 &lt;code&gt;NSArray&lt;/code&gt;나 &lt;code&gt;NSDictionary&lt;/code&gt; 같은 콜렉션으로 추가될 수 있음을 뜻합니다. 블록은 또한 자신을 감싸고 있는 스코프에서 값을 캡쳐할 수 있는 능력을 가지고 있는데 이는 타 프로그래밍 언어에서 &amp;lsquo;클로저(closure)&amp;rsquo;나 &amp;lsquo;람다(lambda)&amp;rsquo;와 비슷합니다. 이 장에서는 블록을 선언하고 참조하는 구문에 대해 설명하고 콜렉션 열거와 같은 통상적인 작업에서 블록을 사용하는 방법에 대해 보일 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;./140&quot;&gt;&lt;strong&gt;블록 기본 구문; 블록의 매개변수 및 반환 형식&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./141&quot;&gt;블록의 스코프 캡쳐와 strong 순환 참조 해결&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;./142&quot;&gt;블록 응용 예&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블록 구문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록을 정의하는 구문은 다음과 같이 &amp;lsquo;캐럿(caret)&amp;rsquo; 기호(^)를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
^{
    NSLog(@&quot;This is a block&quot;);
}&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;여러분이 C 함수를 참조하고 어딘가로 이를 전달하기 위해 함수 포인털르 사용하듯이, 여러분은 Objective-C에서 다음과 같이 블록을 보관할 수 있는 변수를 선언할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
void (^simpleBlock)(void);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분이 C 함수 포인터를 다루는 것에 익숙하지 않다면, 이런 구문이 다소 이상해 보일 것입니다. 다음 예는 &lt;code&gt;simpleBlock&lt;/code&gt;이라는 변수에 매개변수도 없고 반환 타입도 없는 블록을 참조시킬 것입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
simpleBlock = ^{
    NSLog(@&quot;This is a block&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 통상의 변수 대입과 차이가 없습니다. 그래서 문장의 끝은 세미콜론으로 끝납니다. 여러분은 다음과 같이 변수의 선언과 대입을 한 문장으로 적을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
void (^simpleBlock)(void) = ^{
    NSLog(@&quot;This is a block&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 변수를 선언하고 대입한 후에 다음과 같이 블록을 호출 및 실행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
simpleBlock();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt; 블록이 대입되지 않은 변수 또는 &lt;code&gt;nil&lt;/code&gt;이 대입된 변수를 호출하려 할 경우 앱은 충돌합니다.&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;매개변수를 받고 값을 반환하는 블록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록도 메소드 또는 함수와 마찬가지로 매개변수를 받고 값을 반환할 수 있습니다. 다음 예는 두 개의 값을 받아서 그 값을 곱한 결과를 반환하는 블록을 참조하는 변수입니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
double (^multiplyTwoValues)(double, double);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 참조될 수 있는 블록은 다음과 같이 생겼습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
^(double firstValue, double secondValue) {
    return firstValue * secondValue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통의 함수와 마찬가지로 &lt;code&gt;firstValue&lt;/code&gt;와 &lt;code&gt;secondValue&lt;/code&gt;는 블록이 호출될 때 주어진 값을 참조합니다. 이 예제에서 블록의 반환 형식은 블록 내의 &lt;code&gt;return&lt;/code&gt; 문장이 반환하는 형식에서 유래된 것입니다. 물론 여러분의 선호에 따라 캐럿과 매개변수 목록 사이에 블록의 반환 형식을 직접 명시할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
^ double (double firstValue, double secondValue) {
    return firstValue * secondValue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록을 일단 선언 및 정의하고 변수에 참조시키면 여러분은 그 이후부터는 함수처럼 호출할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;objc objectivec&quot;&gt;&lt;code&gt;// Objective-C
double (^multiplyTwoValues)(double, double) 
    = ^(double firstValye, double secondValue) {
    return firstValue * secondValue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/140</guid>
      <comments>https://codingcatkr.tistory.com/140#entry140comment</comments>
      <pubDate>Tue, 21 Sep 2021 12:20:35 +0900</pubDate>
    </item>
    <item>
      <title>[번역글] Swift 클로저 사용을 위한 궁극의 가이드(3) [完]</title>
      <link>https://codingcatkr.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본 게시글은 &lt;a href=&quot;https://learnappmaking.com/closures-swift-how-to/&quot;&gt;The Ultimate Guide to Closures in Swift&lt;/a&gt;를 바탕으로 작성하였습니다.&lt;/p&gt;
&lt;h1&gt;Swift 클로저 사용을 위한 궁극의 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 튜토리얼은 Swift의 클로저(closure)를 여러분에게 상세히 안내해 줄 것입니다. 클로저란 여러분이 마치 변수에 값을 대입하거나 매개변수로서 전달하듯 소스 코드 안에서 주고 받을 수 있는 코드 블럭입니다. 클로저를 완전히 이해하는 것은 iOS 개발을 배우는 과정에서 중대한 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분이 옵셔널(optional)에 대해 이해하는 데 어려움이 있었다면 클로저를 이해하는 것은 더욱 큰 일이 될 지도 모릅니다. 하지만 걱정마시기 바랍니다. 클로저는 보기보다 해롭지 않습니다. 알고보면 매우 유용합니다.&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;/li&gt;
&lt;li&gt;클로저를 선언하고 호출하는 구문&lt;/li&gt;
&lt;li&gt;클로저의 타입을 구성하고 해체하는 방법&lt;/li&gt;
&lt;li&gt;&amp;ldquo;클로징 오버(closing over)&amp;rdquo;란 무엇이고 캡처 리스트(capture list)는 어떻게 사용할 것인가?&lt;/li&gt;
&lt;li&gt;completion handler로서 클로저를 어떻게 사용할 것인가&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;h2&gt;Swift의 클로저와 캡쳐&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 단순히 변수에 대입이 가능한 함수처럼 보는 것은 클로저의 진정한 의미에 부합되지 않습니다. 이전까지 필자가 클로저를 이렇게 표현해 온 것은 가장 중요한 개념인 &amp;ldquo;캡쳐(capture)&amp;rdquo;를 빼고 설명해 온 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;클로저(closure)&amp;rdquo;라는 이름은 &amp;ldquo;동봉하다(enclosing)&amp;rdquo;라는 말에서 따온 것인데, 좀 더 확장해 보면 이 단어는 함수형 프로그래밍의 개념인 &amp;ldquo;closing over&amp;rdquo;에서 온 단어입니다. Swift에서 클로저는 자신을 감싸고 있는 범위 내의 변수와 상수들을 캡쳐합니다(참고: &amp;lsquo;closing over&amp;rsquo;, &amp;lsquo;capturing&amp;rsquo;, &amp;lsquo;enclosing&amp;rsquo;은 여기에서 모두 같은 의미로 쓰였습니다).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서 모든 변수, 함수 및 클로저들은 스코프를 가졌습니다. 스코프(scope)는 여러분이 어디에서 특정 변수, 함수 또는 클로저에 접근할 수 있는지 그 범위를 결정해줍니다. 스코프 내에 변수, 함수 또는 클로저가 없는 경우 여러분은 대상에 접근할 수 없습니다. 스코프는 때떄로 맥락 또는 &amp;ldquo;컨텍스트(context)&amp;rdquo;라고도 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코프를 여러분의 집 열쇠가 어디에 있을 지에 대한 컨텍스트와 비교해 보겠습니다. 여러분이 집 밖에 나가 있을 때 집 열쇠도 집 밖에 있을 것입니다. 여러분은 집 밖에 나와 있는데 열쇠는 집 안에 있다면 여러분과 열쇠는 같은 컨텍스트 내에 있는 것이 아닙니다. 따라서 여러분은 현관문을 열 수 없습니다. 여러분과 집 열쇠가 모두 집 밖에 나와 있어야 같은 컨텍스트에 있는 것이므로, 여러분은 현관문을 열 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분의 코드는 글로벌 스코프(global scope)와 로컬 스코프(local scope)로 나눌 수 있습니다.&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;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
let name = &quot;Zaphod&quot;
let greeting = {
    print(&quot;Don't panic, \(name)!&quot;)
}

greeting()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) &lt;code&gt;name&lt;/code&gt;이라 이름 붙은 상수는 문자열인 &lt;code&gt;&quot;Zaphod&quot;&lt;/code&gt;를 보관하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) &lt;code&gt;greeting&lt;/code&gt;이라 이름 붙은 상수에 클로저가 정의되고 보관됩니다. 이 클로저는 메시지를 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) 마지막 줄에서 클로저가 호출됩니다.&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;code&gt;name&lt;/code&gt;을 캡처(close over)합니다. 다시 말하면 클로저는 자신이 정의된 스코프 내에서 접근 가능한 변수들을 캡슐화합니다. 그 결과 우리는 클로저 안에서 지역적으로 선언되지 않은 식별자인 &lt;code&gt;name&lt;/code&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;swift&quot;&gt;&lt;code&gt;// Swift
func addScore(_ points: Int) -&amp;gt; Int {
    let score = 42
    let calculate = {
        score + points
    }
    
    return calculate()
}

let value = addScore(11)
print(value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) &lt;code&gt;addScore(_:)&lt;/code&gt;가 정의됩니다. 이것은 매개변수 &lt;code&gt;point&lt;/code&gt;로 넘어온 값에 이전 점수인 &lt;code&gt;42&lt;/code&gt;를 더한 값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 이 함수 안에는 클로저 &lt;code&gt;calculate&lt;/code&gt;가 정의되어 있습니다. 이것은 단순히 &lt;code&gt;score&lt;/code&gt;와 &lt;code&gt;point&lt;/code&gt;를 더하여 그 값을 반환합니다. 이 함수는 &lt;code&gt;calculate()&lt;/code&gt;로써 클로저를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) &lt;code&gt;addScore(_:)&lt;/code&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;code&gt;calculate&lt;/code&gt;는 두 개의 값인 &lt;code&gt;score&lt;/code&gt;와 &lt;code&gt;points&lt;/code&gt;를 캡쳐합니다. 이들 변수는 둘 다 클로저 안에서는 선언된 적이 없지만 클로저는 문제없이 이들의 값을 가져올 수 있습니다. 클로저가 자신의 스코프 내 변수들을 캡쳐했기 때문입니다. 클로저의 캡쳐에 대해 몇 가지 주목할만한 사항은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) 클로저는 자신의 내부에서 실제로 사용하는 변수 등을 캡쳐합니다. 클로저 내에서 접근되지 않는 변수 등은 캡쳐되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 클로저는 변수와 상수를 캡쳐할 수 있습니다. 물론 기술적으로 클로저도 변수나 상수의 일종이기 때문에 다른 클로저를 캡쳐할 수도 있습니다. 또한 프로퍼티의 경우 변수 또는 상수에 대입되어 있는 어떤 객체에 소속된 프로퍼티이기 때문에 여러분은 이 또한 클로저로 캡쳐할 수 있습니다. 물론 클로저 안에서 클로저 밖에 있는 함수(메소드)를 호출할 수도 있습니다. 함수(메소드)는 변수 또는 상수도 아니고 여기에 보관되는 개념도 아니기에 캡쳐되지는 않지만 호출이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) 캡쳐는 일방향성을 갖습니다. 클로저는 자신이 정의된 스코프 내의 것들을 캡쳐하는데, 클로저 바깥에서는 클로저 안에서 선언 및 정의된 사항들에 대해서 접근할 수 없습니다.&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;한편 클로저로 값을 캡쳐하는 것은 strong 참조의 순환이나 메모리 누수로 이어질 수 있습니다. 그래서 &amp;lsquo;캡쳐 리스트(capture list)&amp;rsquo;가 도입되게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Strong 참조와 캡쳐 리스트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 값들을 캡쳐하는데 이 때 그 값에 대한 strong 참조를 자동으로 생성합니다. 예를 들어 Bob이 Alice에 대해 strong 참조를 가지고 있을 때 Bob이 메모리에서 제거되지 않는 한 Alice도 메모리에서 제거되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 좋습니다. 하지만 Alice가 Bob에 대해 strong 참조를 가지고 있다면 어떻게 될까요? Bob과 Alice는 서로 붙잡고 있기 때문에 둘 다 메모리에서 제거될 수 없습니다. 이것을 string 참조 순환이라 하며 메모리 누수의 원인이 됩니다. 만일 Bob과 Alice가 10MB씩 차지하고 있다면 다른 앱들은 그 만큼의 메모리를 사용 못하게 되고 이를 제거할 방법도 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡처 리스트를 사용하면 strong 참조 순환을 깰 수 있습니다. 클래스의 프로퍼티에 &lt;code&gt;weak&lt;/code&gt; 키워드를 붙인 것과 같이 클로저가 캡쳐할 값에 &lt;code&gt;weak&lt;/code&gt;나 &lt;code&gt;unowned&lt;/code&gt; 참조를 표시할 수 있습니다. 이를 명시하지 않는다면 기본 속성인 &lt;code&gt;strong&lt;/code&gt;이 지정됩니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
class Database {
    var data = 0
}

let database = Database()
database.data = 11010101

let calculate = { [weak database] multiplier in
    database.data * multiplier
}

let result = calculate(2)
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) &lt;code&gt;data&lt;/code&gt;라는 프로퍼티 하나를 갖는 &lt;code&gt;Database&lt;/code&gt; 클래스를 선언 및 정의하고 이에 대한 새 인스턴스를 생성합니다. 그리고 &lt;code&gt;data&lt;/code&gt; 프로퍼티에 어떤 정수 값을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) &lt;code&gt;calculate&lt;/code&gt; 클로저를 정의합니다. 클로저는 하나의 매개변수인 &lt;code&gt;multiplier&lt;/code&gt;를 받습니다. 또한 &lt;code&gt;database&lt;/code&gt;를 캡쳐합니다. 클로저 안에서 &lt;code&gt;database.data&lt;/code&gt; 값은 &lt;code&gt;multiplier&lt;/code&gt;로 곱해진 다음 반환됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) 마지막 줄에서 클로저는 &lt;code&gt;2&lt;/code&gt;라는 매개변수를 가지고 호출된 다음 그 결과가 &lt;code&gt;result&lt;/code&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;swift&quot;&gt;&lt;code&gt;// Swift
... { [weak database] ... } ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡쳐 리스트는 컴마로 구분되는 변수 이름의 목록인데 대괄호로 둘러싸여서 &lt;code&gt;weak&lt;/code&gt; 또는 &lt;code&gt;unowned&lt;/code&gt; 키워드가 붙을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Swift
[weak self]
[unowned navigationController]
[unowned self, weak database]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 strong 순환 참조를 깨기 위해 위와 같이 &lt;code&gt;weak&lt;/code&gt; 또는 &lt;code&gt;unowned&lt;/code&gt; 키워드로써 캡쳐 리스트를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) &lt;code&gt;weak&lt;/code&gt; 키워드는 캡쳐된 값이 &lt;code&gt;nil&lt;/code&gt;이 될 수 있음을 지시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) &lt;code&gt;unowned&lt;/code&gt; 키워드는 캡쳐된 값이 절대 &lt;code&gt;nil&lt;/code&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;code&gt;weak&lt;/code&gt;와 &lt;code&gt;unowned&lt;/code&gt; 키워드는 둘 다 strong 참조의 반대 개념입니다. 다만 &lt;code&gt;weak&lt;/code&gt;는 캡쳐된 변수가 &lt;code&gt;nil&lt;/code&gt;을 참조할 수 있다는 것이 &lt;code&gt;unowned&lt;/code&gt;와의 차이입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통상적으로 클로저와 캡쳐 값이 항상 서로 참조하고 있으며 동시에 소멸될 때 &lt;code&gt;unowned&lt;/code&gt; 키워드를 사용합니다. 예를 들어 뷰 컨트롤러에서 &lt;code&gt;[unowned self]&lt;/code&gt;가 있는데, 이 경우 클로저는 뷰 컨트롤러보다 오래 존속될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 또한 어떤 경우 캡쳐 값이 &lt;code&gt;nil&lt;/code&gt;이 될 수 있을 때 &lt;code&gt;weak&lt;/code&gt;를 사용할 수 있습니다. 주로 클로저가 자신을 생성해 준 컨텍스트보다 더 오래 존속될 수 있을 때 이것을 사용할 수 있는데, 예를 들어 오래 걸리는 작업이 완료되기 전에 뷰 컨트롤러가 소멸될 수 있을 경우가 해당됩니다. 결과적으로 캡쳐된 값은 옵셔널 타입입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡쳐, 캡쳐 리스트 및 메모리 관리의 개념은 어려울 수 있겠으나 그렇게 복잡한 것만은 아닙니다. 추상적인 개념을 설명하기가 참으로 어려운 일이지만, 실무 iOS 개발에서 일반적으로 캡쳐 리스트는 &lt;code&gt;[weak self]&lt;/code&gt;이나 &lt;code&gt;[unowned self]&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;(1) 클로저는 자신을 감싸는 스코프를 캡쳐할 수 있습니다. 그래서 해당 스코프 내의 변수와 상수들은 클로저 안에서 접근이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 변수와 상수는 기본적으로 strong 참조로 캡쳐되는데 이는 strong 참조 순환을 야기할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) 여러분은 캡쳐 리스트를 사용하여 strong 순환 참조 문제를 깰 수 있습니다. 예를 들어 &lt;code&gt;weak&lt;/code&gt;나 &lt;code&gt;unowned&lt;/code&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;</description>
      <category>Language/Objective-C &amp;amp; Swift</category>
      <author>코딩집사</author>
      <guid isPermaLink="true">https://codingcatkr.tistory.com/139</guid>
      <comments>https://codingcatkr.tistory.com/139#entry139comment</comments>
      <pubDate>Mon, 20 Sep 2021 21:30:02 +0900</pubDate>
    </item>
  </channel>
</rss>