├── .gitignore ├── README.md ├── additional_topics ├── GCD.md ├── allocation.md ├── arc.md ├── assets │ ├── dispatch.png │ ├── dispatch_table.png │ ├── logo.png │ ├── message-dispatch.png │ ├── observable.png │ ├── virtual-dispatch.png │ ├── withoutpool.png │ └── withpool.png ├── autoreleasepool.md ├── concurrent.md ├── memory_safety.md ├── method_dispatch.md ├── operationQueue.md ├── operation_vs_gcd.md ├── performance_improvement.md ├── rxswift.md ├── thread.md └── threadsafe.md ├── assets ├── roadmap.png └── swift_logo.PNG ├── basic ├── access.md ├── appstate.md ├── assets │ ├── coffee.gif │ ├── escaping.png │ ├── nonescaping.png │ ├── sequence.png │ ├── types.png │ └── val_ref.png ├── basic_operator.md ├── basic_type.md ├── bundle.md ├── class.md ├── closure.md ├── codable.md ├── collection_type.md ├── compound.md ├── condition.md ├── enum.md ├── equatable.md ├── file.md ├── func.md ├── function.md ├── generics.md ├── hashable.md ├── lifecycle.md ├── loop.md ├── method.md ├── properties.md ├── protocol.md ├── sequence.md ├── standard.md ├── struct.md ├── tuple.md ├── types.md ├── value_vs_reference.md └── var_let.md ├── etc ├── assets │ ├── ci.png │ └── vn.png └── face.md ├── features ├── assets │ ├── monad.png │ └── wrapping.png ├── error_handling.md ├── fast.md ├── first_class_functions.md ├── functional_programming.md ├── functor.md ├── higher_order_functions.md ├── monad.md ├── optional.md ├── pop.md └── safe.md ├── frameworks ├── about.md ├── arkit.md ├── arsession.md ├── assets │ ├── ARFaceGeometry.png │ ├── ARSessionBaseLifeCycle.png │ ├── ARworldTrackingConfiguration.png │ ├── AVAudioSession.png │ ├── ActivateSenario.png │ ├── CAEmitterLayer2.gif │ ├── FeedbackTrackingQuality.png │ ├── Multiroute.png │ ├── RouteChange.png │ ├── arkitBase.png │ ├── arobjectAnchor.png │ ├── coreml.png │ ├── cubeImage.png │ ├── directionalLight.png │ ├── environmentTexturingExample.png │ ├── imageTracking.png │ ├── interruption.png │ ├── interruptionLifeCycle.png │ ├── interruption_flow.png │ ├── keyword.jpeg │ ├── ml.png │ ├── neural.png │ ├── objectDetection.png │ ├── orientationTracking.png │ ├── persistanceAR.png │ ├── recorder_player_flow.png │ ├── rotationBase.png │ └── vc_to_view.png ├── audio.md ├── audio_difference.md ├── audio_input_output.md ├── audio_port.md ├── audio_session.md ├── buffer.md ├── calayer.md ├── coreml.md ├── environment_texturing.md ├── face_tracking_enhancements.md ├── image.md ├── image_tracking.md ├── object_detection.md ├── realtime.md ├── saveVideo.md ├── save_load_maps.md ├── siri_shortcut.md ├── sirikit.md ├── trimMergeVideo.md ├── vc.md ├── video.md ├── view.md └── view_vs_layer.md ├── maintenance ├── api_design_guideline.md ├── crud.md ├── lint.md ├── naming_convention.md ├── principle.md └── spa_code.md ├── networking ├── message_stream.md ├── overview.md └── restful.md ├── pattern ├── architecture.md ├── assets │ ├── async_problem.png │ ├── mvc.png │ ├── mvp.png │ ├── mvvm.png │ ├── mvvm_rx.png │ ├── observable.png │ ├── oop.png │ ├── relation.png │ ├── uml.png │ └── viper.png ├── dip.md ├── hig.md ├── hig_widget.md ├── isp.md ├── lsp.md ├── mvc.md ├── mvp.md ├── mvvm.md ├── object.md ├── ocp.md ├── solid.md ├── srp.md └── viper.md └── security ├── cryptography.md └── overview.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /additional_topics/allocation.md: -------------------------------------------------------------------------------- 1 | # Memory Allocation 2 | 3 | 메모리는 기본적으로 데이터를 저장하는 공간입니다. CPU가 데이터를 연산하고 처리하는 장치라면 메모리는 입력 및 출력 데이터를 저장하는 역할을 합니다. 이러한 메모리는 물리적 혹은 기능적으로 구분하고는 합니다. 4 | 5 | #### 물리적 메모리 구분 6 | 7 | - ##### 하드디스크 8 | 9 | : 전원이 꺼져도 데이터는 남아있어 반 영구적으로 데이터를 소장할 수 있습니다. 다만 처리속도가 느립니다. 10 | 11 | - ##### 램 12 | 13 | : 처리속도가 매우 빠르고 하드디스크와는 다르게 전원이 꺼지면 데이터가 날아가는 특성이 있습니다. 이러한 특성 때문에 보통 프로그램은 하드디스크에 저장되어 있다가 프로그램이 실행되는 순간 램으로 탑제되어 CPU의 연산을 보조하는 메모리로서의 역할을 합니다. 14 | 15 | - ##### 레지스터와 캐시 16 | 17 | : CPU 칩 안의 메모리 입니다. 램보다도 처리속도가 빠르기 때문에 CPU를 좀더 가까운 거리에서 보좌해줍니다. 18 | 19 | 20 | #### 기능적 (가상) 메모리 구분 21 | 22 | **정적할당:** 변수선언을 통해 컴파일시 필요한 메모리를 확보하는 방법입니다. 메모리의 크기가 하드 코딩되어 있기 때문에 프로그램이 실행 될 때 이미 해당 메모리의 크기가 결정되는 것이 특징입니다. 해제하지 않음으로 인한 메모리 누수와 같은 문제를 신경쓰지 않아도 된다. 정적 할당된 메모리는 실행 도중에 해제되지 않고, 프로그램이 종료할 때 알아서 운영 체제가 회수합니다. 메모리의 크기가 하드 코딩되어 있어서 나중에 조절 할 수 없다. 동적 할당에 비해 할당 받을 수 있는 최대 메모리에 제약을 받습니다. 23 | 24 | **동적할당:** 프로그램 실행 도중, 동적으로 필요한 메모리를 확보하는 방법입니다. 사용이 끝나면 운영체제가 쓸 수 있도록 반납하고 다음에 요구가 오면 재 할당을 받을 수 있다. 이것은 프로그램이 실행하는 순간 프로그램이 사용할 메모리 크기를 고려하여 메모리의 할당이 이루어지는 정적 메모리 할당과 대조적입니다. 상황에 따라 원하는 크기만큼의 메모리가 할당되므로 경제적이며, 이미 할당된 메모리라도 언제든지 크기를 조절할 수 있지만, 더 이상 사용하지 않을 때 명시적으로 메모리를 해제해 주어야 한다는 단점이 있습니다. 25 | 26 | 27 | 28 | - ##### Code (정적) 29 | 30 | : 함수와 상수가 저장되는 공간입니다. 프로그램이 실행되는 중에 변하지 않으므로 정적으로 할당됩니다. 31 | 32 | - ##### Data (정적) 33 | 34 | : Global(전역) / Static(정적) 변수가 저장되는 공간입니다. 프로그램이 실행되는 동안 여기저기서 필요한 변수들이기 때문에 프로그램이 끝날 때까지 정적으로 할당되어 메모리에 남아있습니다. 35 | 36 | cf) 초기화를 시키지 않은 전역/정적 변수들은 BSS 영역에 따로 저장됩니다. 37 | 38 | 39 | - ##### Stack (정적) 40 | 41 | : 특정 구문 내에서 임시로 할당하는 메모리 영역으로 Local(지역) / Parameter(매개) 변수가 저장되는 공간입니다. 함수의 파라미터나 함수 안에서 선언된 지역 변수들 및 리턴값이 저장됩니다. 이들은 함수가 실행될 때 메모리를 할당받아 생성되어 함수가 실행완료되면 할당받았던 메모리를 반환합니다. 이러한 변수들은 할당 시 스택에 값 자체가 저장됩니다. 함수 호출 순서에 따라 물건이 쌓이듯 (stack) 메모리에 쌓이고 나가는 구조 때문에 스택이라고 부릅니다. 42 | 43 | 스택은 CPU에 의해 관리되고 optimized됩니다. 스택의 사이즈는 컴파일시 이미 결정되기 때문에 스택에 할당된 변수들은 direct하게 메모리에 적재되고, 매우 빠르게 접근할 수 있습니다. value type의 변수가 스택에 적재됩니다. 44 | 45 | cf) 개념상 동적으로 할당되지만 컴파일 시에 이미 결정되기 때문에 런타임에는 정적인 사이즈를 갖습니다. 46 | 47 | 48 | 49 | - ##### Heap (동적) 50 | 51 | 개발자가 동적으로 할당하는 영역입니다. 실행 중에 개발자가 원하는 만큼 데이터를 확보/제거 하는 유동적인 데이터 관리에 이점이 있습니다. 하지만 계속해서 할당만 받고 제고하지 않으면 결국 힙 영역에 무리가 올 수 있고, 메모리 누수가 생길 수도 있습니다. (Swift에서는 ARC로 관리합니다.) 52 | 53 | 이 영역은 메모리 주소값에 의해서만 참조되고 사용되는 영역입니다. 즉, Reference type들이 저장되는 곳 입니다. Heap은 Swift의 참조형 자료인 클래스, 클로져 등의 자료가 머무르는 공간입니다. Heap에 저장된 데이터는 모두 참조형이기 때문에 원본 데이터가 갑자기 사라지게 되면 이 원본을 참조했던 변수들에 에러가 발생합니다. 54 | 55 | 힙에서는 스택과 같이 자동적으로 객체에 메모리를 할당 및 반환할 수 없습니다. 외부적으로 이러한 일을 해야합니다. (개발자가 직접하거나 Swift처럼 ARC, 자바의 GC 등이 이러한 일을 합니다.) 따라서 메모리 할당, 참조 추적, 메모리 반환 등의 모든 프로세스가 프로그램 런타임 동안에 일어나게 되므로 스택에 비해 느릴 수 밖에 없습니다. 이것이 value type이 reference type보다 빠른 이유입니다. 56 | 57 | 58 | 59 | ## Swift 자료형 타입 60 | 61 | 62 | 63 | 위에서 언급하였다시피 Reference type은 Heap에 저장됩니다. 64 | 65 | ### Reference: 66 | 67 | - https://m.blog.naver.com/PostView.nhn?blogId=jdub7138&logNo=220916332425&proxyReferer=https%3A%2F%2Fwww.google.com%2F 68 | -------------------------------------------------------------------------------- /additional_topics/assets/dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/dispatch.png -------------------------------------------------------------------------------- /additional_topics/assets/dispatch_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/dispatch_table.png -------------------------------------------------------------------------------- /additional_topics/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/logo.png -------------------------------------------------------------------------------- /additional_topics/assets/message-dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/message-dispatch.png -------------------------------------------------------------------------------- /additional_topics/assets/observable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/observable.png -------------------------------------------------------------------------------- /additional_topics/assets/virtual-dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/virtual-dispatch.png -------------------------------------------------------------------------------- /additional_topics/assets/withoutpool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/withoutpool.png -------------------------------------------------------------------------------- /additional_topics/assets/withpool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/additional_topics/assets/withpool.png -------------------------------------------------------------------------------- /additional_topics/autoreleasepool.md: -------------------------------------------------------------------------------- 1 | # AutoReleasePool 2 | 3 | ## Swift의 기본적 메모리 관리: ARC 4 | 5 | low-level 언어들은 일반적으로 alloc-free 방식 메모리 관리 정책을 이용합니다. 매우 단순한데, 사용할 메모리를 할당(Allocation) 하는 것이 alloc 이고 반대로 이 할당한 메모리를 다시 OS에 돌려주는 것이 free입니다. 6 | 7 | 8 | Swift 이전의 Object-C는 C에서 파생된 언어이기 때문에 C의 이런 메모리관리 방식을 그대로 사용할 수 있지만, 그 보다는 좀 진보한 메모리 관리 정책을 도입하였습니다. 바로 레퍼런스 카운트 개념을 이용한 메모리 해제 방법입니다. 9 | 10 | 레퍼런스 카운트 방식은 Retain과 Release 라는 개념을 이용합니다. 메모리를 할당(alloc)하는 것은 비슷하지만, 코드에서 메모리를 참조하려 할 때 이 레퍼런스 카운트를 증가(retain)시키고 참조가 끝나면 레퍼런스 카운트를 감소(release)시키는 방식이다. 그래서 레퍼런스 카운트가 0이 되면 메모리가 해제(free)됩니다. 따라서 레퍼런스 카운트 메모리 관리 방식에서는 '리테인 하는 코드'와 '릴리즈 하는 코드'가 쌍으로 존재해야 합니다. 11 | 12 | ~~리테인은 '내꺼라고 침 뱉는 행위', 그리고 릴리즈는 이제 필요없으니 '뱉어놓은 침을 닦는 행위'라고 종종 이야기합니다.~~ 13 | 14 | retain과 release가 짝이 잘 맞으면 레퍼런스 카운트 방식의 메모리 관리 방법은 잘못된 메모리 참조 오류를 일으킬 가능성이 낮아지면서도 GC(자동 메모리 회수, Gabage Collection)에 의한 퍼포먼스 저하도 존재하지 않는 효율적인 방식이 됩니다. 15 | 16 | > GC의 대표적 부작용 2가지 17 | 18 | - 1. GC가 언제 실행될 지 코드 상에서 알 수가 없습니다. 19 | 20 | - 2. GC로 인한 퍼포먼스 저하 21 | 22 | 이러한 retain/release의 ARC를 스위프트 코드로 개념적으로만 설명하면 아래와 같습니다. 23 | 24 | ```swift 25 | // 예제 코드 26 | func someFunction() { 27 | var obj = SomeClass() 28 | doWork(obj) 29 | } 30 | 31 | // 위 코드는 빌드 시 ARC에 의해 아래와 같은 식으로 바뀌어져 컴파일됩니다. 32 | // 다만 아래 코드는 개념 설명을 위한 코드이므로 빌드는 안 될 것입니다. 33 | func someFunction() { 34 | var obj = SomeClass() 35 | obj.retain() // ARC 36 | 37 | doWork(obj) 38 | 39 | obj.release() // ARC 40 | } 41 | ``` 42 | 43 | Swift는 strong, weak, unowned의 세 가지 방법중 하나를 골라 ARC에게 retain/release 처리 방법을 알려줄 수 있습니다. 자세한 내용은 [ARC](./arc.md)를 참조하시면 됩니다. 44 | 45 | 46 | 기존에는 개발자가 객체 release 시점과 적용해야 하는 대상 객체, 그리고 Ref Count 등에 대해 정확히 이해하고 관리해야 했으나 ARC가 자동으로 이를 관리하여 주기 때문에 세세하게 신경써야 할 필요가 사라졌습니다. 그러나 이러한 ARC와 더불어 특수한 상황에서 추가적으로 메모리 관리에 필요한 개념이 **AutoRelease**입니다. 47 | 48 | ## AutoRelease 49 | 50 | AutoRelease는 참조 카운트를 나중으로 미루기 위한, 그러면서도 카운트가 나중에 감소되는 것을 보장받기 위한 기법입니다. 객체의 참조 카운트를 감소시킬 때 release대신 autorelease를 사용하면 release가 예약된다. 실제 release를 수행하면 참조 카운트가 바로 줄어들지만, autorelease 직후는 아직 release된 상황이 아니므로 참조 카운트가 감소되지 않습니다. (물론 나중에는 감소합니다). ~~그런데 왜 이름이 reserveRelease가 아니고 autoRelease인지..~~ 51 | 52 | autoRelease된 객체는 **AutoReleasePool**에 등록됩니다. AutoReleasePool은 객체를 관리하는 일종의 컬렉션입니다. 그리고 이 컬렉션이 해제될 때 관리하는 객체를 모두 release합니다. 따라서 autorelease 된 객체의 release 시점은 아주 명료합니다. 바로 AutoReleasePool은이 해제될 때입니다. 53 | 54 | 당연히 사용가능한 AutoReleasePool이 없다면 autoRelease객체는 release 메시지를 받지 못하고 메모리 누수가 발생하게 됩니다. 따라서 AutoReleasePool이 없는 상황에서 autoRelease 메시지를 보내면 코코아는 에러를 띄울 것 입니다. 앱킷이나 UI킷 프레임워크는 각각의 이벤트 루프의 시작점에서 자동적으로 AutoReleasePool을 생성하여 작동합니다. 55 | 56 | 그러나 원하면 각종 컬렉션 객체를 이용하여 별도의 pool을 간단하게 만들어 쓸 수도 있습니다. 다만, Foundation Framework에 AutoreleasePool이 있으므로 일반적으로 애써 만들 이유는 없습니다. 하지만 다음의 경우에는 별도의 AutoreleasePool을 만들어야 합니다. 57 | 58 | > 1. 코코아 애플리케이션의 각 스레드는 자체적인 자동 릴리스 풀 블록 스택을 유지합니다. 그러나 Foundation-only 프로그램의 경우 자체적인 오토릴리즈 풀을 만들어야 합니다. 59 | 60 | > 2. 많은 양의 임시 객체를 사용하는 루프를 쓸 때. 만약 루프 안에서 임시 객체를 만들어 쓸 때 루프 속에 오토릴리즈 풀을 만들면 이는 루프를 돌면서 사용하는 메모리의 최대치를 낮게 제어할 수 있습니다. 61 | 62 | > 3. 다른 스레드를 만들어 분리하는 경우, 오토릴리즈 풀을 생성합니다. 스레드가 실행되면 가능한 먼저 오토스레드 풀을 만들어야 합니다. 그렇지 않으면 메모리가 누수될 여지가 생깁니다. 63 | 64 | 오토릴리즈 풀은 생성된 루프나 함수 내에서 폐기 되어야 합니다. 또한 객체의 인스턴스 변수에 대해서는 오토릴리즈 풀을 생성할 필요도 없고, 보통 하지도 않습니다. 65 | 66 | 때문에 일반적으로 오토릴리즈 객체는 **함수 내부에서 생성되어 리턴되는 객체를 위한 메모리 트릭**이라고 볼 수 있습니다. 아래는 함수 내에서 여러 개의 UIImage를 만드는 스위프트 예시입니다. 67 | 68 | ```swift 69 | func useManyImages() { 70 | let filename = pathForResourceInBundle 71 | 72 | for _ in 0 ..< 5 { 73 | for _ in 0 ..< 1000 { 74 | let image = UIImage(contentsOfFile: filename) 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | 이러한 함수를 돌릴 경우, 함수 시작과 끝에서 retain/release가 일어나기 때문에 프로그램 메모리 allocation graph가 아래와 같이 함수 종료 직전까지 상승하게 됩니다. 81 | 82 | 83 | 84 | 때문에 만약 함수 내에서 수 없이 많은 이미지 객체를 루프를 통해 생성하는 경우, 메모리로 인해 앱이 죽는 상황까지 발생할 수 있습니다. 85 | 86 | 그러나 함수 내에서 루프를 돌 때마다 명시적으로 오토릴리즈 풀을 이용해 메모리를 해제할 수 있습니다. 아래는 위의 예시를 오토릴리즈 풀을 이용하여 쓴 코드입니다. 87 | 88 | ```swift 89 | func useManyImages() { 90 | let filename = pathForResourceInBundle 91 | 92 | for _ in 0 ..< 5 { 93 | autoreleasepool { 94 | for _ in 0 ..< 1000 { 95 | let image = UIImage(contentsOfFile: filename) 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | 오토릴리즈 풀의 블락이 종료됨과 동시에 풀 안의 오토릴리즈 객체들의 메모리는 모두 해제됩니다. 따라서 위의 코드를 돌린 메모리 할당 그래프는 아래와 같습니다. 102 | 103 | 104 | 105 | 106 | ### Reference: 107 | - http://nephilim.tistory.com/120 108 | - https://stackoverflow.com/questions/25860942/is-it-necessary-to-use-autoreleasepool-in-a-swift-program 109 | - http://blog.naver.com/PostView.nhn?blogId=itperson&logNo=220819529932&parentCategoryNo=&categoryNo=72&viewDate=&isShowPopularPosts=false&from=postView 110 | -------------------------------------------------------------------------------- /additional_topics/concurrent.md: -------------------------------------------------------------------------------- 1 | # About Concurrent programming 2 | 3 | ~~잘못 설계된다면..~~ 4 | 5 | 6 | 7 | ## Table of Contents 8 | - Processor, CPU, Core의 차이 9 | 10 | - Concurrency(병행성)와 Parallelism(병렬성)의 차이 11 | 12 | - Process와 Thread의 차이 13 | 14 | - 멀티 프로세싱, 멀티 태스킹, 멀티 쓰레딩의 차이 15 | 16 | ## Processor, CPU, Core의 차이 17 | 18 | ### Processor 19 | 프로세서는 컴퓨터 운영을 위해 기본적인 명령어들을 처리하고 반응하기 위한 논리회로입니다. 종종 시스템 내에서의 CPU를 말하기 위해 사용되기는 하지만, 일반적으로 컴퓨터 시스템 내에는 여러개의 프로세서들이 혼합되어져 있습니다. 20 | 21 | ex) CPU, GPU, VPU, TPU ... 22 | 23 | ### CPU 24 | 25 | 컴퓨터에서 구성 단위 중 기억, 연산, 제어의 3대 기능을 종합하는 장치인 Central Processing Unit(중앙 처리 장치)의 줄임말입니다. 컴퓨터의 대뇌라고 할 정도로 가장 중요한 부분으로, 프로그램의 명령어를 해석하여 데이터를 연산/처리를 하는 부분, 혹은 그 기능을 내장한 칩을 말합다. 컴퓨터가 동작하는 데 필요한 모든 계산을 처리합니다. 컴퓨터를 뇌에 비유하자면 단기기억 담당은 RAM, 장기기억은 하드디스크, CPU는 사고를 담당하는 대뇌피질 정도로 볼 수 있습니다. GHz단위의 한번의 정보 처리량을 통해 CPU 사양을 알 수 있습니다. 26 | 27 | ### Core 28 | 29 | CPU내에서 연산을 담당하는 핵심요소입니다. 많을 수록 일반적으로 처리속도가 빨라집니다. (듀얼코어, 쿼드코어, 헥사코어, 옥타코어...) 30 | 31 | ## Concurrency(병행성)와 Parallelism(병렬성)의 차이 32 | 33 | 34 | 35 | **Concurrency는 프로그램의 성질이고 parallel execution은 기계의 성질입니다.** 36 | Concurrenty is a property of the program and prallel execution is a property of the machine. 37 | 38 | 39 | 40 | ### Concurrency 41 | 어떤 **프로그램이나 알고리즘이 순서에 상관없이 동시에 수행**될 수 있다면 concurrent하다고 말합니다. 예를 들어, 1부터 100까지 숫자를 더하는 과정을 생각해보면 숫자 100개를 여러 부분 집합으로 나눈 뒤 동시에 부분합을 구합니다. 그리고 이 부분합을 다시 더하면 원래 얻고자 하는 값을 얻을 수 있습니다. 이 때 이 알고리즘은 concurrent하다라고 말합니다. 42 | 43 | Concurrent한 프로그램은 싱글코어 머신에서도 분명히 돌아갑니다. 뮤텍스, 데드락은 싱글코어에서도 얼마든지 그 의미를 갖습니다. 멀티스레드 프로그램이 비록 물리적인 제약으로 싱글코어에서 시분할 형태로 돌아가지만 겉으로는 concurrent하게 작동한다고 속일 수 있습니다. 반대로 아무리 멀티스레드로 작성된 프로그램이라 하더라도 멀티코어가 아니라면 병렬로 작동한다고 말하지 못합니다. 44 | 45 | 46 | ### Parallelism 47 | 48 | 위에서 1부터 100까지 더하는 알고리즘이 정말 물리적으로 병렬로 돌아갈지 아닐지는 이 알고리즘이 어떤 하드웨어 위에서 돌아갈지 알아야만 확답할 수 있습니다. 방금 이야기한 알고리즘이 멀티 프로세서 머신에서 돌아가야 병렬 실행된다라고 말할 수 있습니다. Parallel execution은 따라서 프로그램의 성질보다는 하드웨어의 성질입니다. 49 | 50 | OpenMP, MPI, CUDA 같은 프로그래밍 방법론은 병행 프로그래밍 보다는 병렬 프로그래밍이 옳은 표현합니다. 언급한 세 방법론 모두 물리적으로 제공되는 다양한 **병렬 하드웨어**를 활용하기 때문입니다. 51 | 52 | 53 | ## Process와 Thread의 차이 54 | 55 | ### Process 56 | 컴퓨터에서 **프로세서에 의해 연속적으로 실행되고 있는 컴퓨터 프로그램, 즉 운영체제로부터 자원을 할당받는 작업의 단위**입니다. 종종 스케줄링의 대상이 되는 작업(task)이라는 용어와 거의 같은 의미로 쓰입니다. 프로그램은 실행이 되기 전의 명령어와 데이터의 묶음인데, 이러한 정적인 요소인 프로그램이 실행 중에 있을때 그것들을 우리는 프로세스라고 말합니다. 57 | 58 | 다시 말해서, 하드디스크에 저장되어 있는 명령어와 데이터의 묶음 자체는 프로그램이며, 프로세스는 그러한 프로그램을 구동하여(실행하여), 프로그램 자체와 프로그램의 상태가 메모리 상에서 실행되는 작업단위를 말합니다. 59 | 60 | 일반적으로 CPU는 한번에 하나의 프로세스만 관리할 수 있습니다. 61 | 62 | ### Thread 63 | 64 | 스레드(thread)는 어떠한 프로그램 내에서, 특히 **프로세스 내에서 실행되는 흐름의 단위**를 말합니다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있습니다. 65 | 66 | 실행 중인 프로그램을 프로세스라고 했는데, 스레드는 그러한 프로세스 내부에서 실행되는 흐름의 단위 입니다. 즉 프로세스의 내부에 있는 개체를 말합니다. 하나의 프로세스에는 최소 하나이상의 스레드가 존재합니다. 하나의 스레드가 있을 때는 단일 스레드라고 말하며 두개 이상의 스레드가 존재하면 멀티스레드, 다중스레드라고 말합니다. 67 | 68 | ### 차이 69 | 70 | 프로세스는 자신을 실행할 프로세서를 할당받으며 필요한 메모리공간과 데이터등을 할당받습니다. 71 | 72 | 그리고 스레드란 이러한 프로세스 안에서 실행되는 흐름의 단위로써, 프로세스 안에서의 주소나 데이터를 스레드 간에 공유하면서 실행됩니다. 73 | 74 | 스레드가 존재함으로써 데이터와 같은 자원을 메모리에 할당하는 동작이 줄어들어서 자원을 효율적으로 관리하고 운영할 수 있습니다. 또한 프로세스간의 전환 속도보다 스레드간의 전환 속도가 빠르게 됩니다. 75 | 76 | 하지만 스레드를 사용하면 동일한 데이터를 전역 변수로 이용하므로 다양한 문제가 발생될 수 있다는 단점이 있습니다. 77 | 78 | 79 | ## 멀티 프로세싱, 멀티 태스킹, 멀티 쓰레딩의 차이 80 | 81 | ### 멀티 프로세싱 82 | 83 | 84 | 멀티 프로세싱은 한마디로 말해서 **'두개 이상, 다수의 프로세서 (CPU) 가 협력적으로 작업을 동시에 처리하는 것'** 입니다. 85 | 86 | 각각의 프로세서가 하나의 작업만을 처리하는 것이 아니라 다수의 작업을 처리하며, 하나의 작업은 하나의 프로세서에 의해 처리되는 것이 아니라 다수의 프로세서에 의해 처리됩니다. 87 | 88 | 멀티 프로세싱을 하는 장점으로는 여러가지가 있습니다. 89 | 90 | 먼저 프로세서를 여러 개 사용하여 여러 개의 작업을 동시에 수행함으로써 작업 속도를 높일 수 있습니다. 91 | 92 | 또한, 만약 하나의 프로세서가 하나의 작업만을 처리한다면 특정 프로세서가 고장이 났을 때 해당 작업은 정지됩니다. 하지만 멀티 프로세싱을 사용한다면 작업은 정지되지 않습니다. 단지 속도가 느려지는 정도의 손해만 발생하는 등 신뢰성이 증가합니다. 93 | 94 | **즉, 여러 프로세서를 사용하여 여러 작업(프로세스)을 동시에 수행하는 것 입니다.** 95 | 96 | ### 멀티 태스킹 97 | 98 | 99 | 100 | 다수의 Task(프로세스보다 보다 확장된 개념)를 운영체제의 스케줄링에 의해 번갈아 가면서 수행하는 것 입니다. 프로세서가 각각의 Task를 조금씩 자주 번갈아가면서 처리하기 때문에 사용자는 마치 동시에 여러 Task가 수행되는 것처럼 보게 됩니다. 우리가 컴퓨터로 워드를 작성하면 멜론 PC로 노래를 듣는 것 역시 멀티태스킹 입니다. 101 | 102 | **즉, 하나의 프로세서 상에서 여러 작업(프로세스)을 번갈아가며 (분할) 동시에 가깝게 보이도록 수행하는 것 입니다.** 103 | 104 | ### 멀티 쓰레딩 105 | 106 | 하나의 프로세스 내에는 여러 개의 스레드가 존재하게 됩니다. 또한 이런 다수의 스레드는 하나의 데이터 자원을 공유 합니다. 때문에 메모리에 대한 효율성을 가질 수 있습니다. 107 | 108 | 멀티 프로세싱과의 차이점을 말하면, 109 | 멀티 스레딩은 **하나의 프로그램 안에서 병렬 처리의 이점을 보는 것**이며 110 | 멀티 프로세싱은 여러 개의 프로그램들을 병렬로 처리할 수 있는 것입니다. 111 | 112 | 멀티 스레딩의 장점으로는 **자원을 공유하여 메모리에 대한 효율성을 가져올 수 있는 것과 이로 인해 경제성 또한 증가하는 것** 등이 있습니다. 더불어 프로세스 생성과 소멸에 비해 스레드 생성 소멸이 훨씬 적은 자원을 필요로 합니다. 또한 멀티 프로세스는 서로 간의 자원이 공유되지 못하기 때문에 자원 전달을 위해서는 IPC를 구현해야 하며, 멀티 스레딩에 비해 운영체제에 부담을 줄 수 있습니다. 113 | 114 | 그러나 멀티 스레딩은 동시에 복수 개의 코드가 같은 주소 공간에서 실행됨으로써 서로 간섭하고 영향을 주는 경우가 빈번하여 주소 공간 분리의 이점이 없고, 스레드간의 실행 순서를 전혀 예측 할 수 없어 디버깅이 어렵습니다. 또한 **공유자원(메모리지역의 전역변수)을 보호하기가 어렵습니다.** (Critical Section 문제) 115 | 116 | #### Deadlock 117 | 멀티 스레드 중 스레드간에 대기 상태가 종료 되지 않아 무한정 대기만 하는 비정상적인 상태입니다. 118 | 119 | ### 정리 120 | 121 | 이를 총 정리하자면 다음과 같습니다. 122 | > Multi-processing : run in parallel on several processors 123 | Multi-tasking : run several processes in parallel 124 | Multi-threading : run several threads in parallel and manged by the process 125 | 126 | 127 | 128 | 129 | ### Reference: 130 | - http://12bme.tistory.com/184 131 | - http://donghoson.tistory.com/14?category=799810 132 | - http://eaco.tistory.com/1 133 | -------------------------------------------------------------------------------- /additional_topics/operationQueue.md: -------------------------------------------------------------------------------- 1 | # OperationQueue 2 | 3 | Concurrency Programming을 위한 기법중 하나로 특정 작업을 동시에 혹은 백그라운드로 처리하기 위한 용도의 개념입니다. 4 | 5 | 작업 큐(Operation Queue) 라는 방식은 비동기식으로 수행해야 하는 작업을 캡슐화하는 개체 지향 방식으로 병렬 프로그래밍을 하기 위한 개념입니다. 큐에다 단일 작업을 필요한 것 만큼 넣어놓으면 해당 작업을 알아서 꺼내어 별도의 스레드에서 실행시켜줍니다. 큐(Queue)라고 해서 한번에 하나씩 처리가 되는 것이 아니라, 개별 작업을 개별 스레드에서 가능한 만큼 병렬로 한번에 실행합니다. 멀티스레딩의 스레드 풀 관리의 비효율성에 대한 좋은 대안입니다. 6 | 7 | ## Operation 8 | 이 클래스는 오퍼레이션큐에 넣기 위한 단위 클래스입니다. 즉 모든 작업은 이 Operation 타입의 클래스로 구현되어야 합니다. 9 | 10 | 예제의 main() 이라는 오바라이드 메소드가 실제로 병렬로 작동할 코드입니다. 11 | ```swift 12 | class MyOperation: Operation { 13 | var index: Int? 14 | override func main() { 15 | print("From My Operation \(self.index)") 16 | } 17 | init(index: Int) { 18 | super.init() 19 | self.index = index 20 | } 21 | } 22 | ``` 23 | 24 | 모든 operation 객체는 다음과 같은 주요 기능을 지원합니다. 25 | 26 | - operation객체간의 그래프 기반 종속성 설정 지원. 이러한 종속성은 종속 된 모든 작업이 실행을 완료할 때까지 주어진 operation이 실행되는 것을 방지합니다. 27 | 28 | - operation의 main task가 완료된 후에 실행되는 optional completion블록을 지원합니다.(OS X v10.6이상에만 해당) 29 | 30 | - KVO알림을 사용하여 operation의 실행 상태 변경 모니터링을 지원합니다. 31 | 32 | - operation 우선 순위 지정을 지원하므로, 상대적인 실행 순서에 영향을 줍니다. 33 | 34 | - 실행 중 조작을 정지 할 수 있는 canceling semantics기능을 지원합니다. 35 | 36 | operation은 앱의 동시성 수준을 향상시킬 수 있도록 설계되었습니다. operation은 앱의 동작을 단순한 개별 청크로 구성하고, 캡슐화하는 좋은 방법이기도 합니다. 앱의 main쓰레드에서 코드를 실행하는 대신 하나 이상의 operation 객체를 큐에 제출하고, 하나 이상의 개별 쓰레드에서 해당 작업을 비동기적으로 수행 할 수 있습니다. 37 | 38 | 방식은 다르지만 Operation역시 스레드에서 동작합니다. 따라서 sleep(forTimeInterval:)과 같이 Thread에서 제공되는 기능을 활용할 수 있습니다. 39 | 40 | 또한 Operation클래스는 다음 Key-path에 대해 key-value observing(KVO)를 준수합니다. 원하는 대로 이러한 속성을 관찰하여 응용 프로그램의 다른 부분을 제어할 수 있습니다. 41 | - isCancelled 42 | 43 | - isConcurrent 44 | 45 | - isExecuting 46 | 47 | - isFinished 48 | 49 | - isReady 50 | 51 | - dependencies 52 | 53 | - queuePriority 54 | 55 | - completionBlock 56 | 57 | 58 | ## OperationQueue 59 | 이러한 Operation 객체들을 아래와 같이 큐를 만들어 병렬처리를 할 수 있습니다. 해당 클래스의 인스턴스를 만들면 병렬작업이 시작됩니다. 60 | ```swift 61 | class MyWork { 62 | let queue = OperationQueue() 63 | init() { 64 | self.queue.addOperation(MyOperation(index: 0)) 65 | self.queue.addOperation(MyOperation(index: 1)) 66 | self.queue.addOperation(MyOperation(index: 2)) 67 | self.queue.addOperation(MyOperation(index: 3)) 68 | self.queue.addOperation(MyOperation(index: 4)) 69 | } 70 | } 71 | ``` 72 | 인스턴스 생성시 콘솔에 찍히는 테스트 결과입니다. 73 | ```swift 74 | FFFFFFrrrrrroooooommmmmm MMMMMMyyyyyy OOOOOOppppppeeeeeerrrrrraaaaaattttttiiiiiioooooonnnnnn 302451 75 | ``` 76 | 77 | **addOperation(operation: Operation!)** 78 | 79 | 앞서 본 예제에서 볼 수 있듯이 실제 오퍼레이션(Operation을 상속받은 클래스의 인스턴스)을 큐에 추가하는 담당입니다. 추가하자 마자 바로 작업이 실행됩니다. 80 | 81 | 82 | **cancelAllOperations()** 83 | 84 | 이름처럼 모든 오퍼레이션을 취소시키는 역활입니다. 하지만 이것 만으로 반드시 모든 오퍼레이션이 취소되는 것은 아닙니다. Operation 자체에서도 isCancelled 프로퍼티를 체크해서 종료하도록 처리를 해 놔야만 실제로 작업이 종료됩니다. 85 | 86 | 이 외에도 여러 method 및 property가 존재합니다. 87 | 88 | 89 | 90 | ### Reference: 91 | - http://seorenn.blogspot.com/2014/06/swift-nsoperationqueue.html 92 | - https://developer.apple.com/documentation/foundation/operationqueue 93 | - https://zeddios.tistory.com/510 94 | -------------------------------------------------------------------------------- /additional_topics/operation_vs_gcd.md: -------------------------------------------------------------------------------- 1 | # OperationQueue vs DispatchQueue 2 | 3 | 먼저 공통적으로 쓰레드 생성 및 관리에 대해 걱정 할 필요 없이 실제로 수행하려는 작업에 집중 할 수 있다는 장점이 있습니다. 쓰레드를 사용하면 수행 할 작업과 쓰레드 자체의 생성 및 관리를 위한 코드를 모두 작성해야합니다. 그러나 OperationQueue와 DispatchQueue는 시스템이 대신 쓰레드 생성 및 관리를 처리합니다. 이점은 시스템이 단일 앱보다 훨씬 효율적으로 쓰레드를 관리 할 수 있다는 것입니다. 시스템은 사용가능한 자원 및 현재 시스템 조건에 따라 동적으로 쓰레드 수를 조절 할 수 있습니다. 또한 시스템은 일반적으로 쓰레드를 직접 작성 한 경우보다 빨리 task를 실행 할 수 있습니다. 4 | 5 | 차이점은 OperationQueue는 Objective-C level API이고, GCD는 C level API라는 것 입니다. 때문에 OperationQueue는 비동기식으로 수행해야 하는 작업을 class와 같이 캡슐화하는 객체 지향 방식으로 병렬 프로그래밍이 가능합니다. 6 | 7 | 더불어 Operation과 OperationQueue는 GCD에 비해 추가적인 기능을 제공하며 여러 operation에 의존성을 부여할 수도 있습니다. 뿐만 아니라 재사용도 가능하며 취소 혹은 일시정지와 같은 기능도 가능합니다. 또한 Operation은 KVO 기술을 완벽하게 사용할 수 있습니다. 그래서 Operation이 실행되기 시작하면 NotificationCenter를 통해 상태 변화에 대한 노티를 받을 수 있습니다. 또한 메모리 제한적인 환경에서 DispatchQueue는 돌아가는 작업의 수를 시스템에서 모두 관리하기 때문에 제한할 수 없지만, OperationQueue는 제한을 걸 수 있습니다. 8 | 9 | 그러나 OperationQueue는 GCD dispatch queues에 비해 오버헤드가 조금 더 많은 백그라운드 스레드를 사용합니다. 10 | 11 | 요약하자면, OperationQueue는 취소해야 하거나 복잡한 종속성이 있는 장기 실행 작업에 더 적합합니다. GCD dispatch queues은 성능 및 메모리 오버헤드를 최소화해야 하는 짧은 작업에 적합합니다. 12 | 13 | >작업이 그리 복잡하지 않고 최적의 CPU 성능이 필요한 경우 GCD 선호 14 | 15 | >작업이 복잡하고 블록 및 종속성 관리를 취소하거나 일시 중단해야 하는 경우 OperationQueue 선호 16 | 17 | ### Reference: 18 | - https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html 19 | -------------------------------------------------------------------------------- /additional_topics/performance_improvement.md: -------------------------------------------------------------------------------- 1 | # Performance Improvement 2 | 3 | 4 | 5 | 당연히 퍼포먼스의 개선은 하드웨어적인 측면과 소프트웨어적인 측면을 이야기할 수 있습니다. 하드웨어적으로는 더 좋은 CPU, 더 큰 RAM, 더 좋은 GPU 등등 을 이야기할 수 있습니다. 그러나 이러한 하드웨어적인 측면은 배제하고 소프트웨어적으로 퍼포먼스 향상을 이야기하고자 합니다. 6 | 7 | 소프트웨어적으로 퍼포먼스를 향상시키기 위해서는 **퍼포먼스 오버헤드를 줄이고 런 타임이 아닌 컴파일 시점에서 할당 및 위치 파악을 하는 것** 이 주요한 Optimization 기법이라고 말할 수 있습니다. 이러한 퍼포먼스 향상을 위한 Optimization 요소로 3가지를 꼽습니다. 8 | 9 | 1. [Memory Allocation](./allocation.md): Stack or Heap 10 | 11 | 2. [Reference Counting](./arc.md): No or Yes 12 | 13 | 3. [Method Dispatch](./method_dispatch.md): Static or Dynamic 14 | 15 | 각각의 항목에 대해서는 해당 문서를 참조하시면 더 자세한 설명을 보실 수 있습니다. 16 | 17 | 18 | ## 1. Memory Allocation: Stack! 19 | 20 | 주로 메모리 할당은 Stack과 Heap을 이용하여 변수와 상수들을 저장하곤 합니다. Heap은 스택과 같이 자동적으로 객체에 메모리를 할당 및 반환할 수 없습니다. 외부적으로 이러한 일을 해야합니다. (개발자가 직접하거나 Swift처럼 ARC, 자바의 GC 등이 이러한 일을 합니다.) 21 | 22 | 따라서 빈 곳 찾기, 메모리 할당, 참조 추적, 메모리 반환 관리 등의 복잡한 과정이 필요합니다. 더불어 과정이 Thread Safe해야 함으로 lock 등의 동기화 동작으로 인해 성능이 저하됩니다. (performance overhead) 23 | 24 | 반면 Stack 할당은 단순히 스택포인터 변수 값만 바꿔주는 정도입니다. 또한 Heap에서의 메모리 할당 /반환이 동적으로 프로그램 런타임 동안에 일어나게 되므로 스택에 비해 느릴 수 밖에 없습니다. 이것이 value type이 reference type보다 빠른 이유입니다. 가능하면 Heap보다는 Stack을 사용하는 것이 퍼포먼스 향상에 일반적으로!(항상은 아닙니다.) 도움이 됩니다. 25 | 26 | ## 2. Reference Counting: No! 27 | 28 | ARC는 GC의 대표적인 2가지 부작용을 경감합니다. 29 | 30 | - 1. GC가 언제 실행될 지 코드 상에서 알 수가 없습니다. 31 | 32 | - 2. GC로 인한 퍼포먼스 저하 33 | 34 | 물론 ARC는 GC와는 다르게 background process가 아니고, 런타임에 비동기적으로 객체를 메모리에서 반환한다는 점에서 일반적으로 GC보다 퍼포먼스 측면에서 efficient합니다. 하지만 매번 참조할 때 마다 참조값을 검사해야 하므로 많은 수의 단위 객체를 사용하게 되면 그 검사에 대한 부하가 커지고, 참조하는 단위 객체 사이에 서로 참조하게 되면 순환 참조 오류로 인해 잘못된 참조 파괴가 생기거나 또는 단위 객체가 고아가 될 수 있다는 점에서 역시 퍼포먼스 오버헤드가 존재한다고 말할 수 있습니다. 때문에 참조 객체의 수를 줄이는 것이 퍼포먼스 향상에 일반적으로!(항상은 아닙니다.) 도움이 됩니다. 35 | 36 | ## 3. Method Dispatch: Static! 37 | 38 | Method Dispatch 란 쉽게 말해 준비 중인 메서드를 호출하여 실행하는 것으로, Dispatch의 목표는 프로그램에서 특정 메서드 호출의 실행 코드를 찾을 수 있는 메모리 위치를 CPU에 알려주는 것입니다. 이러한 디스패치가 Static하게 컴파일 시에 미리 결정되어 런 타임 시 찾는 과정 없이 그 주소로 바로 점프할 수도 있고 Dynamic하게 런타임 시에 메소드 주소를 찾아 그 주소로 점프할 수 도 있습니다. 당연히 런타임 시 메소드 주소를 찾는 과정이 없는 Static한 메소드 디스패치 방법이 퍼포먼스 향상에 일반적으로!(항상은 아닙니다.) 도움이 됩니다. 39 | 40 | ## 구체적 Tips 41 | 42 | ### 1. Structs vs Classes (When to use what) 43 | 44 | Struct 객체는 스택에 할당되고 클래스 객체는 힙에 할당되므로 Struct가 퍼포먼스적으로 더 빠르다고 할 수 있습니다. 일반적으로 class는 성능에 상관없이 Reference semantics과 상속이 필요한 경우 등에 사용합니다. 이러한 class와 struct 안에서도 다소 차이가 존재합니다. 45 | 46 | #### class 47 | 48 | class보다 final class를 사용하는 것이 고성능 입니다. 49 | 50 | class는 Reference type이므로 Memory allocation으로 Heap을 사용하고 Reference Counting을 합니다. 또한 Dynamic한 메소드 디스패치 방법을 사용합니다. 반면 final class의 경우 동일하게 Memory allocation으로 Heap을 사용하고 Reference Counting을 하지만, Static하게 메소드 디스패치한다는 차이점이 존재합니다. 51 | 52 | 프로젝트 내부 어디에서도 상속되지 않는다고 확신할 때 final 키워드를 사용하시면 됩니다. 53 | 54 | #### struct 55 | 56 | struct 자체가 참조 타입이 아닌 값 타입이므로 고성능 입니다. 57 | (Memory allocation = stack / Method dispatch = dynamic) 58 | 59 | 그러나 내부적으로 참조 타입의 변수나 상수를 갖는 struct는 Reference Counting을 해야하므로 성능이 저하됩니다. 따라서 값의 제한이 가능하면 enum등의 value type으로 변경하고 다수의 class들을 하나로 합쳐 성능을 개선할 수 있습니다. 60 | 61 | ### 2. Use Protocol Oriented Programming 62 | 63 | 상속은 dynamic dispatch만을 사용하는 반면 protocol은 dynamic 또는 static dispatch를 유동적으로 사용할 수 있습니다. 더욱이 protocol은 value type인 struct에도 적용이 가능합니다. 즉, value semantics에서 다형성을 지원합니다. 64 | 65 | 아래의 예시는 다형성을 위한 프로토콜 requirement로서 Dynamic하게 dispatch합니다. 66 | 67 | ```swift 68 | protocol Movable { 69 | func walk() 70 | } 71 | ``` 72 | 73 | 그러나 direct call을 위한 protocol extension은 아래와 같이 Static하게 dispatch 합니다. 74 | 75 | ```swift 76 | extension Movable { 77 | func crawl() { 78 | print(“Default crawling”) 79 | } 80 | } 81 | ``` 82 | 83 | 아래는 평범한 케이스입니다. 84 | 85 | 86 | ```swift 87 | struct Animal: Movable { 88 | func walk() { 89 | print("Animal is walking proudly") 90 | } 91 | func crawl() { 92 | print("Animal is crawling silently") 93 | } 94 | } 95 | let raccoon = Animal() 96 | raccoon.walk() // Animal is walking proudly 97 | raccoon.crawl() // Animal is crawling silently 98 | ``` 99 | 100 | 그러나 아래의 특수한 예제에서 dynamic과 static의 차이를 확인할 수 있습니다. 101 | 102 | ```swift 103 | let wolf: Movable = Animal() 104 | wolf.walk() // Animal is walking proudly 105 | wolf.crawl() // Default crawling 106 | ``` 107 | 108 | wolf를 Movable 프로토콜을 따르도록 했고 Swift 컴파일러는 이 변수를 existential container라는 곳으로 이동시킵니다. 타입을 명시하는 대신, 따르는 프로토콜만 알려주었으므로, static dispatch를 따르는 existential container내에서 생성되면서, 이러한 결과가 발생한 것입니다. 만일 dynamic하게 디스패치함으로서 원하는 결과를 얻고 싶다면 아래와 같이 프로토콜 요구사항을 추가하면 됩니다. 109 | 110 | ```swift 111 | protocol Movable { 112 | func walk() 113 | func crawl() 114 | } 115 | ``` 116 | 117 | ### 3. Use Generics 118 | 119 | 제너릭의 사용은 Dynamic dispatch 없이 다형성의 파워를 갖도록 도와줍니다. 120 | 121 | ```swift 122 | func power(_p: ProtocolType) { 123 | // this method will be dynamically dispatched 124 | } 125 | 126 | power(SomeTypeWhichConformsToProtocolType) 127 | 128 | func power(_p: T) 129 | // this method will be statically dispatched 130 | } 131 | 132 | power(SomeTypeWhichConformsToProtocolType) 133 | ``` 134 | 135 | ### 4. Use private and fileprivate 136 | 137 | private나 fileprivate 키워드를 쓰면 file 선언의 범위를 제한할 수 있습니다. 이것은 컴파일러가 자동적으로 final 키워드를 추론할 수 있게 해주며, 간접적인 콜을 제거시켜 줍니다. 138 | 139 | ### Reference: 140 | 141 | - https://blog.usejournal.com/swift-performance-tips-55dc5688808b 142 | 143 | - https://academy.realm.io/kr/posts/letswift-swift-performance/ 144 | -------------------------------------------------------------------------------- /additional_topics/rxswift.md: -------------------------------------------------------------------------------- 1 | # RxSwift 2 | 3 | logo.png 4 | 5 | > RxSwift is a library for composing asynchronous and event-based code by using observable sequences and functional style operators, allowing for parameterized execution via schedulers. 6 | 7 | *By Marin Todorov. ‘RxSwift - Reactive Programming with Swift.’ iBooks.* 8 | 9 | Keywords: 10 | observable(관찰가능한), asynchronous(비동기), functional(함수의), via schedulers(스케줄러를 통해) 11 | 12 | 다시 표현하자면 다음과 같습니다. 13 | > 새로운 데이터에 대해 코드가 순차적이며 독립적으로 반응하도록 비동기적 프로그래밍하는 것 14 | 15 | 이러한 RxSwift의 장점을 정리하자면 다음과 같습니다. 16 | 17 | > 1. 다수의 비동기 이벤트에 대해 쉽게 관찰함으로써 기존 객체간 소통 (Delegation, Notification, KVO, Callback 등) 에 비해 데이터 업데이트 / 바인딩 및 이벤트 처리가 용이합니다. 18 | 19 | > 2. 이벤트 처리에 있어서 다양한 Operator가 존재합니다. (ex) Map, Merge, TimeInterval 등...) 20 | 21 | > 3. 다수의 비동기 이벤트 처리를 해야하는 경우, 기존보다 용이하게 쓰레드를 관리할 수 있습니다. 22 | 23 |
24 | 25 | 요약하자면, "다수의 (비동기) 이벤트에 대해 용이한 쓰레드 관리로 여러 Operator의 형태의 객체간 소통을 지원할 수 있다" 라고 말할 수 있습니다. 26 | 27 | 28 | 29 | RxSwift는 observer패턴의 확장인 observable이라는 객체를 사용하여 비동기적으로 값을 배출 할수 있고 이러한 1개 혹은 다수의 이벤트를 관찰하고 시퀀스로 반응함으로써 소통합니다. 30 | 31 | observable.png 32 | 33 | > Observeable : 34 | 구독(Subscribe)당하는 오브젝트 35 | 이벤트를 시간의 흐름에 따라 전달하는 전달자 36 | 37 | > Observer : 38 | 구독하는 오브젝트, 3가지 이벤트(next, completed, error)에 대해서만 구독 가능 39 | 40 | > Operator : 41 | filter, map 등의 연산자, 여러 조합으로 다양한 결과를 도출 가능 42 | 43 | - [Reactive Function을 시각적으로 표현해주는 사이트](http://rxmarbles.com/ ) 44 | 45 | > Scheduler : 46 | Dispatch queue와 비슷 47 | 48 | > Dispose : 49 | Observables의 사용이 끝나면 메모리를 해제해야 합니다. 그 때 사용할 수 있는것이 Dispose입니다. 50 | 51 | 52 | 이를 통해서 하나의 방식으로 다수의 비동기 처리가 가능해지기 때문에 객체간 비동기 소통이 매우 편리해 집니다. 즉, NotificationCenter, Delegate, KVO, GCD .. 등.. Observable로 단일화 됩니다. 더불어 비동기 코드를 작성할 때 자주 발생하는 실행 순서가 보장되지 않는다는 것과 Mutable 데이터를 공유하고 있다는 점에 대해 효율적인 접근이 가능합니다. 53 | 54 | 델리게이트의 수많은 프로토콜과 더이상 부딪히지 않아도 되며, Notification Center보다 더욱 간결하고 명확하게 관찰 가능합니다. 55 | 56 | 참고: [객체간 (비동기) 소통](../pattern/object.md) 57 | 58 | ### Reference 59 | 60 | - http://reactivex.io/documentation/operators.html 61 | -------------------------------------------------------------------------------- /additional_topics/thread.md: -------------------------------------------------------------------------------- 1 | # Thread 2 | 3 | Concurrency Programming을 위한 기법중 하나로 특정 작업을 동시에 혹은 백그라운드로 처리하기 위한 용도의 개념입니다. 4 | 5 | OS X 또는 iOS 의 각 프로세스(어플리케이션)는 하나 또는 그 이상의 쓰레드로 구성됩니다. 이때 하나의 쓰레드는 프로그램 내에서의 하나의 실행 단위를 나타냅니다. 모든 어플리케이션은 메인 함수를 실행하는 싱글 쓰레드로 시작합니다. 어플리케이션은 특별한 역할의 코드를 실행할 추가적인 쓰레드들 역시 생성할 수 있습니다. 6 | 7 | 어플리케이션이 새로운 쓰레드를 생성했을 때, 그 쓰레드는 어플리케이션의 프로세스 공간에서 독립적인 개체가 됩니다. 각 쓰레드는 각각의 실행 스택 (execution stack) 을 가지며, 각각 커널에 의해 런타임에 스케줄됩니다. 쓰레드는 다른 쓰레드나 다른 프로세스와 통신할 수 있고, I/O 오퍼레이션을 수행할 수 있으며, 그 외 여러가지 것들을 수행 할 수 있습니다. 하나의 어플리케이션이 사용하는 모든 쓰레드들은 같은 가상 메모리 공간을 공유하고 프로세스 자체에 대한 엑세스 권한도 똑같이 가집니다. 8 | 9 | Swift에서 Thread는 상속받아 구현하는 형태보다는 별도의 메소드(셀렉터)를 스레드 함수로 등록하는 형태로 만들어서 동작시킵니다. 이때 사용 할 수 있는 방법은 두 가지가 있습니다. 10 | 11 | ```swift 12 | // 스레드를 만들어 바로 시작 13 | Thread.detachNewThreadSelector("runLoop", toTarget: self, with: nil) 14 | 15 | // 스레드를 만들고 시작 16 | let thread = Thread(target: self, selector: "runLoop", object: nil) 17 | thread.start() 18 | 19 | // 실행 메소드 20 | func runLoop() { 21 | // 취소되기 전까지 무한루프 22 | while Thread.current.isCancelled == false { 23 | print("From MyThreadingClass") 24 | 25 | // 1초간 휴식 26 | Thread.sleep(forTimeInterval: 1) 27 | } 28 | } 29 | ``` 30 | 31 | 위 두 가지 방법에서 모두 문자열로 “runLoop” 라는 셀렉터 이름을 넘겨주는데 이는 위 코드를 실행시키는 클래스에서 스레드로 돌릴 메소드 이름입니다. 따라서 이 이름은 유저가 임의로 설정하면 됩니다. 32 | 33 | 그러나 Thread는 low-level tool이기 때문에 관리하기가 매우 어렵습니다. 현재 시스템 로드 및 기본 하드웨어 따라 동적으로 어플리케이션의 최적 스레드 수가 변화할 수 있으므로 올바른 스레드 솔루션을 구현하는 것은 거의 불가능합니다. 또한 일반적으로 스레드와 함께 사용되는 동기화 메커니즘은 성능 향상을 보장하지 않고 소프트웨어 설계에 복잡성과 위험을 가중시킵니다. 34 | 35 | 때문에 OperationQueue및 GCD의 사용이 권장됩니다. 36 | 37 | ### Reference: 38 | - http://seorenn.blogspot.com/2014/06/swift-nsthread.html 39 | - https://developer.apple.com/documentation/foundation/thread 40 | - https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html 41 | -------------------------------------------------------------------------------- /additional_topics/threadsafe.md: -------------------------------------------------------------------------------- 1 | # 프로세스 동기화 2 | 3 | ## Critical Section (임계영역) 4 | 5 | 동일한 자원을 동시에 접근하는 작업을 실행하는 코드 영역을 임계 영역이라고 합니다. 프로세스들이 이러한 임계영역을 함께 사용할 수 있는 프로토콜을 설계해야 합니다. 6 | 7 | ## 해결을 위한 기본 조건 8 | 9 | ### Mutual Exclusion 10 | 11 | ### Progress 12 | 13 | ### Bounded Waiting 14 | 15 | 16 | 17 | 18 | Swift에서 굳이 atomic을 빼도 되는 것에 대한 이유를 조심스럽게 value type의 적극적인 사용이라고 추측해봅니다. 19 | Objective-C의 경우 거의 모든 것이 object로 다루어지고 이것이 reference type이기 때문에 thread를 사용하는 순간 어떻게든 문제를 일으킬 수 밖에 없는 여지를 가지고 있죠. 그러다보니 atomic에 대한 요구가 강했던 것 같습니다. 20 | 반대로 Swift의 경우 많은 부분 struct같은 value type으로 이루어지기 때문에 별도의 다른 thread에서 동일한 value를 굳이 접근하지 않고도 많은 부분들이 해결되죠. 21 | 예를 들어 특정 thread에서 사용하기 위해서 struct를 가져가는 순간 변경되면 바로 copy되기 때문에 원본과 분리된 데이터로 다루게 되니 sync를 맞출 일이 없어지겠죠. 22 | 하지만 여전이 class같은 instance type을 사용하면 해당 문제를 벗어날 수 없지만요. 23 | 그리고 사실 property를 atomic으로만 했다고 해서 concurrent programming 상에서 벌어지는 수많은 이슈들을 감당할 수는 없죠. 24 | 25 | Thread-safe in Swift # 26 | Swift에서 무척이나 흥미로운 점 중 하나는 threading 과 관련하여 언어단에서는 어떠한 처리도 없다는 점 입니다. Objective-C 에서는 @synchronized 또는 atomic 속성을 통해 thread-safe 한 변수를 선언할 수 있었는데 가장 최근에 나온 swift 에서 이런 속성들이 빠져있습니다. 27 | 28 | 하지만 플랫폼에서 API를 통해 thread-safe 한 환경을 만들 수 있도록 기능을 제공하고 있습니다. 29 | 30 | Thread-safe 한 singleton 생성 # 31 | Objective-C 와 Swift 1.1 버전까지 가장 전통적인 singleton 생성 방법은 dispatch_once를 사용한 아래와 같은 방법입니다. 32 | 33 | class SingletonA { 34 | class var sharedInstance: SingletonA { 35 | struct Static { 36 | static var onceToken: dispatch_once_t = 0 37 | static var instance: SingletonA? = nil 38 | } 39 | 40 | dispatch_once(&Static.onceToken) { 41 | Static.instance = SingletonA() 42 | } 43 | 44 | return Static.instance! 45 | } 46 | } 47 | Singleton 객체가 생성되기 전 두 개의 thread에서 동시에 접근할 경우 이 객체가 두번 생성이 되는 문제가 발생할 수 있는데 dispatch_once를 사용함으로써 하나의 thread에서만 접근이 되도록 보장하는 방식입니다. 48 | 49 | 하지만 Swift 1.2 버전부터는 애플에서 공식적으로 아래와 같은 방식을 권장하고 있습니다. 50 | 51 | class SingletonA { 52 | static let sharedInstance = SingletonA() 53 | } 54 | 이와 같이 선언하면 swift에서 자체적으로 여러개의 thread 에서 동시에 접근하더라도 단 한번만 수행하여 생성하도록 thread-safe 하게 설계되어있습니다. 55 | 56 | https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html 57 | 58 | Thread-safe 변수 # 59 | Objective-C 에서 thread-safe 를 보장해주던 atomic 속성이 사라졌지만 swift 에서는 다양한 API 를 제공해주고 있습니다. 60 | 61 | pthread : pthread_mutex_t 는 recursive lock으로 구성할 수 있는 방식입니다. 62 | Reader/writer lock : pthread_rwlock_t 는 말그대로 reader/writer 관련 lock API 입니다. 63 | dispatch queue : dispatch_queue_t 는 concurrent queue를 구성하고 barrier block 을 사용할 수 있습니다. 또한 비동기 lock을 지원합니다. 64 | NSOperationQueue : block lock 으로 사용할 수 있으며 dispatch queue와 마찬가지로 비동기 실행을 지원합니다. 65 | NSLock : NSlock은 Objective-C 클래스로 lock을 지원하며 쉽게 사용 가능하고 비교적 빠르게 수행됩니다. 66 | OSSpinLock : OSSpinLock은 lock 이 자주 일어나는 곳에서 사용하기 좋은 방식입니다. 67 | Swift 에서는 @synchronized 나 atomic 속성이 더이상 없지만 GCD, dispatch_queue_t 또는 관련 API를 제공하여 lock 관련 환경을 최적화 할 수 있도록 하고 있습니다. 68 | -------------------------------------------------------------------------------- /assets/roadmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/assets/roadmap.png -------------------------------------------------------------------------------- /assets/swift_logo.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/assets/swift_logo.PNG -------------------------------------------------------------------------------- /basic/access.md: -------------------------------------------------------------------------------- 1 | # Access Control (접근 제어자) 2 | 3 | Swift의 접근 제어자(Access Control)에 대해서 간단히 정리해보고자 합니다. 정의를 살펴보면, 다음과 같습니다. 4 | 5 | > Access control restricts access to parts of your code from code in other source files and modules. 6 | 7 | 접근 제어자는 코드를 작성하는 한 파일에서 다른 파일에 있는 **코드에 대한 접근을 명시적으로 작성**하여 이를 관리하는 것인데, module과 source file에 따라 다른 접근을 할 수 있습니다. 8 | 9 | ## Access Control 사용 이유 10 | 11 | 에플리케이션이 커진다는 것은 다른 말로 망가질 확률이 커진다는 의미와 같습니다. 특히 로직이 망가지는 첫번째 용의자는 사용자입니다. 즉 객체를 사용하는 입장에서 객체 내부적으로 사용하는 변수나 메소드에 접근함으로서 개발자가 의도하지 못한 오동작을 일으키게 되는 것입니다. 12 | 13 | 이런 문제로부터 객체의 로직을 보호하기 위해서는 맴버에 따라서 외부의 접근을 허용하거나 차단해야 할 필요가 생깁니다. 마치 은행이 누구나 접근 할 수 있는 창구와 관계자외에는 출입이 엄격하게 통제되는 금고를 구분하고 있는 이유와 같습니다. 14 | 15 | 16 | 17 | 18 | 접근 제어자를 사용하는 또 다른 이유는 사용자에게 객체를 조작 할 수 있는 수단만을 제공함으로서 결과적으로 객체의 사용에 집중 할 수 있도록 돕기 위함입니다. 어떤 맴버에 대한 접근을 허용할 것인가를 작업자의 판단에 달렸습니다. 19 | 20 | ## Module과 Source file 21 | 22 | module이라는 것은 하나의 프레임워크를 의미합니다. 즉, import 키워드로 추가되는 것들이 module입니다. UIKit, Foundation 등이 모두 module입니다. 프로젝트의 하위에 있는 targets도 각각 모두 하나의 module입니다. source file은 각각의 module 안에 있는 파일들입니다. 예를 들어 example.swift 같은 파일들이 하나의 source file입니다. 23 | 24 | ## Swift의 5가지 접근 제어자 25 | 26 | 이제 module과 source file을 기준으로 나뉘는 5가지 접근 제어자를 알아보고자 합니다. 그리고 여기서는 특정 접근 제어자가 적용되는 대상을 entity로 서술합니다. entity는 접근제어자를 작성할 수 있는 property, method, class, struct 등의 집합을 의미합니다. 27 | 28 | - 1. open, public - 프로젝트 내의 모든 module 해당 entity에 접근할 수 있습니다. 29 | 30 | - 2. internal - default 접근 제어자로, entity가 작성된 module에서만 접근할 수 있습니다. 31 | 32 | - 3. fileprivate - entity가 작성된 source file에서만 접근할 수 있도록 합니다. 서로 다른 클래스가 같은 파일안에 있고 fileprivate로 선언되어 있다면 둘은 서로 접근할 수 있습니다. 33 | 34 | - 4. private - 특정 객체에서만 사용할 수 있도록 하는 가장 제한적인 접근제어자입니다. fileprivate과 달리 같은 파일 안에 있어도 서로 다른 객체가 private로 선언되어 있다면 둘은 서로 접근할 수 없습니다. 35 | 36 | ### open과 public의 차이 37 | 38 | 둘의 차이는 open은 다른 모듈에서 subclass가 가능하지만, public은 그렇지 않다는 것입니다. 먼저 open은 class에만 사용될 수 있습니다. 그리고 한 모듈에서 만든 class를 superClass로 하는 subClass를 다른 모듈에서 만들기 위해서는 해당 superClass가 open으로 선언되어야 합니다. 당연히 overriding도 이 규칙이 적용됩니다. 39 | 40 | 41 | ### Reference: 42 | - https://hcn1519.github.io/articles/2018-01/Swift_AccessControl 43 | - https://opentutorials.org/module/516/6061 44 | -------------------------------------------------------------------------------- /basic/appstate.md: -------------------------------------------------------------------------------- 1 | # States of Application 2 | 3 | iOS 앱의 실행 전 상태, 혹은 실행 후 상태 등을 세분화하여 알아보고, 각각의 상태별로 접근하기 위한 방식에 대해 알아보고자 합니다. 4 | 5 | ## App State 6 | 7 | 앱의 상태라는 것은 여러가지 의미를 내포한 폭넓은 의미로 받아들여질 수 있습니다만, Apple에서 정의하는 앱의 상태(App State)는 크게 5가지로 구분됩니다. 8 | 9 | 10 | 11 | - Not Running: 12 | 13 | > 아무것도 실행하지 않은 상태 14 | 15 | - InActive: 16 | 17 | > 앱이 Foreground 상태로 돌아가지만, 이벤트는 받지 않는 상태, 앱의 상태 전환 과정에서 잠깐 머무는 단계입니다. 18 | 19 | - Active: 20 | 21 | > 일반적으로 앱이 돌아가는 상태 22 | 23 | - Background: 24 | 25 | > 앱이 Suspended(유예 상태) 상태로 진입하기 전 거치는 상태, 26 | 27 | - Suspended: 28 | > 앱이 Background 상태에 있지만, 아무 코드도 실행하지 않는 상태, 시스템이 임의로 Background 상태의 앱을 Suspended 상태로 만듭니다. 29 | 30 | 31 | 위의 상태에서 몇 가지 알아두면 좋은 점들이 있습니다. 32 | 33 | > 1. Background 상태에서는 일부 필요한 추가 작업을 수행할 수 있습니다. 또한, Background 상태에서 앱을 실행하면 InActive 상태를 거치지 않고 앱이 실행됩니다.(iOS에서 홈버튼을 두 번 눌러서 앱을 전환할 때, 앱이 재시작되지 않는다면 해당 앱은 Background 상태에 있던 앱입니다.) 34 | 35 | > 2. 앱이 죽는 것(Suspended 상태에서 Not Running 상태로 진입하는 것)에는 알림을 받을 수 없습니다. 또한 Background 상태에서 Suspended 상태로 진입할 때 willTerminate 메소드가 실행되지만 이 또한 기기를 재부팅하면 실행되지 않습니다. 36 | 37 | > 3. iOS 앱의 다양한 상태가 있지만, 주요한 작업은 Active, Background 상태에서 주로 이뤄지게 됩니다. 38 | 39 | 40 | ## AppDelgate 41 | 42 | 앱의 상태에 대해서 알아보았으니, 이번에는 이 상태에 접근하기 위한 방법에 대해 알아보고자 합니다. 이 때 각각의 상태에 접근하기 위해 사용되는 파일이 AppDelegate.swift입니다. iOS 앱 프로젝트를 생성하면 AppDelegate.swift은 자동으로 생성됩니다. 43 | 44 | AppDelegate은 이름 그대로 앱과 시스템의 연결을 위해 필요한 delegate 메소드를 담고 있습니다. 다만, 이름 때문에 그런 것은 아니고, @UIApplicationMain이라는 annotation이 있기 때문에 앱에서 AppDelegate.swift을 앱과 시스템을 연결하기 위한 파일로 인식합니다. AppDelegate.swift의 코드를 살펴보면 다음과 같습니다. 45 | 46 | ```swift 47 | // AppDelegate.swift 48 | import UIKit 49 | 50 | @UIApplicationMain 51 | class AppDelegate: UIResponder, UIApplicationDelegate { 52 | 53 | var window: UIWindow? 54 | 55 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 56 | return true 57 | } 58 | 59 | func applicationWillResignActive(_ application: UIApplication) { 60 | } 61 | 62 | func applicationDidEnterBackground(_ application: UIApplication) { 63 | } 64 | 65 | func applicationWillEnterForeground(_ application: UIApplication) { 66 | } 67 | 68 | func applicationDidBecomeActive(_ application: UIApplication) { 69 | } 70 | 71 | func applicationWillTerminate(_ application: UIApplication) { 72 | } 73 | } 74 | ``` 75 | AppDelegate 객체는 UIResponder, UIApplicationDelegate을 상속 및 참조하고 있습니다. 먼저 UIResponder는 앱에서 발생하는 이벤트들을 담고 있는 추상형 인터페이스 객체로 View와 사용자의 이벤트간의 연결을 관리하는 역할을 합니다. 76 | 77 | UIApplicationDelegate은 UIApplication 객체의 작업에 개발자가 접근할 수 있도록 하는 메소드들을 담고 있습니다. 예를 들어 설명하자면, didFinishLaunchingWithOptions, applicationWillResignActive 등과 같은 메소드를 통해 앱의 상태가 변할 때 수행할 작업들을 설정할 수 있습니다. UIApplicationDelegate을 통해서 우리는 앱의 상태(Foreground, Background, Suspended 등)가 변하는 순간에 따라 앱에서 어떤 작업을 수행할 것인지 결정할 수 있습니다. 78 | 79 | ### Reference: 80 | - https://hcn1519.github.io/articles/2017-09/ios_app_lifeCycle 81 | -------------------------------------------------------------------------------- /basic/assets/coffee.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/coffee.gif -------------------------------------------------------------------------------- /basic/assets/escaping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/escaping.png -------------------------------------------------------------------------------- /basic/assets/nonescaping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/nonescaping.png -------------------------------------------------------------------------------- /basic/assets/sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/sequence.png -------------------------------------------------------------------------------- /basic/assets/types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/types.png -------------------------------------------------------------------------------- /basic/assets/val_ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/basic/assets/val_ref.png -------------------------------------------------------------------------------- /basic/basic_operator.md: -------------------------------------------------------------------------------- 1 | # Basic Operators 2 | 3 | - **대입 연산자** 4 | 5 | - = 6 | 7 | - **사칙 연산자** 8 | 9 | - +, -, *, / 10 | 11 | - **나머지 연산자** 12 | 13 | - a % b의 연산에 대해 a = (b * 몫) + 나머지 공식을 사용하여 나머지를 구함 14 | 15 | - a가 음수인 경우는 나머지 값은 음수가 됨 16 | 17 | ex) -9 % 4 // -9 = (4 * -2) + (-1)이므로 결과는 -1 18 | 19 | > b가 음수인 경우는 부호를 무시함. 즉, `a % b`와 `a % -b`의 결과는 동일 20 | 21 | - **단항 마이너스/플러스 연산자** 22 | 23 | - 단항 마이너스 연산자(-)는 부호를 변경함 24 | 25 | - 단항 플러스 연산자(+)는 실제로 하는 일은 없음. 단항 마이너스 연산자로 음수를 표시하는 코드와 대칭하여 양수를 표시할 일이 있을때 명시적으로 사용할 수 있음 26 | 27 | ```swift 28 | let a = -6 29 | let b = +a // 아무것도 안함. b는 -6 30 | ``` 31 | 32 | - **복합 할당 연산자** 33 | 34 | - 특이사항 없음. 타 언어와 동일 35 | 36 | ```swift 37 | a += 2 // a = a + 2 38 | ``` 39 | 40 | - **비교 연산자** 41 | 42 | - 스위프트에서도 항등(identity) 연산자 ===와 !==를 제공함. 단순히 값을 비교하는게 아니라 동일한 인스턴스인지를 비교 43 | 44 | - 튜플도 크기 비교가 가능. 이때 비교하는 튜플끼리는 타입 구성이 동일해야 하고 요소끼리의 비교가 성립해야 함 45 | 46 | ```swift 47 | (1, "a") < (2, "b") // (Int, String) 타입끼리의 비교이므로 가능 48 | (1, true) == (1, false) // 가능. 결과는 false 49 | (1, true) < (1, false) // 불가. true < false 비교가 성립하지 않기 때문. 50 | ``` 51 | 52 | - 튜플의 비교는 왼쪽 첫번째 요소부터 하나씩 비교하고, 두 요소가 같지 않을때까지 계속 비교 53 | 54 | ```swift 55 | (1, "zebra") < (2, "apple") // true. 첫번째 요소에서 비교가 끝남 56 | (3, "apple") < (3, "bird") // true. 문자열은 사전식 비교 57 | (4, "dog") == (4, "dog") // true 58 | ``` 59 | 60 | - **삼항 연산자** 61 | 62 | - 타 언어와 동일함. 단 가독성을 위해 남용을 피할 것. 63 | 64 | ```swift 65 | condition ? value1 : value2 66 | ``` 67 | 68 | - **nil 복합 연산자 (nil-coalescing)** 69 | 70 | - 옵셔널 변수가 값을 갖고 있으면 언래핑을 해주고 nil이면 디폴트 값을 사용 71 | 72 | ```swift 73 | var a: Int? 74 | let b = 1 75 | let c = a ?? b // 1 76 | ``` 77 | 78 | 즉 `a ?? b`는 `a != nil ? a! : b`의 단축형임 79 | 80 | a가 nil이 아닌 경우, a값을 사용하면서 즉시 표현식이 종료되므로 b부분의 코드는 실행되지 않음 (short-circuit evaluation) 81 | 82 | - **범위 연산자** 83 | 84 | - a...b는 a와 b 모두 포함, a.. false && A // A에 관계없이 전체 결과는 항상 false가 되므로 A조건 실행안함 102 | 103 | > true || B // B에 관계없이 전체 결과는 항상 true가 되므로 B조건 실행안함 104 | 105 | * 논리 연산자를 섞어서 쓰는 경우 연산자 기준 왼쪽 평가식부터 차례대로 수행함 106 | A && B || C || D 107 | // let eval1 = A && B 108 | // let eval2 = eval1 || C 109 | // let eval3 = eval2 || D 110 | 111 | * 위와 같은 경우는 헷갈리지 않게 괄호를 써서 의도를 명시적으로 표현해 주는게 좋다 112 | (A && B) || C || D 113 | -------------------------------------------------------------------------------- /basic/basic_type.md: -------------------------------------------------------------------------------- 1 | # 기본 자료형 2 | 3 | 프로그래밍 언어에서 프로그래밍을 하는 동안 정보를 저장하려면 다른 유형의 변수를 사용해야 합니다. 변수는 값을 저장하는 예약된 메모리 위치일 뿐입니다. 즉, 변수를 생성할 때 메모리에 일부 공간을 예약합니다. 변수의 데이터 유형에 따라 운영 체제는 메모리를 할당하고 예약된 메모리에 저장할 수 있는 메모리를 결정합니다. 4 | 5 | 스위프트의 타입에는 6개의 종류가 존재합니다. **4개의 named types (protocol, enum, struct, class) 와 2개의 compound type (tuple, function)** 이 스위프트의 6가지 type입니다. 6 | 7 | Basic type이라고 말해지는 Bool, Int, UInt, Float, Double, Character, String, Array, Set, Dictionary, Optional 등은 모두 named types로부터 만들어져 Swift Standard Library로 전달되는 것 입니다. 8 | 9 | 10 | 11 | - 부울 : Bool 12 | 13 | - 정수형 : Int, UInt 14 | 15 | - 실수형 : Float, Double 16 | 17 | - 문자/문자열 : String, Character 18 | 19 | - [Optional](../features/optional.md) 20 | 21 | 22 | ### Reference: 23 | 24 | - http://stack07142.tistory.com/68 25 | -------------------------------------------------------------------------------- /basic/bundle.md: -------------------------------------------------------------------------------- 1 | # The App Bundle 2 | 3 | iOS 앱을 빌드하게 되면 Xcode는 앱을 bundle로 묶어주는데, bundle은 앱과 연관된 리소스들을 모아놓은 디렉토리입니다. Bundle에는 앱 실행 파일과 supporting resources(앱 아이콘, 이미지 파일, 현지화 파일..)이 들어있습니다. 4 | 5 | ## The Information Property List File (info.plist) 6 | 7 | Xcode는 컴파일하는 동안 프로젝트의 General, Capabilities, Info tab의 정보들을 가지고 Info.plist를 생성합니다. 8 | 9 | 모든 앱은 info.plist를 가지고 있어야 합니다. 기본값이 포함되어 있지만 위에서의 경우처럼 대부분이 변경이나 추가가 필수적입니다. 탭을 통해 변경 및 추가를 하거나 직접 source에서 변경 및 추가를 할 수 있습니다. 때문에 새 프로젝트를 시작할 때 어떤 기능이 필요한지를 잘 고려해서 키를 추가해줘야 합니다. 10 | 11 | ## Declaring the Required Device Capabilities 12 | 13 | 모든 앱은 실행에 필요한 장치별 기능을 선언해야 합니다. 14 | Array를 이용해서 키의 값을 지정하는 경우, 키가 있으면 해당 기능이 필요함을 나타내고, 키가 없으면 기능이 필요하지 않음을 나타내며, 없어도 애플리케이션이 실행됩니다. 15 | Dictionary를 이용해서 키의 값을 지정하는 경우, Boolean값을 이용해서 표시하는데 true값은 필수 기능을 나타내고 false값은 디바이스에 기능이 없음을 나타냅니다. 앱에서 특정 기능을 선택할 수 있는 경우 Dictionary에 해당 키를 포함하면 안 됩니다. 16 | 17 | ## App Icons 18 | 19 | 앱 아이콘은 Image Assets에 포함되어 있어야 합니다. 자세한 내용은 Apple의 iOS Human Interface Guidelines에 나와있습니다. 20 | 21 | ## App Launch (Default) Images 22 | 23 | 앱을 실행시켰을 때 잠깐 보이는 정적인 이미지로, 앱 화면을 구성하고 사용자에게 보여질 준비가 완료되면 사라집니다. 스냅샷을 사용할 수 있을 경우, launch image가 아닌 스냅샷을 이용하게 됩니다. 24 | 25 | 앱이 foreground(현재 실행 상태)에서 background로 들어갈 때는 스냅 샷이 생성되며 다시 foreground로 돌아가면 런칭 이미지가 아닌 스냅 샷을 활용합니다. 26 | 27 | 앱을 오랫동안 실행하지 않은 경우에는 스냅 샷을 삭제하고 기존의 런칭 이미지를 활용하게 됩니다. 28 | -------------------------------------------------------------------------------- /basic/class.md: -------------------------------------------------------------------------------- 1 | # Class 2 | 3 | 기본적으로 구조체와 개념적 컨셉이 거의 유사합니다. 구조체와 클래스는 프로그래머가 데이터를 용도에 맞게 묶어 표현하고자 할 때 용이합니다. 구조체와 클래스는 프로퍼티와 메소드를 사용하여 구조화된 데이터와 기능을 가질 수 있습니다. 하나의 새로운 사용자 정의 데이터 타입을 만들어 주는 것 입니다. 다만 중요한 차이는, **구조체의 인스턴스는 값 타입이고 클래스의 인스턴스는 참조 타입이라는 것** 입니다. 4 | 5 | 아래와 같이 클래스를 정의할 수 있습니다. 6 | 7 | ```swift 8 | class Person { 9 | var height: Float = 0.0 10 | var weight: Float = 0.0 11 | } 12 | ``` 13 | 14 | 구조체와는 다르게 클래스의 인스턴스는 참조타입 이므로 상수로 선언해도 내부 프로퍼티 값을 변경할 수 있습니다. 15 | 16 | ```swift 17 | var personA: Person = Person() 18 | personA.height = 123.4 19 | personA.weight = 123.4 20 | 21 | let personB: Person = Person() 22 | personB.height = 123.4 23 | personB.weight = 123.4 24 | ``` 25 | 26 | ## Struct와 Class 27 | 28 | ### 공통점 29 | 30 | - 값을 저장하기 위해 프로퍼티를 정의할 수 있습니다. 31 | 32 | - 기능 수행을 위해 메소드를 정의할 수 있습니다. 33 | 34 | - 내부 프로퍼티에 접근하도록 서브스크립트를 정의할 수 있습니다. 35 | 36 | - 초기화될 때의 상태를 지정하기 위해 initializer를 정의할 수 있습니다. 37 | 38 | - extension을 통해 새로운 기능 추가를 확장할 수 있습니다. 39 | 40 | - 특정 기능 수행을 위해 특정 프로토콜을 준수할 수 있습니다. 41 | 42 | ### 차이점 43 | 44 | - 구조체의 인스턴스는 값 타입이고 클래스의 인스턴스는 참조 타입입니다. 45 | 46 | - 구조체는 상속 불가능하지만 클래스는 상속 가능합니다. 47 | 48 | - deinitializer는 클래스의 인스턴스에만 활용할 수 있습니다. 49 | 50 | - 타입캐스팅은 클래스의 인스턴스에만 허용됩니다. 51 | 52 | ### 선택적 사용 53 | 54 | 값 타입과 참조 타입에 대한 내용은 [여기](./value_vs_reference.md)에서 제사한 확인이 가능합니다. 55 | 56 | 애플은 가이드 라인에서 다음 조건 중 하나 이상에 해당된다면 구조체 사용을 권장합니다. 57 | 58 | - 연관된 간단한 값의 집합을 캡슐화 하는 것만이 목적일 때 59 | 60 | - 캡슐화된 값이 참조되는 것보다 복사되는 것이 합당할 때 61 | 62 | - 구조체에 저장된 프로퍼티가 값 타입이며 참조되는 것보다 복사되는 것이 합당할 때 63 | 64 | - 다른 타입으로부터 상속받거나 자신이 상속될 필요가 없을 때 65 | 66 | 또한 멀티스레딩 환경에서 구조체의 사용이 권장되고는 합니다. 레퍼런스 타입보다 값 타입을 사용하는 가장 주된 목적은 코드를 이해하기 쉽다는 것입니다. 각 인스턴스가 독립된 데이터 사본을 가지고 있다는 것은, 보이지 않는 곳에서 해당 데이터가 임의로 변경되지 않음을 보장할 수 있다는 것입니다. 특히 이는 멀티스레드 환경에서 다른 스레드가 예기치 못한 시점에 데이터를 변경해놓을 수 있는 것을 방지합니다. (보통 이런 류의 문제는 디버깅이 극히 어렵습니다.) 변경이 없으면 값타입이나 참조타입은 기본적으로 동일합니다. 값타입은 동기화 없이 스레드 간에 데이터를 전달할 수 있습니다. 따라서 안정성을 높이는 관점에서 바라볼 때 이런 모델은 코드를 보다 더 예측가능하게 만들어 줄 것입니다. 67 | 68 | 그러나 클래스 사용이 권장되는 상황 역시 분명 존재합니다. 아래는 그 예시입니다. 69 | 70 | ```swift 71 | var s: S? { 72 | didSet { 73 | print("didSet Struct") 74 | } 75 | } 76 | 77 | 78 | var c: C? { 79 | didSet { 80 | print("didSet Class") 81 | } 82 | } 83 | 84 | s = S() 85 | c = C() 86 | 87 | s?.num = 10 88 | c?.num = 10 89 | ``` 90 | 91 | 위 코드에 대한 print 결과는 아래와 같습니다. 92 | 93 | ``` 94 | didSet Struct 95 | didSet Class 96 | didSet Struct 97 | ``` 98 | 99 | Class인 경우에는 Class의 인스턴스 변수 값이 변경되어도 didSet이 호출되지 않습니다. 100 | 101 | 반면에 Struct의 경우에는 인스턴스 변수가 변경되어도 didSet이 호출 됩니다. 102 | 103 | struct의 didSet 구문안에서 struct 내부값을 변경하는 로직을 구현하면 무한루프를 만날 수 있습니다. 때문에 이러한 상황에서는 class의 사용이 권장되고는 합니다. 104 | 105 | ### Reference: 106 | 107 | - http://blog.yagom.net/530 108 | -------------------------------------------------------------------------------- /basic/codable.md: -------------------------------------------------------------------------------- 1 | # Encoding, Decoding, and Serialization 2 | 3 | JSON과 같은 외부 표현과의 호환성을 위해 데이터 유형을 인코딩 및 디코딩할 수 있도록 합니다. 4 | 5 | 많은 프로그래밍 작업 등에서는 네트워크 연결을 통해 데이터 전송, 디스크에 데이터 저장 또는 API 및 서비스에 데이터 제출 등을 수행합니다. 이러한 작업에서는 데이터가 전송되는 동안 중간 형식으로 인코딩 및 디코딩이 필요한 경우가 많습니다. 6 | 7 | Swift 표준 라이브러리는 데이터 인코딩 및 디코딩에 대한 표준화된 접근 방식을 정의합니다. 사용자는 사용자 정의 유형에 대해 인코딩 및 디코딩 가능한 프로토콜을 구현하여 이 접근 방식을 채택합니다. 이러한 프로토콜을 적용하면 인코더 및 디코더 프로토콜 구현이 데이터를 가져와서 JSON 또는 속성 목록과 같은 외부 표현으로 인코딩 또는 디코딩할 수 있습니다. 8 | 9 | 상속 목록에 Codable을 추가하면 Encodable 및 Decodable의 모든 프로토콜 요구 사항이 자동으로 호환되어 충족됩니다. Encodable은 자신을 외부표현(external representation)으로 인코딩 할 수 있는 타입이고, Decodable은 자신을 외부표현(external representation)에서 디코딩 할 수 있는 타입입니다. 10 | 11 | 12 | Array, Dictionary, Optional등의 Built-in 타입들은 Codable을 준수합니다. Codable을 채택했다는 의미는 Class, Struct, Enum을 serialize / deserialize할 수 있다는 것을 의미합니다. 아래는 Codable을 준수하는 Struct를 json형식으로 serialize한 예시입니다. 13 | 14 | ```swift 15 | struct Person : Codable { 16 | var name : String 17 | var age : Int 18 | } 19 | 20 | let encoder = JSONEncoder() 21 | let person = Person(name: "Sample", age: 100) 22 | let jsonData = try? encoder.encode(person) 23 | 24 | if let jsonData = jsonData, let jsonString = String(data: jsonData, encoding: .utf8){ 25 | 26 | print(jsonString) //{"name":"Sample","age":100} 27 | 28 | } 29 | ``` 30 | 31 | 이 예시를 Decode하도록 바꿀 수도 있습니다. 32 | 33 | ```swift 34 | let decoder = JSONDecoder() 35 | var data = jsonString.data(using: .utf8) 36 | 37 | if let data = data, let myPerson = try? decoder.decode(Person.self, from: data) { 38 | 39 | print(myPerson.name) //Sample 40 | print(myPerson.age) //100 41 | 42 | } 43 | ``` 44 | 45 | ### Reference: 46 | - https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types 47 | 48 | - https://zeddios.tistory.com/373 49 | -------------------------------------------------------------------------------- /basic/collection_type.md: -------------------------------------------------------------------------------- 1 | # 컬렉션 자료형 2 | 3 | Array, Dictionary, Set이 존재합니다. 하나의 값이 아닌 여러 값을 가질 수 있으므로 컬렉션 자료형이라고 칭합니다. **컬렉션 타입은 별도의 자료형 이름이 아닌 개념적인 자료 구조의 명칭입니다.** 기본자료형과 마찬가지로 named types로부터 만들어져 Swift Standard Library로 전달됩니다. 4 | 5 | 6 | 7 | ## Array 8 | 9 | 단순히 순서번호 (index)에 따라 값이 저장되어 있는 구조입니다. 10 | 11 | ```swift 12 | let myArray: [String] = ["a", "b", "c"] 13 | ``` 14 | 15 | ## Dictionary 16 | 17 | 사전처럼 "Key: Value"의 구조로 이루어져 값이 저장되는 구조입니다. 18 | ```swift 19 | let myDict: [String: Int] = ["a": 1, "b": 2, "c": 3] 20 | ``` 21 | 22 | ## Set 23 | 24 | 집합입니다. 여러 값들의 묶음을 말하며 순서가 존재하지 않고 동일한 값은 하나의 값으로 합쳐집니다. Set에 들어갈 수 있는 값들은 중복값이 오지 못하게 막을 수 있어야 하기 때문에 자체적으로 Hash 값을 생성할 수 있는 Hashable 자료형 (String, Int, Bool 등)들 입니다. 25 | 26 | ```swift 27 | let mySet: Set = ["a", "b", "c"] 28 | ``` 29 | -------------------------------------------------------------------------------- /basic/compound.md: -------------------------------------------------------------------------------- 1 | # Compound Types 2 | 3 | **Compound types는 이름이 없는 Swift 언어 자체에서 정의되는 타입입니다.** function, tuple 2가지의 타입이 존재합니다. Compound type은 Named types나 혹은, 다른 compound types를 포함합니다. 4 | 5 | 예를 들어서 튜플 (Int, (Int, Int)) 타입의 경우는 Named type인 Int를 첫번째 요소로, Compound type인 (Int, Int)를 두번째 요소로 갖는 compound type 입니다. 6 | -------------------------------------------------------------------------------- /basic/condition.md: -------------------------------------------------------------------------------- 1 | # 분기문 conditional 2 | 3 | 분기문은 조건문이 참일 때 해당 코드를 실행하고, 거짓이면 다음으로 넘어가라는 의미를 지닙니다. 4 | 5 | ## if else 분기문 6 | 7 | if 조건문 {참일 때 실행} else {거짓일 때 실행} 8 | 9 | ```swift 10 | let temperature = 90 11 | if temperature <= 32 { 12 | // code 13 | } else if temperature >= 86 { 14 | // code 15 | } else { 16 | // code 17 | } 18 | ``` 19 | 20 | ## 한줄 if문 Ternary operator 21 | 22 | 조건문 ? 참일 때 값: 거짓일 때 값 23 | 24 | ```swift 25 | let isFemale = false 26 | 27 | let name = isFemale ? "Lady" : "Tramp" // "Tramp" 28 | ``` 29 | 30 | ## Nil-coalescing operator 31 | 32 | a ?? b (Ternary operator로 표현한다면 a != nil ? a! : b) 33 | 34 | ```swift 35 | var num: String = self.anotherNum ?? 90 36 | ``` 37 | 38 | ## Switch - case 분기문 39 | 40 | switch 판별대상 변수 { case 조건1: 코드 ... } 41 | 42 | ```swift 43 | let someChar = "z" 44 | 45 | switch someChar { 46 | case "a": 47 | print("a") 48 | case "b": 49 | print("b") 50 | default: 51 | print("no idea") 52 | } 53 | // no idea 54 | ``` 55 | 56 | ## case - where 57 | 58 | where은 case에 추가 조건을 주고자 할 때 쓰입니다. 59 | 60 | ```swift 61 | let myInfo = ("John", 33) 62 | 63 | switch myInfo { 64 | case ("Mike", 20), ("John", 20): // or을 콤마로 표현 65 | "first case" 66 | case (John, let age) where age == 33: 67 | "second case" 68 | default: 69 | break 70 | } 71 | // second case 72 | ``` 73 | 74 | ## guard let-else 75 | 76 | 만일 다음 조건이 참이라면 다음 코드줄로 넘어갈 수 있다는 것을 말합니다. (다음 줄로 넘어갈 권한을 주는 특수한 if 문) 77 | 78 | ```swift 79 | guard let name = person["name"] else { return } 80 | ``` 81 | 82 | ## API 확인 83 | 84 | 다른 사람이 만든 코드가 내가 개발하고자 하는 아이폰 버전에 부합하는 지 체킹할 필요가 있을 때 사용합니다. 85 | 86 | ```swift 87 | if #available(iOS 10, macOS 10.12, *) { 88 | // code 89 | } else { 90 | // code 91 | } 92 | ``` 93 | 94 | ## 이 외로 알아두어야할 것 95 | 96 | - break 97 | 98 | - continue 99 | 100 | - fallthrough 101 | 102 | 103 | ### Reference: 104 | 105 | - https://m.blog.naver.com/jdub7138/220924373892 106 | -------------------------------------------------------------------------------- /basic/enum.md: -------------------------------------------------------------------------------- 1 | # Enum (열거형) 2 | 3 | 열거라는 뜻을 가진 Enumeration에서 따온 용어입니다. 한글로 번역할 때에는 열거형이라는 말을 많이 사용합니다. 특정한 이름을 나열하고 이 이름을 값 대신 쓸 수 있도록 하는 기능입니다. 각 케이스는 고유의 raw 값을 가질 수 있는데 이는 비단 정수 뿐만 아니라 문자나 문자열, 정수 및 실수값을 가질 수 있습니다. 4 | 5 | ```swift 6 | enum CompassPoint { 7 | case North 8 | case South 9 | case East 10 | case West 11 | } 12 | 13 | /// 혹은 한줄에 쓸 수 있습니다. 14 | enum CompassPoint { 15 | case north, south, east, west 16 | } 17 | ``` 18 | 19 | 개별 사례의 원값을 지정하려면 enum 타입 자체의 타입을 지정해주어야 합니다. 20 | 21 | ```swift 22 | enum CompassPoint: String { 23 | case north //이 때는 "north"가 됩니다. 24 | case south="South", east="East", west="West" 25 | } 26 | 27 | let p = CompassPoint.north 28 | print(p.rawValue) // "north" 29 | ``` 30 | 31 | Swift가 발표되고 열거형의 항목에 자료형과 원시 값을 설정할 수 있고 메서드나 프로퍼티를 정의할 수 있게 되었습니다. 또한, Codable이 도입되면서 원시 값에서 Constant로 편하게 변환할 수 있게 되면서 열거형을 활용하는 코드가 좀 더 세련되게 정리될 수 있는 환경이 되었다고 할 수 있습니다. 32 | 33 | enum을 통하여 단순히 코드의 가독성만 향상만을 목적으로 하는 것이 아니라 가능한 최대한의 캡슐화를 통해서 열거형이 사용될 수 있는 모든 곳의 코드를 간결하게 만들고 쉽게 수정할 수 있는 것을 목적으로 할 필요가 있습니다. 34 | 35 | ```swift 36 | enum Married: Int, Codable { 37 | case Single = 0 38 | case Married = 1 39 | ``` 40 | -------------------------------------------------------------------------------- /basic/equatable.md: -------------------------------------------------------------------------------- 1 | # Equality and Ordering 2 | 3 | contains(_ : )나 표준 비교 연산자와 같은 간단한 collection operation 등에 사용자가 유형을 정의하여 사용할 수 있습니다. 4 | 5 | ## Equatable 6 | 7 | 값이 동일한지 아닌지를 비교할 수있는 타입입니다. Equatable 프로토콜을 준수하는 타입은 등호 연산자 (==) 또는 같지 않음 연산자 (!=)를 사용하여 동등성을 비교할 수 있습니다. Swift 표준 라이브러리의 대부분 기본 데이터타입은 Equatable를 따릅니다. 8 | 9 | 10 | 간단한 Struct를 만들어보겠습니다. 11 | 12 | ```swift 13 | struct StreetAddress { 14 | let number: String 15 | let street: String 16 | 17 | init(_ number: String, _ street: String) { 18 | self.number = number 19 | self.street = street 20 | } 21 | } 22 | ``` 23 | 24 | 위 StreetAddress 구조체를 사용하여 addresses라는 [StreetAddress] 타입의 Array를 만들겠습니다. 25 | 26 | ```swift 27 | let addresses = [StreetAddress("1490", "Grove Street"), 28 | StreetAddress("2119", "Maple Avenue"), 29 | StreetAddress("1400", "16th Street")] 30 | ``` 31 | 32 | addresses 배열에서 특정 StreetAddress(home)가 포함되어 있는지를 찾기 위해서 보통 다음과 같이 찾을 수 있습니다. 33 | 34 | ```swift 35 | let home = StreetAddress("2119", "Maple Avenue") 36 | let containsHome = addresses.contains { (address) -> Bool in 37 | return address.number == home.number && address.street == home.street 38 | } 39 | ``` 40 | 41 | contains 함수에 closure를 이용하여 구현할 수 있지만 Equatable 프로토콜을 conform하여 좀 더 아름답게 수정할 수 있습니다. 42 | 43 | ```swift 44 | extension StreetAddress: Equatable { 45 | static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool { 46 | return lhs.number == rhs.number && lhs.street == rhs.street 47 | } 48 | } 49 | ``` 50 | 51 | StreetAddress 구조체에 Equatable프로토콜을 conform 한 후 ==함수를 다음과 같이 구현해줍니다 52 | 53 | 이제, 두 객체간의 같다/다르다를 == 또는 != 연산자를 사용하여 표현할 수 있습니다. 54 | 55 | ```swift 56 | let home = StreetAddress("2119", "Maple Avenue") 57 | let yourHome = StreetAddress("2119", "Maple Avenue") 58 | home == yourHome 59 | ``` 60 | 61 | 하지만, Equatable 프로토콜은 단순 비교 뿐만아니라, 위에 테스트 했던 contains 함수를 클로져를 사용하지 않고 구현할 수 있게 해줍니다. 62 | 63 | ```swift 64 | let containsHome = addresses.contains(home) 65 | ``` 66 | 67 | 68 | ## Comparable 69 | 70 | <, <=, >=, > 등과 같은 상대적 연산자를 사용하여 비교하는 타입입니다. 71 | 72 | 위의 예시를 가지고 다시 설명해보고자 합니다. 만약, addresses 배열을 정렬을 해야한다면 보통의 경우 다음과 같이 구현할 수 있습니다. 73 | 74 | ```swift 75 | addresses.sorted { $0.number < $1.number || $0.street < $1.street } 76 | ``` 77 | 78 | 위 코드는 1줄로 짧긴 하지만 여전히 closure를 사용하고 있고, 여러 곳에서 정렬이 이루어진다면 비슷하게 생긴 클로져가 중복해서 쓰여지지만 Comparable 프로토콜을 사용하여 아름답게 바꿀 수 있습니다. 79 | 80 | Comparable 프로토콜은 Equtable 프로토콜을 상속받기 때문에 기존에 Equatable을 상속받은 extension은 제거할 수 있습니다. 81 | 82 | ```swift 83 | extension StreetAddress: Comparable { 84 | static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool { 85 | return lhs.number == rhs.number && lhs.street == rhs.street 86 | } 87 | 88 | static func < (lhs: StreetAddress, rhs: StreetAddress) -> Bool { 89 | return lhs.number < rhs.number || lhs.street < rhs.street 90 | } 91 | } 92 | ``` 93 | 94 | Comparable 프로토콜을 conform 한 뒤 ==, <함수를 구현해 줍니다. 95 | 이제, 두 객체간의 비교연산 (<, <=, >, >=)이 가능할 뿐만 아니라, 위의 문제였던 sorted 함수를 클로져를 사용하지 않고 구현할 수 있게 만들어줍니다. 96 | 97 | ```swift 98 | addresses.sorted() 99 | ``` 100 | 101 | ### Reference: 102 | 103 | - https://developer.apple.com/documentation/swift/swift_standard_library/basic_behaviors 104 | -------------------------------------------------------------------------------- /basic/file.md: -------------------------------------------------------------------------------- 1 | # File System 2 | 3 | 파일 시스템은 운영 체제 자체에 연결된 데이터 파일, 앱 및 파일의 영구 저장소를 처리합니다. 따라서 파일 시스템은 모든 프로세스에서 사용하는 기본 리소스 중 하나입니다. 4 | 5 | 기본 형식에 관계없이 장치에 연결된 모든 디스크는 물리적으로 연결되어 있든 네트워크를 통해 간접적으로 연결되어 있든 상관없이 하나의 파일 모음을 만들 수 있는 공간을 제공합니다. 파일 수는 수백만 개일 수 있으므로 파일 시스템은 디렉터리를 사용하여 계층 구조를 생성합니다. 기본 디렉토리 구조는 iOS 및 MacOS와 유사하지만, 각 시스템이 앱과 사용자 데이터를 구성하는 방식에는 차이가 있습니다. 6 | 7 | 정확한 파일 위치는 플랫폼에 따라 다르지만 가장 중요한 목표는 사용자의 파일을 쉽게 검색할 수 있도록 유지하고 코드 내부에서 사용하는 파일이 사용자의 작업에 방해가 되지 않도록 하는 것입니다. 8 | 9 | ## iOS Standard Directories: Where Files Reside 10 | 11 | 보안을 위해 iOS 앱과 파일 시스템의 상호 작용은 앱의 샌드박스 디렉터리 내의 디렉토리로 제한됩니다. 새 앱을 설치하는 동안 설치 관리자는 샌드박스 디렉토리 내에 앱에 대한 여러 컨테이너 디렉토리를 생성합니다. 각 컨테이너 디렉토리에는 특정 역할이 있습니다. 12 | 13 | 번들 컨테이너 디렉토리는 앱의 번들을 보관합니다. **bundle은 앱과 연관된 리소스들을 모아놓은 디렉토리** 입니다. Bundle에는 앱 실행 파일과 supporting resources(앱 아이콘, 이미지 파일, info.plist 등등)이 들어있습니다. 14 | 15 | **데이터 컨테이너 디렉토리는 앱과 사용자 모두를 위한 데이터를 보관** 합니다. 데이터 컨테이너 디렉토리는 앱에서 데이터를 정렬하고 구성하는 데 사용할 수 있는 여러 하위 디렉토리로 추가로 구분됩니다. 16 | 17 | 또한 런타임에 iCloud 컨테이너와 같은 추가 컨테이너 디렉토리에 대한 액세스를 요청할 수도 있습니다. 18 | 19 | 디렉토리 구조를 그림으로 나타내면 다음과 같습니다. 20 | 21 | 22 | 23 | 일반적으로 앱은 컨테이너 디렉토리 외부에 있는 파일에 액세스하거나 만들 수 없습니다. 이 규칙의 한 가지 예외는 앱이 공용 시스템 인터페이스를 사용하여 사용자의 연락처나 음악 등에 액세스하는 경우입니다. 이 경우 시스템 프레임워크는 도우미 앱을 사용하여 적절한 데이터 저장소에서 읽거나 수정하는 데 필요한 파일 관련 작업을 처리합니다. 24 | 25 | 일반적으로 사용되는 iOS 어플리케이션의 디렉토리를 표로 나타내면 다음과 같습니다. 26 | 27 | |Directory|Description| 28 | |--|--| 29 | |AppName.app (Bundle)| 앱의 번들입니다. 이 디렉터리에는 앱과 해당 모든 리소스가 포함되어 있습니다. 이 디렉토리에 쓰기 엑세스 권한을 얻을 수 없습니다. 무단 변경을 방지하기 위해 설치 시 번들 디렉토리에 서명됩니다. 그러나 애플리케이션 번들에 저장된 리소스에 대한 읽기 전용 액세스 권한은 얻을 수 있습니다.| 30 | |Documents/|사용자가 생성한 콘텐츠를 저장하려면 이 디렉토리를 사용합니다. 이 디렉토리의 내용은 파일 공유를 통해 사용자가 사용할 수 있도록 만들 수 있으므로, 디렉토리에는 사용자에게 노출하고자 하는 파일만 포함되어야 합니다.| 31 | |Library/|사용자 데이터 파일이 아닌 파일의 최상위 디렉터리입니다. 사용자에게 노출되지 않을 파일에 대해 라이브러리 하위 디렉토리를 사용합니다. 앱에서 사용자 데이터 파일에 이러한 디렉토리를 사용하면 안 됩니다. (Document나 tmp 사용)| 32 | |tmp/|이 디렉토리를 사용하여 앱의 시작 사이에 유지할 필요가 없는 임시 파일을 작성합니다. 더 이상 필요하지 않은 파일은 앱에서 제거해야 합니다. 앱이 실행되고 있지 않으면 시스템에서 이 디렉토리를 제거할 것 입니다.| 33 | 34 | 35 | iOS 앱은 문서, 라이브러리 및 tmp 디렉토리에 추가 디렉토리를 생성할 수 있습니다. 이렇게 하면 해당 위치에 있는 파일을 더 잘 구성할 수 있습니다. 36 | 37 | ### Reference: 38 | 39 | - https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html 40 | -------------------------------------------------------------------------------- /basic/function.md: -------------------------------------------------------------------------------- 1 | # Function Type 2 | 3 | Function type은 function, method, closure를 나타내는 타입이며 특정 기능을 하는 코드를 특정 방법으로 묶어낸 것입니다. 이러한 Function type은 반복적인 코딩을 피하기 위하여 사용하곤 합니다. Function type은 화살표( ->)로 구분되어지며 **파라미터 타입과 리턴 타입**으로 구성되어 있습니다. 4 | 5 | - (parameter type) -> return type 6 | 7 | #### parameter type: 8 | 9 | 입력값으로서 function type은 이러한 값들을 받아 특정 계산을 수행합니다. parameter type은 comma로 구분되어지는 타입 리스트입니다. 10 | 11 | #### return type: 12 | 13 | 도출되는 특정 계산의 결과값입니다. return type은 튜플 타입을 지원하기 때문에 multiple values를 지원합니다. 14 | 15 | Swift내에서 이러한 함수 타입은 [일급 객체](../features/first_class_functions.md)로서 다음의 사항들을 만족합니다. 16 | 17 | 1. 변수나 상수에 할당 가능 18 | 19 | 2. 인자값 (parameter)로 사용 가능 20 | 21 | 3. 반환타입 (return)으로 사용 가능 22 | 23 | 24 | ### Reference: 25 | 26 | - http://wlaxhrl.tistory.com/39 27 | 28 | - https://docs.swift.org/swift-book/ReferenceManual/Types.html#//apple_ref/swift/grammar/type 29 | 30 | - http://seorenn.blogspot.com/2014/06/swift-function.html 31 | -------------------------------------------------------------------------------- /basic/generics.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | 제네릭이란, 일반 프로그래밍 원론에서 다루는 개념을 살펴보면 데이터 형식에 의존하지 않고 **하나의 값이 여러 다른 데이터타입을 가질 수 있는 기술**에 중점을 두어 재사용성을 높일 수 있도록 하는 프로그래밍 개념입니다. 제너릭의 개념이 없다면 아래와 같이 데이터 형식마다 서로 다른 함수를 짜주어야 하는 불편함이 있을 것 입니다. 4 | 5 | ```swift 6 | func swapTwoInts(_ a: inout Int, _ b: inout Int) { 7 | let temp = a 8 | a=b 9 | b = temp 10 | } 11 | 12 | func swapTwoStrings(_ a: inout String, _ b: inout String) { 13 | let temp = a 14 | a=b 15 | b = temp 16 | } 17 | 18 | func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { 19 | let temp = a 20 | a=b 21 | b = temp 22 | } 23 | ``` 24 | 25 | 그러나 제너릭을 통해 아래와 같이 간단하게 바꿀 수 있습니다. 26 | 27 | ```swift 28 | func swapTwoValues(_ a: inout T, _ b: inout T) { 29 | let temp = a 30 | a=b 31 | b = temp 32 | } 33 | ``` 34 | 35 | 36 | 37 | 객체 지향 프로그래밍에서는 제네릭을 위의 기본 개념을 바탕으로 하여 클래스 내부에서 사용될 데이터 타입을 클래스 내부에서 지정하지 않고 클래스 외부에서 지정하는 기법을 이야기합니다. 다시 말해서, 클래스 코드 블록을 정의할 때 데이터 타입을 지정하는 것이 아니라 클래스의 **인스턴스를 생성할 때 데이터 타입을 지정**해준다는 것입니다. 38 | 39 | 제네릭을 사용하고자 하는 함수나 타입의 이름 뒤에 <> 를 넣고 그 사이에 제네릭 타입을 작성하면 됩니다. 스위프트의 클래스, 열거형 그리고 구조체 뿐만 아니라 함수나 메서드에까지 제네릭 형식을 만들수 있습니다. 40 | 41 | 제너릭을 이용한 스택 struct 예시입니다. 42 | 43 | ```swift 44 | struct Stack { 45 | var items = [T]() 46 | mutating func push(item: T) { 47 | items.append(item) 48 | } 49 | mutating func pop() -> T { 50 | return items.removeLast() 51 | } 52 | } 53 | ``` 54 | 55 | 프로토콜을 구현하기 위한 타입을 요구하거나 두 타입이 동일하기를 요구하거나 혹은 특정 수퍼 클래스를 가지는 클래스를 요구하는 등의 요청 목록을 지정하기 위해 타입 이름 뒤에 where 키워드를 사용할 수 있습니다. 56 | 57 | ```swift 58 | func allItemsMatch 60 | (someContainer: C1, anotherContainer: C2) -> Bool { 61 | 62 | // check that both containers contain the same number of items 63 | if someContainer.count != anotherContainer.count { 64 | return false 65 | } 66 | 67 | // check each pair of items to see if they are equivalent 68 | for i in 0..() 20 | var dict: [GridPoint:Int] = [:] 21 | ``` 22 | 23 | ### Reference: 24 | 25 | - https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98 26 | -------------------------------------------------------------------------------- /basic/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Application Life Cycle 2 | 3 | iOS 앱이 실행되서 실행되는 동안의 과정을 Application Life Cycle 이라고 부릅니다. 일반적으로 C언어 기반의 프로그래밍 언어에서는 main이라는 함수가 앱의 시작이 됩니다. iOS의 앱 또한 ObjectiveC 기반에서(C언어 기반) 돌아가기 때문에 앱은 main 함수에서 시작합니다. 다만, iOS의 핵심 라이브러리인 UIKit framework가 main 함수를 관리하여 앱 개발자들이 직접 main 함수에 코드를 작성하지 않습니다. 4 | 5 | 그렇다고 앱의 실행에 앱 개발자가 관여할 수 없는 것은 아닙니다. UIKit은 main 함수를 다루는 과정에서 UIApplicationMain 함수를 실행합니다. 이 함수를 통해 UIApplication 객체가 생성되는데 이 객체를 통해 앱 개발자는 앱의 실행에 부분적으로 관여할 수 있습니다. 이처럼, 앱 개발자가 앱을 실행할 때 접근할 수 객체가 UIApplication 이기 때문에 앱이 어떤 과정으로 실행되는지 자세히 알아보려면 UIApplication에 대해 좀 더 알 필요가 있습니다. 6 | 7 | > iOS도 C언어 기반의 언어로 만들어졌기 때문에, main 함수가 존재합니다. 이 main 함수는 앱의 시작점이 됩니다. 다만, main 함수는 숨겨져 있기 때문에 프로젝트를 생성해도 파일이 나타나지는 않습니다. 8 | 9 | ## UIApplication 10 | 11 | 모든 iOS 앱들은 UIApplicationMain 함수를 실행합니다. 이 때 생성되는 것 중 하나가 UIApplication 객체입니다. UIApplication 객체는 singleton 형태로 생성되어, UIApplication.shared의 형태로 앱 전역에서 사용할 수 있습니다. UIApplication 객체의 가장 중요한 역할은 user의 이벤트(터치, 리모트 컨트롤, 가속도계, 자이로스코프 등)에 반응하여 앱의 초기 routing(초기 설정)을 하는 것입니다. 구체적인 예를 들자면, UIApplication 객체는 앱이 Background에 진입한 상태에서 추가적인 작업을 할 수 있도록 만들어주거나, 푸쉬 알람을 받았을 때 어떤 방식으로 이를 처리할지 등에 대한 것을 다룹니다. 12 | 13 | ## Main Run Loop 14 | 15 | Main Run Loop라는 것은 유저가 일으키는 이벤트들을 처리하는 프로세스입니다. UIApplication 객체는 앱이 실행될 때, Main Run Loop를 실행하고, 이 Main Run Loop를 View와 관련된 이벤트나 View의 업데이트에 활용합니다. 또한, Main Run Loop는 View와 관련되어 있기 때문에 Main 쓰레드에서 실행됩니다. 16 | 17 | 18 | 19 | 유저가 일으키는 이벤트의 처리 과정을 다음과 같은 순서로 정리할 수 있습니다. 20 | 21 | - 1. 유저가 이벤트를 일으킨다. 22 | - 2. 시스템을 통해 이벤트가 생성된다. 23 | - 3. UIkit 프레임워크를 통해 생성된 port로 해당 이벤트가 앱으로 전달된다. 24 | - 4. 이벤트는 앱 내부적으로 Queue의 형태로 정리되고, Main Run Loop에 하나씩 매핑된다. 25 | - 5. UIApplication 객체는 이때 가장 먼저 이벤트를 받는 객체로 어떤 것이 실행되야하는지 결정한다. 26 | 27 | 28 | 29 | ### Reference: 30 | - https://hcn1519.github.io/articles/2017-09/ios_app_lifeCycle 31 | -------------------------------------------------------------------------------- /basic/loop.md: -------------------------------------------------------------------------------- 1 | # 반복 Loop 2 | 3 | 특정 코드가 반복적으로 수행될 수 있도록 합니다. 4 | 5 | ## for in 6 | 7 | ``` swift 8 | for i in 1..<4 { 9 | print(i) 10 | } 11 | // 1 2 3 12 | ``` 13 | 14 | ## while 15 | 16 | 조건문이 참일 때 코드를 반복 17 | 18 | ``` swift 19 | var number = 0 20 | 21 | while number < 10 { 22 | print(number) 23 | number += 1 24 | } 25 | ``` 26 | 27 | ## repeat - while 28 | 29 | 조건과 관계없이 한번은 돌린 다음 조건문이 참이면 코드를 반복 30 | 31 | ``` swift 32 | var number = 0 33 | 34 | repeat { 35 | print(number) 36 | number += 1 37 | } while number < 10 38 | ``` 39 | 40 | ## for each 41 | 42 | 자세한 것은 [고차함수](../features/higher_order_functions.md) 문서 참조 43 | 44 | for-in loop 같은 역할을 해줍니다. 아래는 array 안에 있는 1, 2, 3을 차례로 출력해줍니다. 45 | 46 | ```swift 47 | [1, 2, 3].forEach { (each: Int) in 48 | print(each) 49 | } 50 | -------------------------------------------------------------------------------- /basic/method.md: -------------------------------------------------------------------------------- 1 | # Method 2 | 3 | Function과 Method는 모두 특정 작업을 수행하기 위한 독립적인 코드들의 집합이라는 점에서 동일하지만, 약간의 차이가 존재합니다. Method는 클래스, 구조체, 열거형 등의 객체에 종속적이지만, Function은 그렇지 않고 객체에 독립적입니다. 아래는 Function과 Method의 예시입니다. 4 | 5 | ```swift 6 | func someFunction { 7 | //some code 8 | } 9 | 10 | class someClass { 11 | func someMethod { 12 | //some code 13 | } 14 | } 15 | ``` 16 | 17 | Method는 항상 data type에 종속되는 만큼, Function에게는 없는 self라는 개념이 존재합니다. self는 Method가 호출된 객체를 지칭합니다. 18 | -------------------------------------------------------------------------------- /basic/properties.md: -------------------------------------------------------------------------------- 1 | # Properties 2 | 3 | 클래스, 구조체, 열거형 안에 선언되어서 사용하는 '소속된 변수' 입니다. 4 | 5 | ## 1.Instance Property 6 | 7 | ### 1.1 Stored instance property 8 | 9 | Stored instance Property(저장 프로퍼티)는 상수(constant)와 변수(variable)값을 인스턴스의 일부로 메모리에 저장합니다. 클래스와 구조체에서만 사용됩니다 10 | 11 | ```swift 12 | class FixedLengthRange { 13 | var firstValue: Int 14 | let length: Int 15 | 16 | init(firstValue : Int, length:Int) { 17 | self.firstValue = firstValue 18 | self.length = length 19 | } 20 | } 21 | ``` 22 | 23 | 위의 예시에서 firstValue와 length를 Stored property라고 말할 수 있습니다. 24 | 25 | #### Lazy Stored Properties 26 | 27 | 값이 사용되기 전까지는 값이 계산되지 않는 프로퍼티입니다. 초기값이 인스턴스의 초기화가 될때까지 값을 모르는 외부요소에 의존하는 경우나 초기값이 복잡하거나 계산비용이 많이 드는 설정을 필요로 할때 유용합니다. 28 | 29 | 이러한 lazy 프로퍼티는 항상 변수로서 선언해야 합니다. 또한 lazy로 선언했다고 해도 lazy 프로퍼티가 초기화 되지 않은 상태에서 여러 쓰레드가 동시에 lazy 프로퍼티에 액세스 한다면, 이 프로퍼티가 단 한번만 초기화 된다는 것을 보장할 수 없습니다. 30 | 31 | 32 | ### 1.2 Computed instance property 33 | 34 | (인스턴스의 상태를 바탕으로) 값을 연산하여 반환합니다. 클래스, 구조체 그리고 열거형에서 사용됩니다. 클래스, 구조체, 열거형은 저장 프로퍼티 이외에도 값을 저장하지 않는 연산 프로퍼티를 정의할 수 있는데, getter와 setter를 통해 다른 프로퍼티와 간접적으로 값을 검색하고 세팅합니다. get, set의 기본 syntax는 아래와 같습니다. 35 | 36 | ```swift 37 | var myProperty: Int { 38 | get { 39 | return myProperty 40 | } 41 | set(newVal) { 42 | myProperty = newVal 43 | } 44 | } 45 | myProperty = 123 // warning 46 | ``` 47 | 48 | get과 set은 해당 프로퍼티에 직접 붙어있기 때문에 위와 같이 get{}, set{}에서 myProperty에 접근하면 recursive하게 자기 자신의 get, set을 호출하게 되므로 이렇게 사용하면 안됩니다. 일반적으로 get, set은 아래처럼 실제 값을 저장 할 backing storage가 필요합니다. 49 | 50 | ```swift 51 | var _myProperty: Int 52 | var myProperty: Int { 53 | get { 54 | return _myProperty 55 | } 56 | set(newVal) { 57 | _myProperty = newVal 58 | } 59 | } 60 | ``` 61 | 62 | _myProperty는 실제로 값이 저장되는 변수입니다. 외부에서 myProperty의 값에 접근하거나 새로운 값을 할당하면 실제로 값이 저장되는 곳은 myProperty가 아닌 _myProperty입니다. 즉 myProperty는 _myProperty의 interface역할을 합니다. 63 | 64 | 이러한 get, set을 사용하는 경우는 다음과 같습니다. 65 | 66 | 1. 프로퍼티에 값이 할당 될 때 적절한 값인지 검증하기 위해 67 | ex) 음수 검증 68 | 69 | 70 | 2. 다른 프로퍼티값에 의존적인 프로퍼티를 관리 할 때 71 | 72 | 3. 프로퍼티를 private하게 사용하기 위해 73 | 74 | ## 2. Type property 75 | 76 | 인스턴스 property는 특정 타입의 인스턴스에 속한 property입니다. 해당 타입의 인스턴스가 새로 생성될 때마다, 다른 모든 인스턴스로부터 분리된 자신만의 property값으로 설정됩니다. type property는 property를 타입 자체와 연결합니다. 얼마나 많은 타입 인스턴스를 만드는 것에는 상관없이 이 type property의 복사본을 갖게 됩니다. 77 | 78 | 특정 유형의 새 인스턴스를 생성할 필요 없이 액세스할 수 있으며, type property는 해당 타입의 어떠한 인스턴스에서도 사용가능합니다. 79 | 80 | stored instance property와는 다르게 stored type property는 기본 값이 항상 주어집니다. 이는 타입 스스로가 초기화를 가질 수 없기 때문이며 초기화 하는 시간에 stored type property에서 값을 할당합니다. 81 | 82 | 사용할 키워드 측면에서 값 타입(구조 및 열거)과 참조 타입(클래스) 사이에는 작지만 유효한 차이가 있습니다. 값 타입에서 type property의 사용은 비교적 간단합니다. 83 | 84 | ```swift 85 | struct SomeStructure { 86 | static var storedTypeProperty = "Some value." 87 | static var computedTypeProperty: Int { 88 | // return an Int value here 89 | } 90 | } 91 | 92 | enum SomeEnumeration { 93 | static var storedTypeProperty = "Some value." 94 | static var computedTypeProperty: Int { 95 | // return an Int value here 96 | } 97 | } 98 | ``` 99 | 100 | 참조 타입이라는 점에서 클래스의 type properties를 구현하면, 유형 속성이 하위 클래스에서 재정의되지 않을 경우 static 키워드를 사용하고, 해당 하위 클래스에서 type properties를 재정의할 경우 class 키워드를 사용해야 합니다. 101 | 102 | 더욱이 class 키워드를 사용한다는 것은 subclass에서 바뀔 가능성을 내포함으로 stored property로서 사용할 수 없습니다. (only computed) 103 | 104 | 또 다른 중요한 점으로는, 클래스 내의 static property는 subclass에서 overridden할 수 없지만 subClass에서 사용 가능합니다. 105 | 106 | 이는 아래의 예시에서 확인 가능합니다. 107 | 108 | ```swift 109 | class Triangle { 110 | static var sides = 3 111 | 112 | class var sideToBeOverridden: Int { 113 | return 3 114 | } 115 | } 116 | 117 | var TriangleSide = Triangle.side // 3 118 | var TriangleSide2 = Triangle.sideToBeOverridden // 3 119 | 120 | Class Octagon: Triangle { 121 | override class var sideToBeOverridden: Int { 122 | return 8 123 | } 124 | } 125 | 126 | var OctagonSide = Octagon.side // 3 127 | var OctagonSide2 = Octagon.sideToBeOverridden // 8 128 | ``` 129 | 130 | 131 | ## Property observer 132 | 133 | Property Observer는 lazy 저장 프로퍼티를 제외하고, stored property에 달 수 있는 것으로, 값의 변화를 주시하여 값이 변하기 직전(willSet)과 직후(didSet)에 어떤 행동을 할 수 있게 해주는 것입니다. 134 | 135 | 또한, 하위 클래스 내의 프로퍼티를 재정의하여, 상속된 프로퍼티(저장프로퍼티 or 연산프로퍼티 어느것이든)에도 프로퍼티 옵저버를 추가할 수 있습니다. 136 | 137 | ● willSet - 값이 저장되기 직전에 호출됩니다. 138 | 139 | ● didSet - 새로운 값이 저장된 직후에 호출됩니다. 140 | 141 | ```swift 142 | class StepCounter { 143 | var totalSteps: Int = 0 { 144 | willSet(newTotalSteps) { 145 | print("About to set totalSteps to \(newTotalSteps)") 146 | } 147 | didSet { 148 | if totalSteps > oldValue { 149 | print("Added \(totalSteps - oldValue) steps") 150 | } 151 | } 152 | } 153 | } 154 | let stepCounter = StepCounter() 155 | stepCounter.totalSteps = 200 156 | // About to set totalSteps to 200 157 | // Added 200 steps 158 | stepCounter.totalSteps = 360 159 | // About to set totalSteps to 360 160 | // Added 160 steps 161 | ``` 162 | 163 | ### Reference: 164 | 165 | - https://medium.com/@micosmin/swift-type-properties-5a51c50c00b7 166 | 167 | - https://hcn1519.github.io/articles/2017-03/swift_property 168 | 169 | - https://zeddios.tistory.com/243?category=685736 170 | 171 | - https://medium.com/ios-development-with-swift/%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-get-set-didset-willset-in-ios-a8f2d4da5514 172 | -------------------------------------------------------------------------------- /basic/protocol.md: -------------------------------------------------------------------------------- 1 | # Protocol 2 | 3 | 프로토콜은 특정 기능 수행에 필수적인 요수를 청의한 청사진(blueprint)입니다. 프로토콜은 인터페이스로 최소한으로 가져야 할 속성이나 메서드를 정의합니다. 구현은 하지 않고, 정의만 합니다. 4 | 5 | 6 | 프로토콜을 만족시키는 타입을 프로토콜을 따른다(conform)고 말합니다. 프로토콜에 필수 구현을 추가하거나 추가적인 기능을 더하기 위해 프로토콜을 확장(extend)하는 것이 가능합니다. 프로토콜은 다중으로 정의하는게 가능합니다. 한 클래스가 여러 프로토콜을 요구받게 만들 수 있습니다. 콤마를 이용해 구분하면 됩니다. 7 | 8 | 프로토콜 정의와 사용은 다음과 같이 쓸 수 있습니다. 9 | 10 | ```swift 11 | protocol SomeProtocol { 12 | func someFunction() -> Int 13 | var someProperty: String { get set } 14 | optional func anotherFunction() 15 | } 16 | class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { 17 | // class definition goes here 18 | } 19 | ``` 20 | 21 | 객체간의 (비동기) 소통을 위한 delegation 등을 위해 프로토콜이 사용될 수 있습니다. 22 | 23 | 기본적으로 Swift Standard Library 이미 구현되어있는 기본 프로토콜 들이 존재합니다. 24 | 25 | - [Encoding, Decoding, and Serialization](./codable.md) 26 | - [Equality and Ordering](./equatable.md) 27 | - [Hash for Sets and Dictionaries](./hashable.md) 28 | - [Sequence and Collection](./sequence.md) 29 | 30 | 31 | ## Protocol Default Implementation 32 | 33 | - Protocol + Extension = Protocol Extension 34 | 35 | 이러한 프로토콜은 메소드와 속성과 같이 확장(extension)이 가능합니다. 이미 있는 여러 클래스에 공통된 기능을 구현하여 함께 처리할 수도 있습니다. 이를 Protocol Default Implementation 가능하다고 합니다. (Protocol + Extension = Protocol Extension) 36 | 37 | 프로토콜의 기능을 익스텐션하여 미리 구현하는 것을 프로토콜 초기 구현이라고 합니다. 이를 통해 특정 타입이 할 일 지정 + 구현을 한번에 할 수 있습니다. 38 | 39 | ```swift 40 | // 코드 중복이 발생 하는 상황 41 | 42 | struct Bird { 43 | func move() { 44 | print("이동하다") 45 | } 46 | func fly() { 47 | print("날다") 48 | } 49 | } 50 | 51 | struct Airplane { 52 | func move() { 53 | print("이동하다") 54 | } 55 | func fly() { 56 | print("날다") 57 | } 58 | } 59 | 60 | // Protocol Extension으로 코드 중복 제거 61 | 62 | protocol Movable {} 63 | extension Movable { 64 | func move() { 65 | print("이동하다") 66 | } 67 | } 68 | 69 | protocol Flyable {} 70 | extension Flyable { 71 | func fly() { 72 | print("날다") 73 | } 74 | } 75 | 76 | struct Bird : Movable, Flyable { } 77 | struct Airplane: Movable, Flyable { } 78 | ``` 79 | 80 | ## Protocol Composition 81 | 82 | Protocol Composition을 통해 multiple protocols를 결합하여 single requirement에 충족하도록 할 수 있습니다. SomeProtocol & AnotherProtocol의 형식을 통해 새로운 프로토콜을 만들지 않아도 temporary local protocol를 만들어 이용할 수 있습니다. 83 | 84 | ```swift 85 | protocol Food { 86 | var calorie: String { get } 87 | } 88 | 89 | protocol Product { 90 | var price: Int { get } 91 | } 92 | 93 | struct Drink: Food, Product { 94 | var calorie: String 95 | var price: Int 96 | } 97 | 98 | func getInfo(of thing: Food & Product) { 99 | print("The calorie is \(thing.calorie), and the price is \(thing.price)!") 100 | } 101 | ``` 102 | 103 | 104 | ## where Self 105 | 106 | 보통 Extension 뒤에 'where Self: 자료형'을 통해 추가적인 조건을 지정할 수 있습니다. Protocol Extension 역시 'A를 따른다고 한 것들 중 특별히 B까지 따르는 것들은 추가로 이것들을 더 주겠다.'라는 의미를 추가할 수 있습니다. 107 | 108 | ```swift 109 | protocol CanShootThrees {} 110 | protocol CanBlock {} 111 | protocol CanRebound {} 112 | 113 | extension CanBlock where Self: CanShootThrees { 114 | func showOff() { 115 | print("I can play both offense and defense") 116 | } 117 | } 118 | 119 | struct PureShooter: CanShootThrees {} 120 | struct DefensiveForword: CanBlock, CanRebound {} 121 | struct SuperStar: CanShootThrees, CanBlock, CanRebound {} 122 | 123 | let iverson = PureShooter() 124 | let bowen = DefensiveForword() 125 | let jordan = SuperStar() 126 | 127 | iverson.showOff() // 에러 128 | bowen.showOff() // 에러 129 | jordan.showOff() // "I can play both offense and defense" 130 | ``` 131 | 132 | ## Protocol과 Interface (Java) 차이 133 | 134 | Interface: 135 | 136 | - 파라미터 초기값 설정 가능 137 | 138 | - 선언된 메소드 모두 구현해야 함 139 | 140 | - 정적 멤버 선언 불가 141 | 142 | Protocol: 143 | 144 | - 파라미터 초기값 설정 불가 (Protocol Extension으로 가능) 145 | 146 | - Optional 통해서 선택적 구현 147 | 148 | - 정적 멤버 선언 가능 149 | 150 | - 값 타입에도 적용될 수 있기 때문에 mutating keyword를 명시적으로 씀 151 | 152 | 153 | ### Reference 154 | 155 | - https://jusung.gitbook.io/the-swift-language-guide/untitled-17 156 | -------------------------------------------------------------------------------- /basic/sequence.md: -------------------------------------------------------------------------------- 1 | # Sequence and Collection 2 | 3 | 4 | 5 | 6 | ## Sequence 7 | 8 | Sequence 프로토콜은 Array, Dictionary, Set과 같은 Collection 타입의 기반이 되는 프로토콜로, 값을 iterate(반복) 할 수 있는 동일한 타입의 일련의 값이다. 9 | Sequence를 순회하는 가장 일반적인 방법은 for loop를 통해서 사용 할 수 있습니다. 10 | 11 | ```swift 12 | for element in someSequence { 13 | doSomething(with: element) 14 | } 15 | ``` 16 | 17 | Sequence 프로토콜을 구현하게되면 for loop를 통한 순회 뿐만아니라 18 | forEach, map, filter, flatMap, prefix 등등 다양하고 유용한 함수를 사용 할 수 있습니다. 19 | 20 | 즉, 일련의 값에 대한 순차적 접근에 의존하는 공통 작업을 해야한다면 Sequence 위에 구현하는 것을 고려해야 합니다. 21 | 22 | 특정 타입이 Sequence 프로토콜을 conform 하는 것은 간단합니다. 23 | makeIterator() 함수만 구현해주면 됩니다. 24 | 25 | Sequence 프로토콜은 다음과 같이 선언되어 있습니다. 26 | 27 | ```swift 28 | protocol Sequence { 29 | associatedtype Iterator: IteratorProtocol 30 | func makeIterator() -> Itertator 31 | } 32 | ``` 33 | 34 | Sequence 프로토콜은 두 개의 요소로 구성됩니다. 우선, Iterator라는 associated type이 있습니다. 이 associated type은 IteratorProtocol을 준수(conform)하고 있습니다. 다른 하나는 Iterator를 만드는 makeIterator라는 함수이며, 앞서 선언한 Iterator 타입을 반환합니다. 35 | 36 | 37 | ## IteratorProtocol 38 | 39 | IteratorProtocol은 Sequence와 매우 유사합니다. Element라는 associated type을 가지고 있으며, 이것은 요소를 추가하거나 이터레이트를 수행할 때 사용하는 타입을 나타냅니다. 그리고 next라는 함수가 있는데 이것은 다음(next) 요소를 반환합니다. 40 | 41 | next() 함수는 호출시 다음 Element를 반환하고, Sequence가 고갈되면 nil를 반환합니다. 42 | 43 | IteratorProtocol 프로토콜은 다음과 같이 선언되어 있습니다. 44 | 45 | ```swift 46 | protcol IteratorProtocol { 47 | associatedtype Element 48 | mutating func next() -> Element? 49 | } 50 | ``` 51 | 52 | 위 코드에서 associatedtype으로 선언된 Element는 Iterator가 생성하는 값의 유형을 지정합니다. 53 | 54 | ```swift 55 | extension Sequence where Iterator.Element == CustomType { ... } 56 | ``` 57 | 58 | 위와 같은 코드를 많이 사용하게 되는데 Sequence 확장에서 제네릭 타입 제약시 Iterator.Element가 사용되는 이유입니다. 59 | 60 | Iterator를 직접적으로 순회하는 경우는 매우 드뭅니다. 왜냐하면 for loop으로 순회하는 것이 일반적이 방법이기 때문입니다. (for loop도 내부에서는 다음과 같은 로직이 타고 있습니다.) 61 | 62 | ```swift 63 | var iterator = someSequence.makeIterator() 64 | // next 함수에서 nil이 반환되기 전까지 계속 순회함 65 | while let element = iterator.next() { 66 | doSometing(with: element) 67 | } 68 | ``` 69 | 70 | Iterator는 순회시 절대로 reversed 되거나 reset되지 않습니다. next() 함수에서 nil이 반환되지 않는다면 무한 루프에 빠질 수 있습니다. 71 | 72 | 피보나치 수열을 이러한 프로토콜들을 준수하게끔 만들 수 있습니다. 73 | 74 | ```swift 75 | struct FibsIterator: IteratorProtocol { 76 | var state = (0, 1) 77 | var initial = 0 78 | let prefix: Int 79 | 80 | init(prefix: Int) { 81 | self.prefix = prefix 82 | } 83 | 84 | mutating func next() -> Int? { 85 | guard initial < prefix else { 86 | return nil 87 | } 88 | initial += 1 89 | let upcomingNumber = state.0 90 | state = (state.1, state.0 + state.1) 91 | return upcomingNumber 92 | } 93 | } 94 | ``` 95 | 96 | 유한한 스트림을 생성하는 FibIterator 구조체를 만들었으니 다음과 같이 순회 할 수 있습니다. 97 | 98 | ```swift 99 | var fibIterator = FibsIterator(prefix: 5) 100 | while let value = fibIterator.next() { 101 | print(value) 102 | } 103 | // 0 104 | // 1 105 | // 1 106 | // 2 107 | // 3 108 | ``` 109 | 110 | FibsIterator를 만들었기 때문에, FibsSequence도 쉽게 만들 수 있습니다. 111 | 위에서 언급한대로, makeIterator() 함수만 구현해주면 끝납니다. 112 | 113 | ```swift 114 | struct FibsSequence: Sequence { 115 | typealias Iterator = FibsIterator // 타입 추론으로 생략 할 수 있음 116 | let prefix: Int 117 | 118 | func makeIterator() -> FibsIterator { 119 | return FibsIterator(prefix: prefix) 120 | } 121 | } 122 | ``` 123 | 124 | Sequence 프로토콜을 conform 하는 커스텀 타입을 만들었기 때문에 Sequence 프로토콜의 많은 기능을 사용 할 수 있습니다. 125 | 126 | ```swift 127 | for value in fibsSequence { 128 | print(value) // 0, 1, 1, 2, 3 129 | } 130 | fibsSequence.map { $0 + 1 } // [1, 2, 2, 3, 4] 131 | fibsSequence.filter { $0 % 2 == 0 } // [2] 132 | fibsSequence.reduce(0, +) // 7 133 | ``` 134 | 135 | ## Collection 136 | 137 | 모든 Collection은 Sequence를 상속받아 구현되었으며, Collection은 Sequence가 지닌 두 가지 문제점을 해결했습니다. 우선, 모든 Collection은 유한(finite)합니다. 몇 개의 요소로 구성되어 있는지 항상 알 수 있다는 뜻입니다. 절대로 무한(infinite)해질 수 없으며, 원하는 만큼 이터레이트를 할 수 있습니다. Collection 프로토콜은 다음과 같이 작성되어 있습니다. 138 | 139 | ```swift 140 | protocol Collection: Sequence { 141 | associatedtype Index: Comparable 142 | var startIndex: Index 143 | var endIndex: Index 144 | subscript(position: Index) -> Element { get } 145 | func index(after i: Index) -> Index 146 | } 147 | ``` 148 | -------------------------------------------------------------------------------- /basic/standard.md: -------------------------------------------------------------------------------- 1 | # Swift Standard Library 자료형 2 | 3 | 스위프트의 타입에는 6개의 종류가 존재합니다. **4개의 named types (protocol, enum, struct, class) 와 2개의 compound type (tuple, function)** 이 스위프트의 6가지 type입니다. 4 | 5 | 이러한 6가지 타입을 가지고 Swift 표준 라이브러리는 다음과 같은 Swift 프로그램 쓰기를 위한 기본 기능 계층을 정의합니다. 6 | 7 | - Int, Double, and String와 같은 기본 자료형 8 | 9 | - Array, Dictionary, and Set와 같은 공통 자료 구조 10 | 11 | - print와 같은 global function 12 | 13 | - 기본적인 protocols 14 | -------------------------------------------------------------------------------- /basic/struct.md: -------------------------------------------------------------------------------- 1 | # Struct (구조체) 2 | 3 | 구조체와 클래스는 프로그래머가 데이터를 용도에 맞게 묶어 표현하고자 할 때 용이합니다. 구조체와 클래스는 프로퍼티와 메소드를 사용하여 구조화된 데이터와 기능을 가질 수 있습니다. 하나의 새로운 사용자 정의 데이터 타입을 만들어 주는 것 입니다. 아래와 같이 구조체를 정의할 수 있습니다. 4 | 5 | ```swift 6 | struct BasicInformation { 7 | var name: String 8 | var age: Int 9 | } 10 | ``` 11 | 12 | 구조체 정의를 마치고 구조체의 인스턴스를 생성하고 초기화할 수 있습니다. 구조체에 기본 생성된 이니셜라이저의 매개변수는 구조체의 프로퍼티 이름으로 자동 지정됩니다. 구조체를 상수로 선언하면 내부 프로퍼티 값을 변경할 수 없고, 변수로 선언하면 내부 프로퍼티 값을 변경해 줄 수 있습니다. 13 | 14 | ```swift 15 | // 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체를 생성합니다. 16 | var aInfo: BasicInformation = BasicInformation(name: "a", age: 99) 17 | aInfo.age = 100 // 변경 가능! 18 | aInfo.name = "Bear" // 변경 가능! 19 | 20 | // 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체를 생성합니다. 21 | let bInfo: BasicInformation = BasicInformation(name: "b", age: 99) 22 | bInfo.age = 100 // 변경 불가! 23 | ``` 24 | 25 | 26 | ### Reference: 27 | 28 | - http://blog.yagom.net/530 29 | -------------------------------------------------------------------------------- /basic/tuple.md: -------------------------------------------------------------------------------- 1 | # Tuple 2 | 3 | 튜플Tuple은 여러가지 타잆의 값들의 묶음입니다. 배열과 비슷하지만 길이가 고정되어 있다는 점에서 차이가 있습니다. 4 | 5 | ```swift 6 | var coffeeInfo = ("아메리카노", 5100) 7 | 8 | coffeeInfo.0 // 아메리카노 9 | coffeeInfo.1 // 5100 10 | coffeeInfo.1 = 5100 11 | 12 | var namedCoffeeInfo = (coffee: "아메리카노", price: 5100) 13 | 14 | namedCoffeeInfo.coffee // 아메리카노 15 | namedCoffeeInfo.price // 5100 16 | namedCoffeeInfo.price = 5100 17 | ``` 18 | 19 | compound type인 만큼 Named types나 혹은, 다른 compound types를 포함합니다. 20 | 21 | 예를 들어서 (Int, (Int, Int)) 타입의 경우는 Named type인 Int를 첫번째 요소로, Compound type인 (Int, Int)를 두번째 요소로 갖는 compound type 입니다. 22 | 23 | 이러한 Tuple의 특성 중 주의할 점은 [Sequence](./sequence.md) 프로토콜을 따르지 않기 때문에 iterate 하거나 index로서 접근할 수는 없다는 것 입니다. 24 | 25 | ## Tuple의 장점 26 | 27 | ### 1. 간단한 구조체의 대체가 가능하다. 28 | 29 | 이름이 붙은 튜플 아이템은 구조체 등 다량의 데이터를 표시하기 위한 특수 데이터구조를 쉽게 만들 수 있습니다. 30 | 31 | ```swift 32 | let whoAmI = (name: "Seorenn", age: 24, gender: "Etc") 33 | println("I am \(whoAmI.name), \(whoAmI.age) years old, and gender is \(whoAmI.gender)") 34 | ``` 35 | 36 | 그러나 주의할 점은, 간단한 자료형을 만들 때에는 구조체 대신 튜플을 사용하기도 하지만 데이터 구조가 임시범위를 넘어서 존속할 가능성이 있는 경우에는 클래스나 구조체로 모델링해야 한다는 것 입니다. 37 | 38 | ### 2. 멀티 리턴값을 만들 수 있다. 39 | 40 | 함수는 일반적으로 하나의 값 만을 리턴합니다. 하지만 2개 이상의 값을 리턴해야 할 필요가 있을 수도 있습니다. 보통은 함수에서 리턴할 구조체나 클래스를 만들어서 그 인스턴스를 함수에서 만들어서 리턴하는 경우가 다반사입니다. 41 | 42 | 그런데 스위피트는 이 튜플을 활용하면 간단하게 처리가 가능합니다. 43 | 44 | ```swift 45 | func plusAndMinus(a: Int, b: Int) -> (Int, Int) { 46 | return (a + b, a - b) 47 | } 48 | 49 | let (plusResult, minusResult) = plusAndMinus(1, 2) 50 | ``` 51 | 52 | 이 외에 튜플의 활용은 함수의 인자로 전달 할 때 등등 다양한 요소에 활용이 가능합니다. 53 | 54 | ### Reference: 55 | 56 | - http://seorenn.blogspot.com/2014/06/swift-tuple.html 57 | -------------------------------------------------------------------------------- /basic/types.md: -------------------------------------------------------------------------------- 1 | # Swift Type System 2 | 3 | 4 | 5 | 6 | 스위프트의 타입에는 6개의 종류가 존재합니다. 4개의 named types (protocol, enum, struct, class) 와 2개의 compound type (tuple, function)이 스위프트의 6가지 type입니다. Basic type이라고 말해지는 Bool, Int, UInt, Float, Double, Character, String, Array, Set, Dictionary, Optional 등은 모두 named types로부터 만들어져 Swift Standard Library로 전달되는 것 입니다. 7 | 8 | ## Named Types 9 | 10 | **Named types는 정의되었을 때 특정한 이름이 주어지는 타입입니다.** 예로 MyClass라고 이름지어진 User-defined class 의 instance는 MyClass 타입을 갖게 됩니다. 위에서 언급하였다시피, user-defined named types 에 추가적으로 Swift Standard Library 내에서는 Structures를 사용하여 array, dictionaries, optional values 등을 나타내기 위해 named types를 사용합니다. 이러한 것들은 named types이기 때문에 특정 behavior 등을 extend하기 위하여 extension declaration을 사용할 수 있습니다. 11 | 12 | 추가적으로 Named Type의 모든 모델(enum, struct, class)들은 프로토콜을 도입할 수 있습니다. 13 | 14 | - [프로토콜](./protocol.md) 15 | 16 | - [프로토콜 지향 프로그래밍](../features/pop.md) 17 | 18 | 19 | 20 | 21 | 22 | ## Compound Types 23 | 24 | **Compound types는 이름이 없는 Swift 언어 자체에서 정의되는 타입입니다.** function, tuple 2가지의 타입이 존재합니다. Compound type은 Named types나 혹은, 다른 compound types를 포함합니다. 예를 들어서 튜플 (Int, (Int, Int)) 타입의 경우는 Named type인 Int를 첫번째 요소로, Compound type인 (Int, Int)를 두번째 요소로 갖는 compound type 입니다. 25 | 26 | 추가로 named Type과 compound Type을 괄호로 묶을 수 있지만, 어느 "한"타입에 괄호를 묶는것은 아무런 효과가 없습니다. 예를 들어 (Int)와 Int는 동일한 타입입니다. 27 | 28 | 29 | ### Reference: 30 | 31 | - https://www.raywenderlich.com/1314-getting-to-know-enums-structs-and-classes-in-swift 32 | 33 | - https://docs.swift.org/swift-book/ReferenceManual/Types.html#//apple_ref/swift/grammar/type 34 | -------------------------------------------------------------------------------- /basic/value_vs_reference.md: -------------------------------------------------------------------------------- 1 | # Value vs. Reference types 2 | 3 | Reference type과 Value type은 개념적으로 다음과 같이 메모리에 저장된다고 볼 수 있습니다. 4 | (메모리 할당에 대한 자세한 내용은 [여기](../additional_topics/allocation.md)를 참조하시면 됩니다.) 5 | 6 | 7 | 8 | 9 | ## Value type 10 | 11 | Value type은 변수에 값을 할당 하거나, 함수에서 호출할 때, 새로운 인스턴스를 만드는(copy) 데이터 type을 말합니다. Swift Named Types 중 enum과 struct는 Value type입니다. 12 | 13 | 값타입의 가장 큰 특징은 복사 — 대입, 초기화, 파라미터로 전달 –가 되어 개별 이스턴스들이 각각의 독립된 사본으로 존재하게 되는 것입니다. 14 | 15 | ex) primitive data type - Int, Double, Char ... 16 | 17 | 18 | 19 | 구조체와 열거형은 값 타입입니다. 그래서 기본적으로 인스턴스 매소드 내에서 값 타입의 프로퍼티를 변경할 수 없습니다. 때문에 값 타입 형의 매소드에서 프로퍼티를 변경하고 싶은 특정 상황이 존재한다면, 매소드에 mutating을 붙여 주면 가능합니다. 20 | 21 | ## Reference type 22 | 23 | Reference type은 생성자를 통해 초기화 되고 나면, 변수에 값을 할당 하거나, 함수에서 호출할 때, 동일한 인스턴스의 reference(주소)를 반환하는 데이터 type을 말합니다. Swift Named Types 중 class는 Reference type입니다. 24 | 25 | 레퍼런스를 복사하는 것은 암시적으로는 공유된 인스턴스를 갖는 것입니다. 따라서 복사 후에도 두 인스턴스는 동일한 데이터이며, 두 번째 인스턴스를 변경하는 것은 원본에도 영향을 줍니다. 26 | 27 | ex) Object 28 | 29 | 30 | 31 | 32 | ## 차이 33 | 34 | 35 | 36 | > class는 변수 자신이 자신의 속성을 바꾸는 것 이외에도 외부에서 속성을 변경할 수 있습니다. 반면 struct는 자신의 속성은 자신이 바꾸어야 합니다. 그렇기 때문에 value type인 struct가 class보다 좀 더 mutation에 대해 안전하다고 할 수 있습니다. 37 | 38 | 이에 대한 예시입니다. 39 | 40 | 41 | ```swift 42 | class SportsCar { 43 | var brand: String 44 | var model: String 45 | 46 | init(brand: String, model: String) { 47 | self.brand = brand 48 | self.model = model 49 | } 50 | } 51 | 52 | struct Truck { 53 | var weight: Int 54 | var mileage: Double 55 | } 56 | 57 | var sportCar1 = SportsCar(brand: "포르쉐", model: "911") 58 | var sportCar2 = sportCar1 59 | sportCar2.brand = "람보르기니" 60 | 61 | print(sportCar1.brand) // "람보르기니" 출력 - Reference type 특징 62 | 63 | var truck1 = Truck(weight: 2000, mileage: 16) 64 | var truck2 = truck1 65 | truck2.weight = 4000 66 | 67 | print(truck1.weight) // 2000 출력 - Value type 특징 68 | ``` 69 | 70 | 여러 Data type을 Value / Reference type으로 분류하면 다음과 같이 분류할 수 있습니다. 71 | 72 | 73 | 74 | ## 변경과 안정성 75 | 76 | 레퍼런스 타입보다 값 타입을 사용하는 가장 주된 목적은 코드를 이해하기 쉽다는 것입니다. 각 인스턴스가 독립된 데이터 사본을 가지고 있다는 것은, 보이지 않는 곳에서 해당 데이터가 임의로 변경되지 않음을 보장할 수 있다는 것입니다. 특히 이는 멀티스레드 환경에서 다른 스레드가 예기치 못한 시점에 데이터를 변경해놓을 수 있는 것을 방지합니다. (보통 이런 류의 문제는 디버깅이 극히 어렵습니다.) 변경이 없으면 값타입이나 참조타입은 기본적으로 동일합니다. 값타입은 동기화 없이 스레드 간에 데이터를 전달할 수 있습니다. 따라서 안정성을 높이는 관점에서 바라볼 때 이런 모델은 코드를 보다 더 예측가능하게 만들어 줄 것입니다. 77 | 78 | ### Reference: 79 | 80 | - https://www.raywenderlich.com/1314-getting-to-know-enums-structs-and-classes-in-swift 81 | 82 | - http://dsnight.tistory.com/50 83 | 84 | - https://soooprmx.com/archives/5355 85 | -------------------------------------------------------------------------------- /basic/var_let.md: -------------------------------------------------------------------------------- 1 | # 변수와 상수 2 | 3 | ## 변수 var 4 | 5 | var는 variable의 약자로 변수를 말합니다. 참조 및 조작이 가능합니다. 변경 가능하다는 의미에서 mutable한 특성을 갖는다고 말합니다. 보통 자료형을 선언하고 값을 대입합니다. 6 | 7 | ```swift 8 | var 변수명: 자료형 = 값 9 | var centimeter: Double = 5.00 10 | ``` 11 | 12 | 일반적으로 Swift는 매우 똑똑한 언어이기 때문에 변수의 자료형을 추론할 수 있습니다. (자료형 추론) 아래의 예시와 같이 Double이라고 자료형을 명시하지 않아도 언어 스스로 값을 보고 자료형을 추론하여 계산합니다. 13 | 14 | ```swift 15 | var centimeter = 5.00 16 | ``` 17 | 18 | ## 상수 let 19 | 20 | 상수는 항상 같은 수라는 의미입니다. 변수와 달리 변경이 불가능하여 immutable하다고 말합니다. 21 | 22 | ```swift 23 | let centimeter: Double = 5.00 24 | ``` 25 | 26 | ### Reference: 27 | 28 | - https://m.blog.naver.com/jdub7138/220924072382 29 | -------------------------------------------------------------------------------- /etc/assets/ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/etc/assets/ci.png -------------------------------------------------------------------------------- /etc/assets/vn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/etc/assets/vn.png -------------------------------------------------------------------------------- /etc/face.md: -------------------------------------------------------------------------------- 1 | # Apple Face Detection with 2 | ## 1. Core Image / 2. Vision 3 | 4 | --- 5 | 애플은 CIDetector 클래스를 통해 코어 이미지 프레임워크의 공개 API에서 얼굴 탐지 기능을 처음 공개하였습니다. 이 API는 포토와 같은 Apple 앱에서도 내부적으로 사용되었습니다. 6 | 7 | CIDetector의 초기 버전은 Viola-Jones 탐지 알고리즘에 기반한 방법을 사용했습니다. 이후 CIDetector의 개선은 전통적인 컴퓨터 비전의 발전에 기초하였습니다. 8 | 9 | 그러나 딥러닝의 출현과 컴퓨터 vision 문제에 대한 딥러닝 적용으로, 얼굴 감지 정확성의 최첨단 기술이 큰 도약을 했습니다. 10 | 11 | 그리고 이를 적절히 활용하여 2017년, CoreML과 Vision이 등장하였습니다. 12 | 13 | [Viola-Jones 탐지 알고리즘](https://en.wikipedia.org/wiki/Viola–Jones_object_detection_framework) 14 | 15 | [애플의 Face Detection 진보와 Vision 원리 설명](https://machinelearning.apple.com/2017/11/16/face-detection.html#1) 16 | 17 | --- 18 | 19 | ## 1. Core Image 20 | 21 | Core Image는 정지된 영상과 비디오 영상을 위해 실시간에 가까운 처리 기능을 제공하도록 설계된 영상 처리 및 분석 기술입니다. GPU 또는 CPU 렌더링 경로를 사용하여 Core Graphics, Core Video 및 Image I/O 프레임워크의 이미지 데이터 유형에서 작동합니다. 22 | 23 | ### Face Detection using Core Image ### 24 | 25 | A: Convert the UIImage to CIImage 26 | 27 | ```swift 28 | let ciimage = CIImage(image: UIImage) 29 | ``` 30 | 31 | B: Set the CIDetector accuracy 32 | ```swift 33 | let detectOption = [CIDetectorAccuracy: CIDetectorAccuracyHigh] 34 | ``` 35 | 36 | C: Create an object of CIDetector of typeface with options (CIDetector는 정지된 이미지 또는 비디오에서 중요한 특징(예: 얼굴 및 바코드)을 식별하는 이미지 프로세서입니다.) 37 | ```swift 38 | let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: detectOption) 39 | ``` 40 | D: Call features function of CIDetector which will extract and return an array of faces from the given image. 41 | ```swift 42 | let faces = faceDetector.features(in: ciimage) 43 | ``` 44 | 45 | #### Observation Result #### 46 | 47 | 48 | 49 | --- 50 | 51 | 52 | ## 2. Vision 53 | 54 | Vision은 머신러닝을 이용하여 고성능 이미지 분석 및 얼굴 식별, 영상 및 비디오 장면 분석을 위한 컴퓨터 비전 기법을 적용합니다. 55 | 56 | ### Face Detection using Vision ### 57 | 58 | A: 얼굴 검출을 요청하는 이미지 분석 리퀘스트인 VNDetectFaceRectanglesRequest와 VNSequenceRequestHandler를 initialize 합니다. 59 | 60 | ```swift 61 | let faces = faceDetector.features(in: ciimage) 62 | ``` 63 | B: Convert the UIImage to CIImage 64 | 65 | ```swift 66 | let faceDetectionRequest = VNDetectFaceRectanglesRequest() 67 | 68 | let faceDetectionHandler = VNSequenceRequestHandler() 69 | ```` 70 | C: Perform the face detection request on the given image 71 | 72 | ```swift 73 | try? faceDetectionHandler.perform([faceDetectionRequest], on: ciimage) 74 | ```` 75 | D: Return an array of Faces 76 | 77 | ```swift 78 | let results = faceDetection.results as? [VNFaceObservation] 79 | ```` 80 | 81 | #### Observation Result #### 82 | 83 | async_problem.png 84 | 85 | 86 | 87 | --- 88 | 89 | # Firebase MLKit를 이용한 image Labeling 90 | 91 | ML Kit의 얼굴 감지 API를 사용하면 영상에서 얼굴을 감지하고 주요 얼굴 특징을 식별할 수 있습니다. 92 | 93 | 얼굴 탐지 기능을 사용하면 자아와 초상화를 윤색하거나 사용자 사진에서 아바타를 생성하는 것과 같은 작업을 수행하는 데 필요한 정보를 얻을 수 있습니다. 94 | 95 | UIImage 혹은 CMSampleBufferRef를 이용하여 요청하면 해당 사진에 대한 얼굴 정보가 비동기적으로 받아와집니다. 96 | 97 | 애플의 vision등과 비교하였을 때, 98 | 99 | Apple vision은 검출된 face에 대해서 tracking 하지 않기 때문에 실시간 검출의 결과가 일정하지 않지만 GPU를 이용하여 매우 빠릅니다. 100 | 101 | Google firebase face detection은 검출된 face에 대해서 tracking 하기 때문에 균일하게 이어지는 실시간 검출에 대해서 유리하지만, CPU만을 이용하기 때문에 상대적으로 느립니다. 102 | 103 | 자세한 사용법은 [Firebase 사용법]( 104 | https://firebase.google.com/docs/ml-kit/ios/detect-faces) 을 참조할 수 있습니다. 105 | -------------------------------------------------------------------------------- /features/assets/monad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/features/assets/monad.png -------------------------------------------------------------------------------- /features/assets/wrapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/features/assets/wrapping.png -------------------------------------------------------------------------------- /features/error_handling.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | 프로그램 실행시 에러가 발생하면 그 상황에 대해 적절한 처리가 필요합니다. 이 과정을 에러 처리라고 부릅니다. Swift에서는 런타임에 에러가 발생한 경우 그것의 처리를 위해 에러의 발생(throwing), 감지(catching), 증식(propagating), 조작(manipulating)을 지원하는 일급 클래스(first-class)를 제공합니다. 4 | 5 | 어떤 명령은 항상 완전히 실행되는 것이 보장되지 않는 경우가 있습니다. 그런 경우에 옵셔널을 사용해 에러가 발생해 값이 없다는 것을 표시할 수 있지만, 어떤 종류의 에러가 발생했는지 확인할 수는 없습니다. 이럴 때는 구제적으로 발생한 에러를 확인할 수 있어야 코드를 작성하는 사람이 각 에러의 경우에 따른 적절한 처리를 할 수 있습니다. 6 | 7 | 예를들어, 디스크에서 파일을 읽어 데이터를 처리하는 일을 한다고 할 때 이 작업이 실패할 경우의 수는 여러가지가 존재합니다. 파일이 특정 경로에 존재하지 않거나, 읽기 권한이 없거나 혹은 파일의 데이터가 식별할 수 있는 포맷으로 적절히 인코딩 되지 않은 경우 등 종류별 에러 상황을 식별해 사용자에게 제공해 주면 프로그램 실행 중 발생한 각 에러별로 사용자가 적절히 대응할 수 있도록 도울 수 있습니다. 8 | 9 | ## Representing and Throwing Errors 10 | 11 | Swift에서 에러는 Error 프로토콜을 따르는 타입의 값으로 표현됩니다. 12 | 13 | Swift의 열거형은 특별히 이런 관련된 에러를 그룹화(Grouping)하고 추가적인 정보를 제공하기에 적합합니다. 예를들어, 게임 안에서 판매기기 동작의에러 상황을 다음과 같이 표현할 수 있습니다. 14 | 15 | ```swift 16 | enum VendingMachineError: Error { 17 | case invalidSelection 18 | case insufficientFunds(coinsNeeded: Int) 19 | case outOfStock 20 | } 21 | ``` 22 | 23 | 에러를 발생시킴으로써 무언가 기대하지 않았던 동작이 발생했고 작업을 계속 수행할 수 없다는 것을 알려줄 수 있습니다. 어떤 함수, 매소드 혹은 초기자가 에러를 발생 시킬 수 있다는 것을 알리기 위해서 throw 키워드를 함수 선언부의 파라미터 뒤에 붙일 수 있습니다. throw 키워드로 표시된 함수를 throwing function이라고 부릅니다. 만약 함수가 리턴 값을 명시했다면 throw 키워드는 리턴 값 표시 기호인 -> 전에 적습니다. 24 | 25 | ```swift 26 | func canThrowErrors() throws -> String 27 | ​ 28 | func cannotThrowErrors() -> String 29 | ``` 30 | 31 | ## Handling Errors 32 | 33 | 에러가 발생하면 특정 코드영역이 해당 에러를 처리하도록 해야합니다. 예를들어, 문제를 해결하거나, 혹은 우회할 수 있는 방법을 시도하거나 아니면 사용자에게 실패 상황을 알리는 것이 에러 처리의 방법이 될 수 있습니다. 34 | 35 | Swift에서 4가지 에러를 처리 방법이 있습니다. 36 | 37 | 1. 에러가 발생한 함수에서 리턴값으로 에러를 반환해 해당 함수를 호출한 코드에서 에러를 처리하도록 하는 방법 (guard 등) 38 | 39 | 2. 두번째로 do-catch 구문을 사용하는 방법 40 | 41 | 3. 셋째로 옵셔널 값을 반환하는 방법 (try? 사용) 42 | 43 | 4. 마지막으로 강제로 에러 발생을 중지하기 (try! 사용) 44 | 45 | ### Reference: 46 | 47 | - https://jusung.gitbook.io/the-swift-language-guide/untitled-13 48 | -------------------------------------------------------------------------------- /features/fast.md: -------------------------------------------------------------------------------- 1 | # LLVM & Clang 2 | 3 | Swift의 3가지 큰 특징 중 하나인 fast를 위한 애플의 노력 결과입니다. 4 | 5 | 6 | ## LLVM 7 | 8 | 우리는 앱을 개발하기 위해 소스코드를 작성합니다. 그리고 나서 작성한 소스코드를 빌드한 후 실행합니다. 소스코드는 사람이 이해할 수 있는 형태의 언어로 작성된 코드입니다. 기계(iPhone, iPad, Macbook 등)가 이 소스코드를 실행하려면 소스코드를 기계가 이해할 수 있는 형태 즉, 바이너리나 기계어로 변환해야 합니다. 이 변환을 하는 것이 컴파일러의 역할입니다. 9 | 10 | 컴파일러는 보통 Frontend, Optimzer, Backend의 세 가지 구성요소를 가지고 있습니다. 11 | 12 | 13 | 14 | - Frontend : 어휘분석(Lexical analysis), 구문분석(Syntax analysis), 의미분석(Semantic analysis), 중간코드 생성(Immediate code generation) 15 | 16 | 17 | - Optimizer : 런타임시 성능향상을 위해 중복계산 제거 및 기타 여러 변환 실행 18 | 19 | - Backend : 각 코드를 타켓 아키텍처에 맞는 인스트럭션 셋(Instruction Set)으로 매핑해 실행코드 생성 20 | 21 | LLVM은 이러한 기존 컴파일러의 비효율성을 개선하고자 나온 컴파일러 입니다. LLVM은 기존의 기존의 컴파일러의 3가지 주요 구성요소를 분리해서 각각의 부분들을 조합할 수 있게 디자인 했습니다. 22 | 23 | 24 | 25 | 소스 코드는 먼저 Swift Abstract Syntax Tree (AST)로 변환됩니다. 다음 Swift Intermediate Language(SIL)로 변환되어 Swift를 위한 최적화를 실시한 후 LLVM IR로 변환 된 LLVM Optimizer에 보내집니다. 26 | 27 | LLVM이 기존의 컴파일러의 3가지 구성요소를 분리했다는 것 외에 가장 다른 점은 바로 비트코드라 불리는 중간코드(IR : Intermediate Representation)를 생성한다는 것입니다. 그래서 Frontend에서는 비트코드를 생성하고 Backend에서는 이 코드를 받아서 타겟에 맞는 결과물을 생성합니다. 이 비트코드를 사용함으로 얻는 장점은 효율성/확장성입니다. 28 | 29 | 만약 일반적인 컴파일러가 새로운 언어를 지원한다고 가정해 보고 그 언어를 K라고 해보겠습니다. K언어를 x86아키텍처, PowerPC, ARM등 각기 다른 아키텍처의 기계어를 생성하려면 3개의 컴파일러를 작성해야 하고 컴파일러 각각의 Frontend, Optimiser, Backend를 구현해 주어야 합니다. 하지만 LLVM을 이용하면 K언어의 Frontend 부분만 개발하면 여러 아키텍처를 타겟으로 빌드할 수 있습니다. (clang이 LLVM에서 C, C++, Objective-C같은 C언어 계열의 Frontend입니다.) 30 | 31 | LLVM 덕분에 GCC(GNU Compiler Collection)와 비교했을때, 컴파일 속도는 2배 빨라지고, 빌드된 실행파일의 크기도 2~30% 줄일 수 있었습니다. 실행 속도 또한 파일의 크기가 줄어든 것과 비례에 빨라졌습니다. 뿐만아니라 LLVM가 있었기때문에 Block을 지원하고 GCD(Grand Central Dispatch)가 가능해졌습니다. Xcode의 다양한 Instruments 기능과 Xcode IDE와의 연동도 역시 LLVM 덕분입니다. 32 | 33 | ## Clang 34 | 35 | LLVM은 컴파일과 링크 시 런타임 등 모든 지점에서 프로그램을 최적화 할 수 있는 컴파일러 기반이다. 현재 최신 버전 Xcode 컴파일러 프론트엔드에 Clang, 백엔드에 LLVM 조합이 사용되고 있지만, LLVM이 채택 된 당초는 프론트엔드는 GCC가 사용 되고 있었습니다. 기존 Objective-C 컴파일러는 GCC를 이용하고 있었지만, 확장이 어렵기 때문에 Apple은 Clang을 개발했습니다. 여러 장점이 있겠지만 대표적인 Clang의 장점으로 메모리 관리의 참조 카운트 방식을 말할 수 있습니다. 기존 GCC의 경우 가비지 컬렉션을 통해 메모리 관리를 시도합니다. 대표적으로 GC의 부작용이 2가지 언급되어지곤 합니다. 36 | 37 | - 1. GC가 언제 실행될 지 코드 상에서 알 수가 없습니다. 38 | 39 | - 2. GC로 인한 퍼포먼스 저하 40 | 41 | 그러나 ARC(Automatic Reference Counting)와 Static Analyzer의 등장으로 메모리 관리가 쉬워졌으며 퍼포먼스도 좋아졌습니다. ARC는 컴파일 시에 참조 횟수를 증감하는 코드를 자동으로 채웁니다. Static Analyzer를 사용하면 정적으로 잠재적인 메모리 누수를 자동으로 감지 할 수 있습니다. 이러한 Clang을 통한 확장으로 Swift의 성능이 향상되고 있다고 말할 수 있습니다. 42 | 43 | ### Reference: 44 | 45 | http://kyejusung.com/2015/11/llvm%EC%9D%B4%EB%9E%80-clang-%EB%B9%84%ED%8A%B8%EC%BD%94%EB%93%9C-%ED%8F%AC%ED%95%A8/ 46 | -------------------------------------------------------------------------------- /features/first_class_functions.md: -------------------------------------------------------------------------------- 1 | # First Class Function (일급함수) 2 | 3 | 프로그래밍 언어에서 함수를 값으로 다룰수 있는 개념입니다. (함수 스스로 일급 객체 취급) 4 | 5 | ## First-class citizen (1급 객체) 6 | 7 | 아래 조건을 충족한다면 1급 객체라고 할수 있습니다. 8 | 9 | 1. 변수나 상수에 할당 할 수 있어야 한다. 10 | 11 | 2. 객체의 인자로 넘길 수 있어야 한다. 12 | 13 | 3. 객체의 리턴값으로 리턴 할수 있어야 한다. 14 | 15 | 4. 컴파일 시점이 아닌 런타임 시점에 생성이 가능해야 한다. 16 | 17 | 5. 할당에 사용된 이름과 관계없이 고유한 구별이 가능해야 한다 18 | 19 | 20 | ### 1. 변수나 상수에 함수를 할당 21 | 22 | - 변수나 상수에 함수를 할당할 때, 함수는 실행되지 않고 객체만 할당됩니다. 23 | 24 | - 함수 타입으로 변수나 상수를 선언 가능합니다. 25 | 26 | - 인자값의 타입과 반환값에 관한 것만 표기합니다. 27 | 28 | ```swift 29 | func foo(base: Int) -> String { 30 | return "결과값은 \(base+1)입니다." 31 | } 32 | 33 | let f : Int -> String = foo // 함수 타입 선언 및 할당 34 | f(5) // 결과값은 6입니다. 35 | ``` 36 | 37 | 38 | ### 2. 함수의 반환 타입으로 함수를 사용 39 | 40 | ```swift 41 | func foo() -> String { 42 | return "this is foo()" 43 | } 44 | 45 | func boo() -> (Void -> String) { 46 | return foo 47 | } 48 | 49 | let b = boo() 50 | b() // this is foo() 51 | ``` 52 | ```swift 53 | func plus(a: Int, b: Int) -> Int { 54 | return a + b 55 | } 56 | 57 | // 함수의 반환타입을 바탕으로 함수의 변수 할당 58 | func calc(operand: String) -> (Int, Int) -> Int { 59 | switch operand { 60 | case "+": 61 | return plus 62 | default: 63 | return plus 64 | } 65 | } 66 | 67 | let c = calc(operand: "+") 68 | c(3,4) // return 7 69 | ``` 70 | 71 | ### 3. 함수의 인자값으로 함수를 사용 72 | 73 | ```swift 74 | func boo(param: Int) -> Int { 75 | return param + 1 76 | } 77 | 78 | func foo(base: Int, function f: Int -> Int) -> Int { 79 | return f(base) 80 | } 81 | 82 | foo(3, function: boo) // return 4 83 | = 84 | ``` 85 | 86 | ### Currying 87 | 88 | 이러한 일급함수의 특성을 사용하여 Currying이란 기법을 구현할 수 있습니다. Currying은 여러개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법을 말합니다. 89 | 90 | ```swift 91 | func curry(f: @escaping (X, Y) -> Z) -> (X) -> (Y) -> Z { 92 | return { x in 93 | { y in 94 | f(x, y) 95 | } 96 | } 97 | } 98 | 99 | func add(a: Int, b:Int) -> Int { 100 | return a + b 101 | } 102 | 103 | let result = curry(f: add)(3)(4) 104 | ``` 105 | 106 | curry(f: add)(3)(4)처럼 하나 씩 분리해서 받는 기법을 currying(커링) 이라고 합니다. 이러한 커링의 이점은 다음과 같습니다. 107 | 108 | 1. 여러 파라미터를 한번 에 받지않고 부분적으로 받은 후 함수의 실행을 늦출수 있습니다.(전체가 아닌 부분적으로 파라미터를 받는다고 해서 함수가 실행되지 않는다) 109 | 110 | 2. 변하지 않는 부분은 별도로 정의함으로써 간단하게 재사용 할 수 있습니다. 111 | 112 | 113 | ### Reference: 114 | - http://blog.xenomity.com/entry/Functional-Programming-1급-함수와-고차-함수의-개념 115 | - https://m.blog.naver.com/PostView.nhn?blogId=yukhyung&logNo=220801488911&proxyReferer=https%3A%2F%2Fwww.google.com%2F 116 | -------------------------------------------------------------------------------- /features/functional_programming.md: -------------------------------------------------------------------------------- 1 | # Functional Programming 이란 2 | 3 | 4 | 5 | Wikipedia의 정의에 따르면, 다음과 같습니다. 6 | 7 | > 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임 8 | 9 | 명령형의 함수는 프로그램의 상태의 값을 바꿀 수 있는 side-effect가 있어 같은 코드라도 실행되는 프로그램의 상태에 따라 다른 결과값을 낼 수 있지만, 함수형의 함수는 출력값은 함수에 입력된 인수에만 의존하므로 항상 동일한 결과가 나오게 됩니다. 10 | 11 | 때문에 명령형 함수의 side-effect를 제거하면 프로그램의 동작을 이해하고 예측하기가 훨씬 쉬워질 것입니다. 이 것이 함수형 함수의 핵심 동기입니다. 12 | 13 | 이런 부분을 보면 Swift에서 let과 value type이 차지하는 비중을 가늠해볼 수 있습니다. 14 | (프로그램의 상태에 의존적이지 않으므로) 15 | 16 | 즉, 기본적인 개념은 함수형 언어가 아니라도 적용 가능한 부분이 많으며 Objective-C Cocoa Framework에서도 mutable/immutable로 분리된 객체는 바탕에 깔린 기본적은 철학은 함수형에서 말하는 동기와 일맥상통하는 면이 있습니다. 17 | 18 | ## Functional Programming Language의 기본이 되는 세가지 function # 19 | 20 | > Swift는 아래의 세가지 모두를 가집니다. 그러므로 완전한 함수형 언어에 비하면 부족함이 있기는 하지만 하이브리드 언어로서 functional programming으로 나아가기 위한 기본적인 준비는 되어있다고 생각됩니다. 21 | 22 | ### 1) Swift에서의 Pure Function 23 | 24 | side-effect가 없는 함수, 즉, 실행이 외부에 영향을 끼치지 않는 함수입니다. 따라서 thread에 안전하고 병렬적인 계산이 가능합니다. 25 | 26 | 매우 쉽게 구현 할 수 있습니다. 가장 기본적이며 다른 functional programming의 속성과 결합하여 강력한 도구가 될 수 있습니다. 27 | 28 | ```swift 29 | func plus10(value: Int) -> Int { 30 | return value + 10 31 | } 32 | ``` 33 | 34 | ### 2) Swift에서의 Anonymous Function 35 | 36 | 이름이 없는 함수입니다. Swift의 closure나 Objective-C의 block 같은 것들이 이에 해당합니다. 37 | 38 | ```swift 39 | let f = { (a: Int) in return a + 10 } 40 | ``` 41 | 42 | 위의 코드 (클로저) 를 보면 정수 a를 받아서 10을 더해서 돌려주는 함수라는 것을 알 수 있습니다. 하지만, 함수의 이름은 없습니다. 다만 상수 f에 대입되어있을 뿐입니다. 43 | 44 | (클로저의 경우, 단순 이름이 없을 뿐만 아니라 정의되는 시점에 캡쳐를 통하여 주변환경(context)으로부터 여러 상수와 변수의 값의 참조들을 저장하거나 값 자체를 복사하여 내부적으로 저장할 수있다는 특성도 가지고 있습니다.) 45 | 46 | ### 3) Swift에서의 Higher-order Function 47 | 48 | 함수를 다루는 함수입니다. 함수를 인자로 받거나 함수를 반환하는 함수입니다. 이것이 가능한 이유는 Functional Programming에서는 함수도 값으로 취급하기 때문입니다. 49 | 50 | 51 | ```swift 52 | func run(completion: () -> Void) { 53 | completion() 54 | } 55 | ``` 56 | 57 | run이라는 함수는 function(anonymous function)을 인자로 받고 있습니다. 이런 함수를 higher-order function이라 합니다. 위의 경우는 함수를 인자로 받는 경우이지만, 반대로 함수를 return 할 수도 있습니다. 58 | 59 | 60 | ### Reference: 61 | - https://yobi.navercorp.com/iOSDev/posts/252 62 | -------------------------------------------------------------------------------- /features/functor.md: -------------------------------------------------------------------------------- 1 | # Functor 2 | 3 | 결론적으로 functor는 어떤 value를 **map 함수를 지원하는 context로 쌀 수 있는 어떤 자료형태 (container type)** 를 말합니다. 4 | 5 | map 함수를 사용하면 작업의도를 더 명확하게 표현할 수 있습니다. 즉, 어떻게가 아니라 무엇을 달성하려는지에 대해 더 잘 표현하는데 함수형 프로그래밍의 장점 중 하나라고도 할 수 있습니다. 아래는 그러한 장점의 예시입니다. 6 | 7 | 먼저, 루프를 사용하여 단순히 array element를 2배로 곱하는 예제입니다. 8 | 9 | ```swift 10 | var list: [Int] = [1,2,3] 11 | for element in list { 12 | list.append(element * 2) 13 | } 14 | print(list) // 2,4,6 15 | ``` 16 | 이를 map 함수를 사용하면 다음과 같이 처리할 수 있습니다. 17 | 18 | ```swift 19 | var list: [Int] = [1,2,3] 20 | let doubledNumbers = list.map { $0 * 2 } 21 | print(doubledNumbers) // 2,4,6 22 | ``` 23 | 24 | map은 array 뿐만 아니라 어떠한 컨테이너 타입에도 구현할 수 있는 고차 함수입니다. 여기에는 값이 있거나 또는 없음을 포장하는 Optional 타입도 해당합니다. 25 | 26 | ```swift 27 | let number: Int? = 100 // Optional(100) 28 | let transformedNumber = number.map{ $0 * 2 } 29 | print(transformedNumber.map{ $0%2 == 0 }) // Optional(true) 30 | ``` 31 | Optional.map은 nil에 대한 처리를 보장해줍니다. 이를 통해 연산의 중간과정에서 중첩된 if let 혹은 guard let을 사용해야 하는 번거로움을 피할 수 있습니다. 32 | 33 | ```swift 34 | let number: Int? = nil 35 | let transformedNumber = number.map{ $0 * 2 } 36 | print(transformedNumber.map{ $0%2 == 0 }) // Optional(nil) 37 | ``` 38 | 39 | ### Reference: 40 | - http://hiddenviewer.tistory.com/275 41 | -------------------------------------------------------------------------------- /features/monad.md: -------------------------------------------------------------------------------- 1 | # Monad 2 | 3 | Monad는 값이 있을 수도 있고 없을 수도 있는 상태를 포장하는 타입입니다. (Swift에서 Optional이 이에 해당합니다.) Monad는 [Functor](./functor.md) 의 한 유형입니다. 4 | 5 | Functor와 Monad는 컨테이너 타입에 적용할 수 있는 공통 연산을 기술하기 위한 하나의 디자인 패턴이라고 볼 수 있습니다. 6 | 7 | 이러한 Monad는 중첩되어 wrapping된 결과물을 flat하여주는 flatMap과 함께 사용할 때 매우 효과적입니다. ~~(Optional(Optional(Optional(5)))와 같은 이상한 타입을 즐기는 취향이 아닌 이상...)~~ flatMap과 함께 사용할 때의 대표적인 예를 그림으로 그려 표현하면 아래와 같습니다. 8 | 9 | 10 | 11 | ### Reference: 12 | - http://hiddenviewer.tistory.com/275 13 | -------------------------------------------------------------------------------- /features/optional.md: -------------------------------------------------------------------------------- 1 | # Optional 2 | 3 | ## nil 4 | 5 | 어떠한 value도 없는 경우를 말합니다.다만 유의할 점은 Swift의 nil 은 다른 언어에서 pointer가 존재하지 않는 값을 가리키는 것과는 다릅니다. (Reference or value type) 6 | 7 | ## Optional 이란? 8 | 9 | Swift에는 optional이라는 개념이 있습니다. optional은 ‘?’을 통해 표현되는데, 그 의미는 다음과 같습니다. “이 변수에는 값이 들어갈 수도 있고, 아닐 수도 있어(nil)” Swift에서는 기본적으로 변수 선언시 nil 값이 들어가는 것을 허용하지 않습니다. 그러므로 첫 번째 줄의 코드는 에러이고, 두 번째 줄은 Optional type(String?)으로 선언했으므로 에러가 아닙니다. Optional은 nil값으로 인해 일어나는 프로그램의 잠재적 오류를 보완하기 위해 탄생된 일종의 안전장치 입니다. 10 | 11 | ```swift 12 | var optionalString: String = nil 13 | var optionalString: String? = nil 14 | ``` 15 | 16 | ## Wrapping 17 | 18 | 19 | 20 | Optional에 대해 보다보면, 많은 곳에서 wrapping 이라는 개념이 나옵니다. Optional 타입은 기본적으로 wrap되어 있는 상태입니다. 즉, Optional 변수들은 제대로된 value가 있는 것인지, nil인 것인지 wrap되어 있어서 모르는 상태라고 생각하시면 됩니다. 그렇기 때문에(컴파일러 입장에서는 변수가 nil일 수도 있기 때문에) wrap된 상태에서는 설령 변수에 value값이 있다고 하더라도 바로 value가 출력되지 않습니다. 21 | 22 | ```swift 23 | var optionalString: String? = "Hello" 24 | print(optionalString) 25 | // 출력 결과: Optional("Hello") 26 | ``` 27 | 이 경우, optionalString이 nil일 수도 있기 때문에, 결과값 “Hello”가 출력되지 않고, Optional(“Hello”) 가 출력됩니다. 28 | 29 | ## Optional의 장점 30 | 31 | 프로그램 사용 도중 nil값이 될 수 있는 변수들로 인해 일어나는 **잠재적 오류를 예방**할 수 있습니다. 32 | 33 | ## Optional 접근 방법 34 | 35 | ### Force Unwrapping 36 | 37 | Optional Unwrapping이란 Optional 변수에서 Optional 껍데기를 벗겨내는 작업입니다. Optional로 선언했지만, 무조건 변수가 있는 상황이 보장된 경우 느낌표(!)를 쓰면 강제적으로 Optinal변수를 Unwrapping합니다. 다만, Forced unwrapping은 쉽게 에러를 낼 수 있기 때문에 항상 값이 있다고 보장할 수 있는 경우에만 사용하는 것이 권장됩니다. 38 | 39 | ```swift 40 | let value2: String! = nil 41 | if value2 != nil { 42 | print(value2) 43 | } 44 | ``` 45 | 46 | ### Optional Binding 47 | 48 | Optional Binding은 Optional 타입으로 선언된 변수에 값이 있는지 없는지를 확인할 수 있도록 해주는 기능입니다. Optional Binding을 사용하면 느낌표 없이 Optional 타입의 변수 값을 출력할 수 있어서 좀 더 안전한 형태로 값을 얻을 수 있습니다. 기본적인 형태는 다음과 같습니다. 49 | 50 | ```swift 51 | if let 변수명 = Optional 변수 { 52 | // 임시 변수에 Optional 변수의 value값이 할당됩니다. 53 | } 54 | ``` 55 | 아래는 예시입니다. 56 | 57 | ```swift 58 | // Optional type으로 선언한 myNumber 59 | let myNumber: Int? = 1234 60 | 61 | if let actualNumber = myNumber { 62 | print("\(myNumber)은 실제로 \(actualNumber)입니다.") 63 | } else { 64 | print("\(myNumber)는 변환될 수 없습니다.") 65 | } 66 | // 출력 결과 : Optional(1234)은 실제로 1234입니다. 67 | 68 | print(actualNumber) // error 69 | ``` 70 | 위의 예에서는 myNumber가 Optional 타입으로 선언되어 있습니다. 원래는 이 myNumber값을 출력하기 위해서는 !를 사용해야합니다. 하지만, Optional Binding은 먼저 이 myNumber의 값이 있는 경우와 없는 경우로 나누고, 값이 있는 경우를 if let 구문 안에 넣을 수 있습니다. 여기서는 actualNumber에 myNumber의 값을 할당하고, 값이 있다면 actualNumber에 이를 넘겨주어 바로 실제 값으로 사용할 수 있도록 해줍니다. 특이한 점은 actualNumber가 if문 안에서만 할당되는 로컬 변수라는 점입니다. if 밖에서는 actualNumber를 사용할 수 없습니다. 71 | 72 | > 즉, Optional Binding은 Optional type의 변수에 대한 nil 체크와 로컬변수에 이 값을 할당하는 두 가지 기능을 가지고 있습니다. 73 | 74 | ### Optional Chaining 75 | 76 | 여러 객체를 혼합해서 사용하다보면 Optional끼리의 연산이 필요한 경우가 있습니다. 이 경우에 객체마다 옵셔널 바인딩을 사용하게 되면, if문이 지나치게 중첩되는 결과가 발생할 것 입니다. 이럴 때에 Optional Chaining을 통해서 좀 더 간단하게 Optional 예외처리를 할 수 있습니다. 여러 개의 query가 묶여서 나타날 수 있고, 전체 연결은 하나라도 nil이라면 nil을 반환합니다. 77 | 78 | ```swift 79 | if let roomCount = john.residence?.numberOfRooms { 80 | print("residence에 값이 있습니다. 값 : \(roomCount)") 81 | } else { 82 | print("residence is nil") 83 | } 84 | # residence is nil 출력 85 | ``` 86 | 이 때, if let roomCount = john.residence?.numberOfRooms은 해당 chain에(residence?와 numberOfRooms) 값이 있는지 없는지를 체크하고, 하나라도 없으면 false를 반환합니다. 87 | 88 | 89 | ### Implicitly Unwrapped Optional 90 | 91 | 변수 선언 시에 무조건 값이 존재할 것이라고 선언하여 주는 것입니다. 이 역시 강제 언래핑과 함께 주의해서 사용해야 합니다. 객체가 생성된 이후에 nil이 될 수 없는게 확실한 경우 사용가능합니다. 92 | 93 | ```swift 94 | @IBOutlet weak var collectionView: UICollectionView! 95 | ``` 96 | 97 | ### With 98 | 99 | 옵셔널만을 위해 존재하는 것은 아니지만 옵셔널과 함께 유용하게 쓸 수 있는 몇 가지의 옵션이 존재합니다. 100 | 101 | ### 1. guard 102 | 103 | ```swift 104 | guard let strongSelf = self else { return } 105 | ``` 106 | 107 | ### 2. as 108 | 109 | 타입 캐스팅을 시도합니다. 110 | 111 | > as?: 해당 타입에 대해 맞으면 옵셔널 바인딩 아니면 nil로 처리합니다. 112 | 113 | > as!: 강제적 타입 변환을 시도합니다. 변환이 되지 않으면 크래쉬가 발생하기 때문에 가급적 사용하지 않는 것이 좋습니다. 114 | 115 | ### 3. try 116 | 117 | > try?: 시도하여 언래핑이 되지 않으면 nil이니 다른 조치를 취합니다. 118 | 119 | > try!: 강제적으로 언래핑을 시도 합니다. 120 | 121 | ### Reference: 122 | - https://hcn1519.github.io/articles/2017-01/swift_optionals 123 | -------------------------------------------------------------------------------- /features/pop.md: -------------------------------------------------------------------------------- 1 | # POP (Protocol Oriented Programming) 2 | 3 | OOP (객체지향 프로그래밍)에 기반을 둔 대부분의 언어는 대부분 클래스의 상속을 상요해 타입에 공통된 기능을 구현합니다. 그러나 스위프트는 클래스가 아닌 Struct로 대부분 기본 타입이 구현되어 있습니다. 상속이 되지 않는 Struct로 다양한 공통 기능을 가지게 된 해답은 프로토콜과 익스텐션에 있습니다. 4 | 5 | [Protocol에 대한 설명](../basic/protocol.md) 6 | 7 | ## POP를 사용하는 이유 8 | 9 | #### 1. 가벼움과 보안성 10 | 11 | - 인스턴스 생성시 프로토콜을 타입으로 사용함으로서 해당 인스턴스를 추상화하여 보안성을 높일 수 있습니다. 12 | 13 | ```swift 14 | protocol Eat { 15 | func eat() 16 | } 17 | 18 | class Human: Eat { 19 | func speak() { 20 | print("hello") 21 | } 22 | 23 | func eat() { 24 | print("yami") 25 | } 26 | } 27 | 28 | // Class로 접근 29 | let human1 = Human() 30 | human1.speak() 31 | human1.eat() 32 | 33 | // Protocol로 접근 34 | let human2: Eat = Human() 35 | //human2.speak() 에러, 접근 불가 36 | (human2 as! Human).speak() // 단, 구체적인 인스턴스로 캐스팅하여, 추상화에 의해 가려진 부분에 다시 접근할 수도 있습니다. 37 | human2.eat() 38 | ``` 39 | 40 | #### 2. Value Type 상속 41 | 42 | - Struct, 클래스, 열거형 등 구조화된 타입 중에 상속은 클래스 타입에서만 가능합니다. 43 | 44 | - 그러나 Protocol Extension을 통해 Struct, Enum같은 Value 타입도 상속 효과를 줄 수 있게 되었습니다. 45 | 46 | - 클래스는 참조 (Call by reference) 타입이므로 참조 추적에 비용이 많이 발생합니다. 비교적 비용이 적은 값 타입을 사용하고 싶어도 상속을 할 수 없으므로 그 때마다 기능을 다시 구현해 주어야 했지만 POP는 그 한계를 없앴습니다. 47 | 48 | - 코드 중복이 발생하는 상황 -> 상속이 없는 구조체로 만들수 있습니다. (성능적 이득) 49 | 50 | - Value Type을 통해 멀티 스레드 환경에서 Thread Safety 높일 수 있습니다. 51 | 52 | - 단, Anyobject 프로토콜을 protocol’s inheritance list에 추가할 경우, struct나 enum이 아닌 class만 프로토콜을 따르게 할 수 있습니다. 53 | 54 | ```swift 55 | protocol GreetProtocol { 56 | func greet() 57 | } 58 | 59 | extension GreetProtocol { 60 | func greet() { 61 | print("hi there") 62 | } 63 | } 64 | 65 | struct SingerStruct: GreetProtocol { 66 | let canSing = true 67 | } 68 | 69 | let tom = SingerStruct() 70 | tom.greet() 71 | ``` 72 | 73 | #### 3. 다중 상속과 수평 확장 74 | 75 | - 하나의 Superclass만 상속 가능한 class와 달리 여러 프로토콜을 상속받을 수 있습니다. 76 | 77 | - OOP에서 클래스의 상속은 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가 기능을 구현하는 수직 확장이지만, POP는 기존의 타입에 기능을 추가하는 수평적인 확장 구조를 지닙니다. 78 | 79 | ```swift 80 | protocol CanShootThrees {} 81 | protocol CanBlock {} 82 | protocol CanRebound {} 83 | 84 | struct PureShooter: CanShootThrees {} 85 | struct DefensiveForword: CanBlock, CanRebound {} 86 | struct SuperStar: CanShootThrees, CanBlock, CanRebound {} 87 | ``` 88 | 89 | #### 4. Generics 더하기 90 | 91 | - 제너릭과 함께 쓰면 더 강력한 활용도를 갖습니다. 92 | 93 | 94 | ### Reference: 95 | - http://blog.yagom.net/531 96 | - http://blog.naver.com/PostView.nhn?blogId=jdub7138&logNo=220968251035&beginTime=0&jumpingVid=&from=search&redirect=Log&widgetTypeCall=true 97 | -------------------------------------------------------------------------------- /features/safe.md: -------------------------------------------------------------------------------- 1 | # Safe features of Swift 2 | 3 | Optional과 Error Handling외에도 다양한 기능을 통하여 스위프트는 안전한 프로그래밍을 구현하고 있습니다. 4 | 5 | ### 간단한 상수 변수 처리 6 | 7 | 변수는 var로 선언하고 상수는 let으로 선언합니다. 변수와 상수 선언을 각각의 키워드로 만든 것은 안전성을 염두에 둔 언어 설계라고 밝히고 있습니다. 개발자의 의도가 명확해지기 때문입니다. 8 | 9 | ### 강력한 타입 통제 10 | 11 | 자료형에 있어서는 변수를 처음 선언할 때 자료형이 유지가 돼서 다른 자료형을 대입할 수 없습니다. 자료형을 섞을 때 자동 형변환은 없으며 명시적으로 변환이 필요합니다. 함수 등에서 역시 인자의 자료형, 반환하는 자료형을 명시합니다. 12 | 13 | ### guard 14 | -------------------------------------------------------------------------------- /frameworks/about.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | ## Library 4 | 5 | 라이브러리란 자주 쓰일 만한 기능들을 모아 놓은 유틸(클래스)들의 모음집입니다. 재사용이 필요한 기능으로 반복적인 코드 작성을 없애기 위해 언제든지 필요한 곳에서 호출하여 사용할 수 있도록 Class나 Function으로 만들어진 것입니다. 사용 여부는 코드 작성자 선택 사항이며 새로운 라이브러리 제작 시에도 엄격한 규칙이 존재하지 않습니다. 제작 의도에 맞게 작성하면 됩니다. 6 | 7 | 예시로 표현하자면 톱, 망치, 삽같은 연장입니다. 도구를 쓸 때, 급하면 썰어야 할 곳에 망치를 쳐도 됩니다. 땅 파야할 때 톱으로 땅을 긁어내도 됩니다. 8 | 9 | 사람은 도구를 선택하는 입장이기 때문에, 어떤 도구를 사용하든 원하는 것을 만들어낼 수 만 있으면 됩니다. 10 | 11 | 대표적인 iOS 라이브러리들은 [여기](https://github.com/vsouza/awesome-ios)에서 확인할 수 있습니다. 12 | 13 | ## Frameworks 14 | 15 | GoF의 디자인 패턴으로 유명한 랄프 존슨(Ralph Johnson) 교수는 프레임워크를 "소프트웨어의 구체적인 부분에 해당하는 설계와 구현을 재사용이 가능하게끔 일련의 협업화된 형태로 클래스들을 제공하는 것" 이라고 정의하였습니다. 16 | 17 | 프레임워크는 라이브러리와 달리 어플리케이션의 틀과 구조를 결정할 뿐만 아니라, 그 위에 개발된 개발자의 코드를 제어합니다. 프레임 워크는 구체적이며 확장 가능하느 기반 코드를 가지고 있으며 설계자가 의도하는 여러 디자인 패턴의 집합으로 구성되어 있습니다. 18 | 19 | 사용자(프로그래머)와 실제 구현하고자 하는 기능 사이에, 사용자로 하여금 구현하고자 하는 기능을 쉽게 쉽게 제공해주는 중간 계층이란 면에 있어서 라이브러리와 프레임워크는 일견 비슷한 점이 있습니다. 20 | 21 | 그러나 앞서 언급하였다시피 프레임워크는 설계의 기반이 되는 부분을 기술한 확장 가능한 기반 코드를 포함한다는 점에서 라이브러리와 차이점이 존재합니다. 프레임워크는 어플리케이션의 흐름을 통제하지만 라이브러리는 그러지 않습니다. 확장 가능한 기반 코드에는 프레임워크 제작자의 설계 철학이 담겨 있으며, 차후 이 프레임워크 사용자가 제작자가 설계한 구조를 유지하면서 확장할 수 있도록, 제작자에 의해 의도된 제약 사항이 존재합니다. 이런 제약 사항은 흔히 디자인 패턴을 구현한 코드의 형태로 표현되는데, 가장 흔한 예로 MVC 패턴을 말할 수 있습니다. 22 | 23 | 24 | 예시로 표현하자면 프레임워크는 차, 비행기, 배같은 탈것입니다. 사람이 타서 엔진 켜고, 기어 넣고, 핸들 돌리고, 운전하거나, 조종하거나 해야합니다. 탈것은 정해진 곳으로만 다녀야 합니다. 차를 타고 하늘을 날거나, 배를 타고 땅으로 갈 수는 없습니다. 하지만, 그 목적에 맞게 만들어져 있기 때문에, 톱이나 망치를 들고 먼저 탈것을 만들어야할 필요가 없습니다. 그저 정해진 규칙에 맞춰서 엔진, 기어, 핸들만 잘 돌리면 됩니다. 25 | 26 | ## API 27 | 28 | 개발자가 공개적으로 노출한 멤버들을 사용하여 기능에 접근하고, 해당 기능을 구현하는데 사용된 코드를 숨길 수 있는 인터페이스입니다. 29 | 30 | 라이브러리와 차이점을 들자면, 라이브러리는 특정 목적을 위해 별도로 제공되는 함수들의 모음으로서 외부로 노출되는 형태와 내부에서 돌아가는 형태 둘 다 포함한 개념입니다. 그러나 API는 외부로 노출되는, 인터페이스 역할을 하는 함수들의 모음으로서 API를 통해 라이브러리가 사용되는 개념입니다. 31 | 32 | ## 아키텍쳐 33 | 34 | 기획한 내용을 프로그램화했을 경우 필요한 주요 특징을 기술적으로 설계하고 명시하는 것입니다. 도면과 같은 역할입니다. 35 | 36 | ## SDK 37 | 38 | 소프트웨어 개발 도구 모음입니다. SDK 안에는 개발에 도움이 될 개발 도구 프로그램, 디버깅 프로그램, 문서, API 등이 있습니다. 39 | 40 | 41 | 42 | ### Reference: 43 | 44 | - http://jokergt.tistory.com/89 45 | 46 | - https://kldp.org/node/124237 47 | -------------------------------------------------------------------------------- /frameworks/arkit.md: -------------------------------------------------------------------------------- 1 | # ARKit 이해 # 2 | 3 | 4 | ## 기본 동작 로직 ## 5 | 6 | 7 | 8 | 9 | #### Ref : [WWDC 2018 Understanding ARKit Tracking and Detection](https://developer.apple.com/videos/play/wwdc2018/610/) #### 10 | 11 | 12 | 첫번째로 **ARSession**은 구성에서부터 AR Technology 실행까지 모든 것을 다루는 객체입니다. 13 | 14 | 15 | ARSession을 구성하였다면, 이제 다음에 실제로 어떤 분류의 기술들을 실행하고 싶은지 설명해주어야합니다. 16 | 17 | 여기서 어떤 분류라 함은, 어떠한 Tracking 기술을 사용할지, 혹은 어떠한 feature들이 가능해야하는지 등을 예로 수 있습니다. 즉 **ARConfiguration**를 설정 한 후, ARSession의 **run(_ configuration)** 메소드를 통해, 구성한 ARConfiguration를 호출하여, 세션을 돌립니다. 18 | 19 | 이후 ARSession의 내부적으로, 이미지들을 받기 위해, **AVCaptureSEssoin**을 구성하려고 할 것입니다. 게다가 motion sensor 데이터를 받아오는 것을 시작하기 위해, **CMMotionManager**를 호출할 것입니다. 20 | 21 | 즉, 기본적으로 ARKit용 기기의 built-in Input 시스템입니다. 22 | 23 | processing 후 결과는 초당 60 프레임의 **ARFrame**으로 반환됩니다. 24 | 25 | ARFrame은 AR Scene을 렌더링하는 데 필요한 모든 것을 제공하는 스냅 샷입니다. 26 | 27 | 여기까지가 위의 그림의 내용입니다. 28 | 29 | 30 | ## 기존에 존재했던 AR Technology ## 31 | 32 | ### Orientation Tracking ### 33 | 34 | **Orientation(방향)에 대해서만** Tracking을 하는 기술입니다. 기존의 ARKit에 보면 **AROrientationTrackingConfiguration**이 존재합니다. 35 | 36 | #### AROrientationTrackingConfiguration #### 37 | 38 | 기기의 orientation만 tracking하는 configuration. 39 | 40 | 정확히는 3DOF(three degrees of freedom), 즉, 세 개의 회전 축(roll, pitch, yaw)에 대해서 tracking을 한다고 볼 수 있습니다. 41 | 42 | #### roll, pitch, yaw란? #### 43 | 44 | 어떠한 object가 가지는 좌표계에서 한 좌표축(X, Y, Z)에 대한 회전을 가리키는 용어ㅇ입니다. 45 | 46 | **roll** : **X축**(object의 전,후 방향)을 기준으로 회전 하는 것입니다. 47 | 48 | **pitch** : **Y축**(object의 좌,우 방향)을 기준으로 회전 하는 것입니다. 49 | 50 | **yaw** : **Z축**(object의 수직 방향)을 기준으로 회전 하는 것입니다. 51 | 52 | 자세한 내용은 아래의 그림을 참고하시면 좋을 것 같습니다. 53 | 54 | 55 | 56 | Ref: [Roll? Pitch? Yaw?](https://m.post.naver.com/viewer/postView.nhn?volumeNo=4249905&memberNo=2493468&vType=VERTICAL) 57 | 58 | 59 | 이러한 3DOF만을 가지고 하는 motion tracking의 기본적 단계에서는 제한된 AR 경험을 할 수 밖에 없습니다. 이 configuration을 통해 가상의 object는 현실 세계에 표현이 될 수 있고, 기기를 회전 시켜봤을 때에도 tracking을 합니다. 하지만, 이 configuration은 기기의 이동에 대하여 tracking을 할 수 없습니다. 추가적으로, 3DOF Tracking은 plane(평면) detection, hit testing을 지원하지 않습니다. 60 | 61 | 아래의 그림은 **AROrientationTrackingConfiguration**에 대하여 잘 표현하고 있습니다. 62 | 63 | 64 | 65 | rotation에 대해서는 가능하나, 기기를 이동시켰을 경우에는 더 이상 tracking을 하지 못합니다. 66 | 67 | Ref: [AROrientationTrackingConfiguration](https://developer.apple.com/documentation/arkit/arorientationtrackingconfiguration) 68 | 69 | 70 | 71 | ### World Tracking ### 72 | 73 | 현실과 가상공간 간의 일치를 만들기 위해, ARKit은 **VIO(Visual-Inertial Odometry)**라는 기술을 사용합니다. World Tracking은 또한 scene의 내용들을 이해하고, 분석합니다. **hit-testing 기능(ARHitTestResult)**를 이용하여, 카메라의 이미지 안에 있는 특정 포인트들과 대응하는 실제 세계의 부분을 찾습니다. 또한 만약 현재 ARsession Configuration에서 **planeDetection**을 세팅한다면, ARKit은 카메라에 나오는 이미지에 대하여 평평한 면을 찾고, 그들의 위치와 크기를 보고합니다. 이러한 hit-test 결과 혹은 발견된 평면들을 사용하여, scene에 가상의 컨텐츠를 배치하거나, 상호작용을 할 수 있습니다. 74 | 75 | 76 | #### ARWorldTrackingConfiguration #### 77 | 78 | 이 Configuration은 앞서 설명한 OrientationConfiguration과는 다르게, **six degrees of freedom(6DOF)**를 사용합니다. 3DOF의 roll, pitch, yaw와 함께 세개의 translation 축들(x, y, z에 대한 움직임)입니다. 79 | 80 | 아래의 이미지에서도 볼 수 있듯이, AROrientationTracking과는 다르게 기기를 이동시킬 때에도 Tracking이 가능합니다. 81 | 82 | 83 | 84 | 85 | ##### 3가지 제공하는 기능 ##### 86 | 87 | - planeDetection : 평면 탐지. 이것을 ARSession에 ARPlaneAnchor 객체들로 넣어 사용합니다. 88 | - detectionImages : 이미 알고 있는 2D 이미지들의 움직임을 인지, 추적. 이것을 scene에 ARImageAnchor 객체들로 넣어 사용. 89 | - detectionObjects(**ARKit2**) : 알고 있는 3D 객체들을 인지, scene에 ARObjectAnchor 객체들로 넣어 사용. 90 | - hit-testing Methods : ARFrame, ARSCNView, ARSKView 등에 hit test를 사용하여, 카메라 화면의 2D 포인트에 대응하는 실제 세계의 3D 포인트를 찾음. 91 | 92 | 93 | 94 | #### VIO(visual-inertial odometry, 시각적 관성 거리계) #### 95 | 96 | 이 프로세스는 iOS 기기의 모션 감지 하드웨어의 정보와 기기의 카메라에서 볼 수있는 scene의 컴퓨터 비전 분석을 결합합니다. ARKit은 scene 이미지의 주목할 만한 특징들을 인식하고, 비디오 프레임에서 이러한 특징들의 위치 차이를 추적, 해당 정보를 동작 감지 데이터와 비교합니다. 그 결과, 기기의 위치와 동작을 고정밀도로 모델링합니다. 97 | 98 | 99 | #### ARHitTestResult [Class] #### 100 | 101 | ARSession의 기기 카메라 뷰에서의 한 지점을 검사하여, 실제 세계의 표면에 대한 정보를 찾을 수 있습니다. 자세한 사항은 추후 더 작성 혹은 [애플 문서](https://developer.apple.com/documentation/arkit/arhittestresult)를 참고하면 좋을 것 같습니다. 102 | 103 | 104 | #### World Tracking에 대한 분석(Best Practices and Limitations) #### 105 | 106 | World Tracking은 정확하지 않습니다. 이 과정은 실제적인 AR을 놀라운 정확도로 보여줄 수 있지만, 어찌돼었든, 항상 일관적이지 않거나, 실시간으로 측정하기 어려울 때도 존재한다는 기기의 물리적환경의 디테일들에 의존합니다. 높은 퀄리티의 AR을 만들고 싶다면, 아래의 3가지 팁들을 인지하고 있어야합니다. 107 | 108 | 1. 예측 가능한 빛? 광도? 조건 하에 AR을 디자인해야합니다. 109 | 110 | World Tracking은 이미지 분석을 통해서 하기 때문에 깨끗한 이밎가 필요합니다. Tracking 퀄리티는 카메라가 디테일한 요소들을 볼수 없을 경우에 낮아집니다.(ex 빈 벽이나 어두운 scene에 대해서는 Tracking하기 어렵습니다.) 111 | 112 | 2. 유저 피드백을 제공하기 위해 tracking 퀄리티 정보를 사용합니다. 113 | 114 | World tracking은 이미지 분석과 기기의 동작을 서로 연관 시킵니다. 115 | 116 | 3. 명확한 결과를 만들기 위해 평면 탐지에 시간을 충분히 주고, 만약 필요한 결과를 얻었다면, 평면 탐지를 종료합니다. 117 | 평면 탐지 결과들은 시간에 따라 다양하기 때문입니다. 118 | 119 | 120 | 121 | 122 | ### Plane Detection ### 123 | 124 | 세션이 카메라에서 캡쳐된 이미지 내에서 자동적으로 평평한 표면을 찾으려 하는지 여부 및 방법입니다. 단순히 말하자면 수직, 수평에 대한 평면을 찾는 방법입니다. 125 | 126 | 이 기술은 **ARWorldTrackingConfiguration**에서의 insatcne Property로 존재합니다. 127 | 128 | 기본 값으로는, 평면 탐지가 꺼져있으며, 만약 수평 혹은 수직에 대하여 평면 탐지를 세팅한다면, 세션은 **ARPlaneAnchor** object들을 추가하고, 각각에 쓰여진 뷰에 따라 **ARSessionDelegate, ARSCNViewDelegate, or ARSKViewDelegate** 에서 캡쳐된 비디오 이미지들의 공간이 평면인 표면으로 나타나는지 탐지하여, 알려줍니다. 129 | -------------------------------------------------------------------------------- /frameworks/assets/ARFaceGeometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/ARFaceGeometry.png -------------------------------------------------------------------------------- /frameworks/assets/ARSessionBaseLifeCycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/ARSessionBaseLifeCycle.png -------------------------------------------------------------------------------- /frameworks/assets/ARworldTrackingConfiguration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/ARworldTrackingConfiguration.png -------------------------------------------------------------------------------- /frameworks/assets/AVAudioSession.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/AVAudioSession.png -------------------------------------------------------------------------------- /frameworks/assets/ActivateSenario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/ActivateSenario.png -------------------------------------------------------------------------------- /frameworks/assets/CAEmitterLayer2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/CAEmitterLayer2.gif -------------------------------------------------------------------------------- /frameworks/assets/FeedbackTrackingQuality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/FeedbackTrackingQuality.png -------------------------------------------------------------------------------- /frameworks/assets/Multiroute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/Multiroute.png -------------------------------------------------------------------------------- /frameworks/assets/RouteChange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/RouteChange.png -------------------------------------------------------------------------------- /frameworks/assets/arkitBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/arkitBase.png -------------------------------------------------------------------------------- /frameworks/assets/arobjectAnchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/arobjectAnchor.png -------------------------------------------------------------------------------- /frameworks/assets/coreml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/coreml.png -------------------------------------------------------------------------------- /frameworks/assets/cubeImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/cubeImage.png -------------------------------------------------------------------------------- /frameworks/assets/directionalLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/directionalLight.png -------------------------------------------------------------------------------- /frameworks/assets/environmentTexturingExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/environmentTexturingExample.png -------------------------------------------------------------------------------- /frameworks/assets/imageTracking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/imageTracking.png -------------------------------------------------------------------------------- /frameworks/assets/interruption.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/interruption.png -------------------------------------------------------------------------------- /frameworks/assets/interruptionLifeCycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/interruptionLifeCycle.png -------------------------------------------------------------------------------- /frameworks/assets/interruption_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/interruption_flow.png -------------------------------------------------------------------------------- /frameworks/assets/keyword.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/keyword.jpeg -------------------------------------------------------------------------------- /frameworks/assets/ml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/ml.png -------------------------------------------------------------------------------- /frameworks/assets/neural.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/neural.png -------------------------------------------------------------------------------- /frameworks/assets/objectDetection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/objectDetection.png -------------------------------------------------------------------------------- /frameworks/assets/orientationTracking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/orientationTracking.png -------------------------------------------------------------------------------- /frameworks/assets/persistanceAR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/persistanceAR.png -------------------------------------------------------------------------------- /frameworks/assets/recorder_player_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/recorder_player_flow.png -------------------------------------------------------------------------------- /frameworks/assets/rotationBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/rotationBase.png -------------------------------------------------------------------------------- /frameworks/assets/vc_to_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/frameworks/assets/vc_to_view.png -------------------------------------------------------------------------------- /frameworks/audio.md: -------------------------------------------------------------------------------- 1 | 2 | # Audio # 3 | 4 | ## Fundamental Flow ## 5 | 6 | 7 | 8 | 9 | ## Route Change Flow ## 10 | 11 | 12 | 13 | ## Intuerruption Flow ## 14 | 15 | 16 | 17 | ## Usage ## 18 | 19 | create AudioManger Instance. 20 | 21 | ```swift 22 | // create instance with record file name 23 | // create instance called setupSession, createRecorder(fileName: fileName), setupNotification() 24 | audioManager = AudioManager(fileName: "sample") 25 | 26 | // set delegate 27 | audioManager.delegate = self 28 | 29 | // request microphone permission 30 | audioManager.requestMicrophonePermission { (allow) in 31 | } 32 | // record start 33 | audioManager.record() 34 | // record pause 35 | audioManager.recordPause() 36 | // record stop 37 | audioManager.recordStop() 38 | // player play 39 | audioManager.play() 40 | // player pause 41 | audioManager.audioPlayerPause() 42 | 43 | ``` 44 | 45 | ### Documentation ### 46 | 47 | 48 | #### Method #### 49 | 50 | - **init**(fileName: String, category: String = AVAudioSessionCategoryPlayAndRecord, categoryOptions: AVAudioSessionCategoryOptions = [.allowBluetooth, .allowBluetoothA2DP]) 51 | 52 | AudioManager를 생성하는 코드입니다. 53 | 54 | Parameter로는, fileName, category, categoryOption들이 있으며, 55 | 56 | fileName은 처음에 녹음할 파일의 이름, category는 AVAudioSessionCategory들 중 하나를 선택해야하며, 만약 그 중에서 선택하지 않을 경우, AVAudioSessionCategory의 default값인 'AVAudioSessionCategorySoloAmbient'이 선택이 됩니다. 57 | 58 | - **createRecorder**(fileName: String, recordSettings: [String: Any]) -> String? 59 | 60 | 녹음할 파일의 이름을 설정하고, 자신이 custom할 녹음세팅이 있다면, 세팅을 해줍니다. 기본적인 녹음세팅이 들어가 있으며, Recorder를 생성하였다면, 파일의 경로를 반환, 생성하지 못하였다면, nil값을 반환해줍니다. 61 | 62 | - **activeSession**(completionHandler: CompletionHandler?) 63 | 64 | AudioManager를 사용하고 있는 앱의 세션을 활성화시켜주는 코드입니다. completionHandler를 통해 세션이 활성화가 되었다면 true, 실패하였다면 false를 반환합니다. 65 | 66 | - **deactivateSession**(notifyOthersOnDeactivation: Bool = false, completionHandler: CompletionHandler?) 67 | 68 | AudioManager를 사용하고 있는 앱의 세션을 비활성화시켜주는 코드입니다. 69 | 파라미터로 있는 notifyOthersOnDeactivation가 true라면, 앱의 세션이 비활성화 되었다는 것을 다른 앱들에게도 알려주어, 다른 앱이 세션을 점유할 수 있게 해줍니다. false라면, 다른 앱들에게 알려주지 않아, 다른 앱이 세션을 활성화 시켜야 사용할 수 있게 됩니다. completionHandler를 통해 세션이 비활성화 되었다면 ture, 실패하였다면 false를 반환합니다. 70 | 71 | - **useLoudSpeaker**(completionHandler: CompletionHandler?) 72 | 73 | 기본 폰의 작은 소리가 아닌, 스피커 모드로 변환을 해주는 코드입니다. 74 | CompletionHandler를 통해, 스피커로 전환 된다면 true, 실패시 false를 반환합니다. 75 | 76 | - **useDefaultOutput**(completionHandler: CompletionHandler?) 77 | 78 | 오디오를 현재 카테고리와 모드에 맞는 시스템의 default 출력포트로 변경해주는 코드입니다. 79 | CompletionHandler를 통해, default로 변경이 된다면, true, 실패시 false를 반환합니다. 80 | 81 | 82 | 83 | 84 | 85 | #### Property #### 86 | 87 | - **retryCount**: when interrupt end, then this app's session can activate. but sometimes get audiosession fail. so retry to get audio session for this app by looping retryCount(default value = 10). 88 | - **isPlay**: get-only property. 89 | - true : audioPlayer playing or pause. 90 | - false : audioPlayer finish. 91 | - **isRecord**: get-only property. 92 | - true : audioRecorder recording or pause. 93 | - false : audioRecorder finish. 94 | - **recordEndTime**: get-only property, when user stop record, then recordEndTime update by current record.stop() 95 | - **recordedFileUrl**: get-only property, when user want to know recordedfile's url. 96 | - **AudioManagerDelegate**: AudioManagerDelegate. 97 | 98 | #### Delegate(some may optional) #### 99 | 100 | - **audioRecorderDidStart**(_ recorder: AVAudioRecorder) 101 | - audioRecorder started. 102 | 103 | - (*optional*)**audioRecorderDidPause**(_ recorder: AVAudioRecorder) 104 | - audioRecorder paused. 105 | - **audioRecorderDidStop**(_ recorder: AVAudioRecorder) 106 | - audioRecorder stoped. 107 | - **audioPlayerDidStart**(_ player: AVAudioPlayer, error: Error?) 108 | - audioPlayer started. 109 | - (*optional*)**audioPlayerDidPause**(_ player: AVAudioPlayer) 110 | - audioPlayer paused. 111 | - **audioPlayerDidFinsih**(_ player: AVAudioPlayer, successfully flag: Bool) 112 | - audioPlayer finished. 113 | - **audioPlayerDecodeErrorDidOccur**(_ player: AVAudioPlayer, error: Error?) 114 | - audioPlayer Decode Error occured. 115 | - (*optional*)**routeDidChanged**(reason: AVAudioSessionRouteChangeReason) 116 | - route is changed. 117 | - **routeDetectNewDevice**(_ newInputPortName: String, newOutputPortName: String) 118 | - plugged in new device's inputPort, outputPort. 119 | - **routeRemoveOldDevice**(_ oldInputPortName: String, oldOutputPortName: String) 120 | - plugged out old device's inputPort, outputPort. 121 | - (*optional*)**didInterruptBegin**() 122 | - other app get audio session so this app's audio session interrupted. 123 | - **didInterruptEnd**(shouldResume: Bool) 124 | - other app release that app's audio session so this notice for this app's session, record, play can activate. 125 | 126 | 127 | ### Audio Interruption Observe ### 128 | 129 | this app used **AVAudioSessionInterruptionNotification** for observing Interruption begin and end. 130 | 131 | Especially, when Interruption end, Notification's userinfo may have **AVAudioSessionInterruptionOptionKey** 's **shouldResume** for check this app can re-activate app's session so that recording can be maintainted. 132 | 133 | ### Route Change Observe ### 134 | 135 | this app used **AVAudioSessionRouteChangeNotification** for observing route change. 136 | 137 | ### Reference: 138 | https://oss.navercorp.com/JTF-P9/mskimProject/tree/master/AVAudio 139 | -------------------------------------------------------------------------------- /frameworks/audio_difference.md: -------------------------------------------------------------------------------- 1 | # AVAudioRecorder, AVAudioPlayer vs AVAudioEngine # 2 | 3 | ## 차이점 ## 4 | 5 | ### AVAudioRecorder, AVAudioPlayer ### 6 | **하나의 트랙**(하나의 input, 하나의 output)에 대하여 재생 / 녹음할 경우에는 AVAudioPlayer, AVAudioRecorder를 사용합니다. 7 | 8 | ### AVAudioEngine ### 9 | 10 | 더 복잡한 오디오 처리를 위해서라면 AVAudioEngine을 사용합니다. 11 | 12 | **AVAuidoInputNode** : 오디오의 입력 13 | 14 | **AVAudioOutputNoted** : 오디오의 출력 15 | 16 | 가령, **여러 트랙**(여러 input들과 여러 output들)으로 녹음/ 재생을 하며, 노드들을 연결하여 섞인 트랙을 만들기도 하고, 오디오 스트림들 처리, 3D 오디오와 같은 이팩트를 넣는 작업도 합니다. 17 | -------------------------------------------------------------------------------- /frameworks/audio_input_output.md: -------------------------------------------------------------------------------- 1 | # Output과 Input을 자신이 원하는 포트로 나오게 하기 # 2 | 3 | ## Input ## 4 | 5 | 가능한 input들을 찾아, setPreferredInput으로 자신이 원하는 Input을 바꿔주면 됩니다. 6 | 7 | ### AVAudioSession.availableInputs ### 8 | 9 | availableInputs는 현재 연결된 Input들([AVAudioSessionPortDescription]?)을 보여주는 property입니다. 10 | 11 | ### preferredInput, setPreferredInput ### 12 | 13 | - preferredInput : preferred된 입력포트를 반환합니다. 14 | - 기존에 preferred된 입력포트 존재한다면, 해당 입력포트를 반환 15 | - 기존에 preferred된 입력포트가 없을 경우에는 nil을 반환 16 | 17 | - setPreferredInput : 이 메소드는 선택한 입력 포트를 사용하는 입력 포트로 사용하게 합니다. 18 | - 사용자가 사용하고 있는 입력 포트와 동일한 입력 포트일 경우 : 아무런 영향이 없음. 19 | - 다른 입력 포트일 경우에, 입력 포트를 바꿔줌. 20 | - nil 값을 파라미터로 넣어 초기화 21 | 22 | 23 | 아래는 관련 코드입니다. 24 | 25 | ```swift 26 | do { 27 | try audioSession.setPreferredInput(input) 28 | } catch { 29 | print("setpreferredInput error \(error)") 30 | } 31 | 32 | ``` 33 | 34 | 35 | ## Output ## 36 | 37 | ### Loud Speaker ### 38 | 39 | 기본 아이폰의 스피커는 기존에 테스트한 결과 작게 나오는데, 그 이유가 Loud Speaker를 설정을 해주지 않아서 였습니다. 아래는 Loud Speaker를 설정하는 코드입니다. 40 | 41 | ```swift 42 | do { 43 | try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker) 44 | } catch { 45 | print("speaker port override error \(error)") 46 | } 47 | ``` 48 | 49 | 여기서 AVAudioSessionPortOverride.speaker의 경우를 AVAudioSessionPortOverride.none이라고 바꿀 경우에는, 현재 오디오의 카테고리의 default에 해당하는 오디오 route를 리턴해줍니다. 즉, Speaker가 아닌 현재의 output으로 output을 선택하려면, 아래와 같이 AVAudioSessionPortOverride를 .none이라고 설정 해주면 됩니다. 50 | 51 | 52 | ```swift 53 | do { 54 | try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker) 55 | } catch { 56 | print("speaker port override error \(error)") 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /frameworks/audio_port.md: -------------------------------------------------------------------------------- 1 | # AVAudioSession Port Type 정리 # 2 | 3 | 이 문서는 애플 문서의 [InputPort](https://developer.apple.com/documentation/avfoundation/avaudiosessionportdescription/input_port_types), [OutputPort](https://developer.apple.com/documentation/avfoundation/avaudiosessionportdescription/output_port_types), [I/OPort](https://developer.apple.com/documentation/avfoundation/avaudiosessionportdescription/i_o_port_types)를 참고 하였습니다. 4 | 5 | ## Input Port Types ## 6 | 7 | ### lineIn ### 8 | 9 | *Line-level input from the dock connector* 10 | 11 | - **Line-level**이란? [설명 링크](#https://www.jh-studio.com/archives/306) 잘 번역이 되어있는 것같아 같이 올립니다. 12 | 13 | - 일반적인 유선마이크나 무선 마이크의 경우에는 mic-level이며, 대부분의 오디오 기기들이 사용하는 것이 '라인 레벨'입니다. 14 | 15 | - 즉, line-level에 대한 input이 들어 온 경우에 **lineIn**이 호출이 됩니다. 16 | 17 | 18 | ### builtInMic ### 19 | 20 | *The built-in microphone on a device* 21 | 22 | 기본 아이폰 등의 기기에 들어있는 내장 마이크. 23 | 24 | 25 | ### headsetMic ### 26 | 27 | *A microphone that is built-in to a wired headset* 28 | 29 | 유선 이어폰의 마이크(있는 경우). 코드상으로는 **MicrophoneWired** 라고 호출이 됩니다. 30 | 31 | 32 | ## Output Port Types ## 33 | 34 | ### lineOut ### 35 | 36 | *Line-level output to the dock connector* 37 | 38 | **lineOut** 단자란? 기본적으로 해당 소스를 재증폭하여, 출력하는 기기에 연결하는 용도. 39 | 40 | 위에서 정의한 것처럼 라인 레벨에서의 출력을 해주는 port type을 말함. 41 | 42 | ### headphones ### 43 | 44 | *Output to a wired headset* 45 | 46 | 기본적인 유선 이어폰. 47 | 48 | ### builtInReceiver ### 49 | 50 | *Output to a speaker intended to be held near the ear* 51 | 52 | 일반적인 상황에서의 기기의 사운드(외부로 나가는 소리가 아님). 53 | 54 | ### builtInSpeaker ### 55 | 56 | *Output to the device’s built-in speaker* 57 | 58 | 통화시 등에서 외부 스피커 모드로 변경 등이 이에 해당한다. 외부로 그게 들리게 하기 위한 기기 내의 외부 스피커를 사용했을 경우에 사용. 59 | 60 | ### HDMI ### 61 | 62 | *Output to a device via the High-Definition Multimedia Interface (HDMI) specification* 63 | 64 | HDMI 포트를 통하여 나가는 포트. 65 | 66 | 67 | ### airPlay ### 68 | 69 | *Output to a remote device over AirPlay* 70 | 71 | AirPlay를 통하여 오디오를 애플 TV, 맥, AirPlay 호환 스피커 등으로 송출하는 Port. 72 | 73 | 74 | 75 | ### bluetoothLE ### 76 | 77 | *Output to a Bluetooth Low Energy (LE) peripheral* 78 | 79 | 저전력 블루투스인 주변 기기의 스피커 등의 Port. 80 | 81 | [Accessory Design Guidelines for Apple Devices](https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf) 이 가이드에서 애플 기기에서의 블루투스 등의 기기에 대한 가이드라인을 보여주고 있습니다. 82 | 83 | 84 | ### bluetoothA2DP ### 85 | 86 | *Output to a Bluetooth A2DP device* 87 | 88 | 블루투스는 프로파일이라고 미리 정의된 프로토콜을 통해 통신해야함. 89 | 90 | 스테레오 오디오 프로파일(**A2DP**) : 스테레오 출력에 사용되는 프로파일. 블루투스로 노래를 들을 때에 보통 사용. **소리 출력만 전송, 마이크 사용할 수 없다** 91 | 92 | 93 | ## I/O Port Types ## 94 | 95 | ### bluetoothHFP ### 96 | 97 | *Input or output on a Bluetooth Hands-Free Profile device* 98 | 99 | Hands Free(헤드셋) 프로파일(**HFP**) : 통화 목적 프로파일이며, 모노 사운드와 마이크데이터를 주고 받는 프로파일. 100 | 101 | 102 | 참고 - 블루투스 헤드셋 -> A2DP, HFP로 연결하여, 노래 듣다가 전화 변경 가능!, 하지만, 스테레오 사운드(A2DP)와 마이크는 동시 작동 불가. 103 | 104 | 105 | ### usbAudio ### 106 | 107 | *Input or output on a Universal Serial Bus device* 108 | 109 | USB 오디오에 연결하여 오디오 신호 전송하여 입출력하는 Port. 110 | 111 | ### carAudio ### 112 | 113 | *Input or output via Car Audio* 114 | 115 | 이 부분은 아직 작성중입니다. 116 | 117 | 아이폰이 직접적으로 자동차에 연결시 CarPlay가 실행 될 경우의 Port로 알고있습니다. 118 | 119 | **이슈** : 그렇다면 블루투스로 CarPlay를 연결한다, HFP 포트가 될지? 좀 더 알아봐야할듯합니다. 120 | -------------------------------------------------------------------------------- /frameworks/buffer.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Buffer for Data processing 4 | 5 | ### CMSampleBuffer 6 | CMSampleBuffer는 미디어 샘플 데이터를 파이프라인을 통해 이동하는 데 사용되는 **특정 미디어 유형(오디오, 비디오, muxed 등)의 0개 이상의 압축(또는 압축되지 않은) 샘플을 포함하는 핵심 기반 개체**입니다. 샘플 버퍼에는 샘플 레벨 및 버퍼 레벨 첨부 파일이 모두 포함될 수 있습니다. 샘플 레벨 첨부 파일은 버퍼의 각 개별 샘플(프레임)과 연결되며 타임스탬프 및 비디오 프레임 종속성과 같은 정보를 포함합니다. 7 | 8 | - CMBlockBuffer: 9 | 10 | 하나 이상의 미디어 샘플 11 | 12 | - CVImageBuffer: 13 | 14 | 각 미디어 샘플의 크기 및 타이밍 정보, 버퍼 레블 과 샘플 레벨의 첨부 파일, 다양한 유형의 이미지 데이터를 관리하기 위한 인터페이스입니다. 15 | 16 | CMSampleBufferGetSampleAttachmentsArray(_:createIfNecessary:) 기능을 사용하여 샘플 레벨 첨부 파일을 읽고 쓸 수 있습니다. 버퍼 수준 첨부 파일은 재생 속도 및 버퍼 사용 시 수행할 작업과 같이 버퍼 전체에 대한 정보를 제공합니다. 17 | 18 | 19 | ### CVBuffer 20 | 21 | CVBuffer는 **데이터의 버퍼와 상호 작용하는 방법을 정의하는 추상 기본 클래스 역할**을 합니다. 버퍼 개체는 비디오, 오디오 또는 다른 유형의 데이터를 저장할 수 있습니다. 22 | 23 | CVImageBuffer 및 CVPixelBuffer와 같이 Core Video에 의해 정의된 다른 모든 버퍼 유형은 CVBuffer에서 파생됩니다. 모든 코어 비디오 버퍼에서 CVBuffer 프로그래밍 인터페이스를 사용할 수 있습니다. 24 | 25 | ### CVImageBuffer 26 | 27 | CVImageBuffer는 **다양한 유형의 이미지 데이터를 관리하기 위한 인터페이스**입니다. 픽셀 버퍼 및 Core Video OpenGL 버퍼는 Core Video 이미지 버퍼에서 파생됩니다. 28 | 29 | ### CVPixelBuffer 30 | 31 | CVPixelBuffer는 **메인 메모리에 픽셀을 저장하는 이미지 버퍼입**니다. 프레임 생성, 비디오 압축 또는 압축 풀기 또는 Core Image를 사용하는 애플리케이션은 모두 Core Video 픽셀 버퍼를 사용할 수 있습니다. 32 | -------------------------------------------------------------------------------- /frameworks/calayer.md: -------------------------------------------------------------------------------- 1 | # CALayer 2 | 3 | 이미지 기반 컨텐츠를 관리하고 해당 컨텐츠에 대해 애니메이션을 수행할 수 있는 개체입니다. 레이어는 뷰에서 눈에 보여지는 부분을 다루는 중요한 기능입니다. 4 | 5 | 뷰에는 하나의 레이어만 있을 수 있습니다. 대신 레이어는 서브레이어(Sublayers)를 여럿 가질 수 있습니다. 그리고 이름답게 다층(multi layer) 구조로 구성 할 수 있습니다. 6 | 7 | 뷰를 여러개 쌓아서 만드는 것에 비해, 같은 모양을 레이어를 쌓아서 만드는게 훨신 가볍기 때문에 애니메이션 등에 유리합니다. 8 | 9 | 간단히 말해서, CALayer는 NSObject에서 상속되며, **렌더링, 애니메이션 등의 보이는 부분에 초점을 맞춥니다.** layer의 기본 작업은 사용자가 제공하는 시각적 콘텐츠를 관리하는 것이지만 배경색, 테두리 및 그림자 같은 시각적 속성 역시 설정되어 있습니다. 시각적 콘텐츠 관리 외에도, 계층은 해당 콘텐츠를 화면에 표시하는 데 사용되는 콘텐츠의 형태(위치, 크기 및 변환 등)에 대한 정보도 유지합니다. 10 | 11 | 12 | 13 | ## CAShapeLayer 14 | 15 | CASHapeLayer는 확장 가능한 벡터 경로를 사용하여 이미지를 사용하는 것보다 훨씬 빠릅니다. 여기에서 얻을 수 있는 또 다른 이점은 @2x 및 @3x 크기의 이미지를 더 이상 제공할 필요가 없다는 것입니다. 16 | 17 | 또한 선 두께, 색상, 대시, 선이 다른 선과 결합되는 방식, 해당 영역을 어떤 색상으로 채우는지 등을 원하는 대로 설정할 수 있는 다양한 특성이 있습니다. 18 | 19 | 먼저 색상, 경로 및 shape layer를 작성합니다. 20 | ```swift 21 | let rwColor = UIColor(red: 11/255.0, green: 86/255.0, blue: 14/255.0, alpha: 1.0) 22 | let rwPath = UIBezierPath() 23 | let rwLayer = CAShapeLayer() 24 | ``` 25 | 26 | 다음으로 shape layer's path를 그립니다. move(to:), addLine(to:)과 같은 방법을 사용하여 점 간 그림을 그려서 이 작업을 수행합니다. 27 | 28 | ```swift 29 | rwPath.move(to: CGPoint(x: 0.22, y: 124.79)) 30 | rwPath.addLine(to: CGPoint(x: 0.22, y: 249.57)) 31 | rwPath.addCurve(to:CGPoint(x: 249.37, y: 38.25), 32 | controlPoint1: CGPoint(x: 249.57, y: 85.64), 33 | controlPoint2: CGPoint(x: 249.47, y: 38.15)) 34 | rwPath.close() 35 | ``` 36 | 37 | 그리고, set up the shape layer를 set up 합니다. 38 | 39 | ```swift 40 | func setUpRWLayer() { 41 | rwLayer.path = rwPath.cgPath 42 | rwLayer.fillColor = rwColor.cgColor 43 | rwLayer.fillRule = kCAFillRuleNonZero 44 | rwLayer.lineCap = kCALineCapButt 45 | rwLayer.lineDashPattern = nil 46 | rwLayer.lineDashPhase = 0.0 47 | rwLayer.lineJoin = kCALineJoinMiter 48 | rwLayer.lineWidth = 1.0 49 | rwLayer.miterLimit = 10.0 50 | rwLayer.strokeColor = rwColor.cgColor 51 | } 52 | ``` 53 | 54 | 55 | ## CAEmitterLayer 56 | 57 | CAEmitterLayer는 CAEmitterCell의 인스턴스인 애니메이션 입자를 렌더링합니다. CAEmitterLayer와 CAEmitterCell 모두 렌더링 속도, 크기, 모양, 색상, 속도, 수명 등을 변경하는 특성이 있습니다. 58 | 59 | 이러한 CAEmitterLayer를 이용하여 입에서 쏟아져 나오는 입자들을 표현하였습니다. ex) 음표 60 | 다음과 같은 순서를 따릅니다. 61 | 62 | 1. emitter layer와 cell을 만듭니다. 63 | 2. 레이어의 랜덤 번호 생성기에 대한 시드를 제공하여 레이어의 이미터 셀과 같은 특정 특성을 랜덤화합니다. 64 | 3. 레이어의 위에 있는 CAEmitterCell을 렌더링모드에서 지정한 순서대로 렌더링합니다. 65 | 4. drawsAsynchronously를 true로 설정하여 퍼포먼스를 높입니다. 66 | 5. emitter position을 지정하여 줍니다. 67 | 6. emitter cell의 특성을 지정하여 줍니다. (velocity, color 등) 68 | 69 | 70 | emitter 71 | -------------------------------------------------------------------------------- /frameworks/environment_texturing.md: -------------------------------------------------------------------------------- 1 | # Environment Texturing # 2 | 3 | 4 | 5 | Environment texture는 scene의 특정 지점에서 모든 방향으로 뷰를 묘사하는 cube-map texture입니다. 3d asset rendering에서, environment texture는 표면들이 사실적으로 주변 환경으로부터 빛을 반사할 수 있는 이미지 기반 lighting 알고리즘의 기본 요소입니다. ARKit는 카메라 이미지를 사용하여, ARKit는 카메라 이미지를 사용하여, AR 세션 동안에 environment texture를 생성할 수 있으므로, ScenKit 혹은 다른 커스텀 렌더링 엔진이 AR에서 가상 객체에 사실적인 이미지 기반 lighting을 제공할 수 있습니다. 6 | 7 | 8 | 아래의 이미지는 Environment Texturing을 한 예시입니다. 9 | 10 | 11 | 12 | 13 | 아래의 코드는 Envrionment Texturing을 하는 예시 코드입니다. 14 | 15 | ```swift 16 | // Environment Texturing 17 | 18 | // Create world tracking configuration 19 | let configuration = ARWorldTrackingConfiguration() 20 | 21 | // Enable automatic environment texturing 22 | configuration.environmentTexturing = .automatic 23 | 24 | // Run the configuration 25 | session.run(configuration) 26 | ``` 27 | 28 | 29 | ## ARWorldTrackingConfiguration.EnvironmentTexturing ## 30 | 31 | world-tracking ARsession의 environment texture 생성하는 옵션. 32 | 33 | | Options | Description | 34 | |:--------|:--------| 35 | |**none** | ARKit이 environment map texture를 생성하지 않습니다.| 36 | |**manual** | ARKit은 명시적으로 세션에 추가하는 probe anchor에 대해서만 environment texture를 생성합니다.| 37 | |**automatic** | ARKiet은 언제 어디에 environment texture들을 생성할지 자동적으로 결정합니다.| 38 | 39 | - **menual environment texturing**을 사용할 경우, AREnvironmentProbeAnchor 객체를 생성하고 세션에 추가함으로써, light probe texture map들을 원하는 scene의 점들을 식별할 수 있습니다. 40 | - **automaic environment texturing**을 사용할 경우, ARKit은 자동적으로 AREnvironment ProbeAnchor 객체를 생성, 위치시키고 세션에 추가합니다. 41 | 42 | 이 두 경우에, ARKit은 세션이 카메라 형상을 수집함으로써, 자동적으로 environment texture들을 생성합니다. **session(_:didUpdate:)** 델리게이트 메소드를 사용하여, 언제 texture가 사용가능한지 알아내고, anchor의 environmentTexture 송성에서 액세스를 합니다. 43 | 44 | ## Realistic Rendering ## 45 | 46 | 앞서 설명한 것처럼, Environment texturing은 사실적인 렌더링을 합니다. 47 | 48 | 아래의 내용들은, 이러한 사실적인 묘사를 설명하는 요소들입니다. 49 | 50 | - Position and orientation 51 | - Scale 52 | - Lighting 53 | - Shadow 54 | - **Reflection of textures** 55 | 56 | 57 | ## AREnvironmentProbeAnchor ## 58 | 59 | 요약 : world-tracking AR session의 특정 공간 영역에 대한 environmental lighting 정보를 제공하는 객체. 60 | 61 | 이 anchor는 다른 anchor들과 같은 생명주기를 가지며, SceneKit의 ARSCNView에서 사용할 수 있습니다. 또한 물리적인 크기나 빛 반영 등에 대하여 처리를 해줍니다. 62 | -------------------------------------------------------------------------------- /frameworks/face_tracking_enhancements.md: -------------------------------------------------------------------------------- 1 | # Face Tracking Enhancements # 2 | 3 | ## Recap ## 4 | 5 | - 강력한 얼굴 탐지와 tracking 6 | - 매 프레임마다 위치와 방향을 업데이트함. 7 | - **ARFaceGeometry** 삼각형 그물코 모양의 집합으로 맞추어 인식합니다. 아래의 그림이 예시입니니다. 8 | 9 | 10 | 11 | 12 | - ARFaceAnchor 13 | 14 | face-tracking AR 세션에서 찾은 얼굴의 형태, 위상, 표현 등에 대한 정보. 15 | 16 | 17 | ## Directional Light Estimation ## 18 | 19 | 빛이 비추는 방향에 따라 얼굴에 나타나는 그림자, 밝기 등입니다. 20 | 21 | 아래는 예시입니다. 22 | 23 | 24 | 25 | ## Blendshapes Recap ## 26 | 27 | 표현들을 실시간으로 추적할 수 있습니다. 28 | 29 | 50가지 이상의 표현들을 tracking 합니다. 30 | 31 | 이러한 blend 모양들은 0 ~ 1사이의 값을 취합니다. 32 | 33 | 34 | ## Face Tracking Enhancements ## 35 | 36 | ### Gaze tracking ### 37 | 38 | tracking을 하여, 사람이 응시하는 곳, 왼쪽/ 오른쪽 눈의 변화를 추적하여 보여줍니다. 39 | 40 | ```swift 41 | open class ARFaceAnchor : ARTrackable { 42 | 43 | open var leftEyeTransform: simd_float4x4 { get } 44 | 45 | open var rightEyeTransform: simd_float4x4 { get } 46 | 47 | open var lookAtPoint: simd_float3 { get } 48 | } 49 | ``` 50 | 51 | 52 | 53 | ### Tongue support ### 54 | 55 | 혀가 나오는지에 대한 인식을 합니다. 56 | 57 | ```swift 58 | extension ARFaceAnchor.BlendShapeLocation { 59 | 60 | public static let tongueOut: 61 | ARFaceAnchor.BlendShapeLocation 62 | 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /frameworks/image.md: -------------------------------------------------------------------------------- 1 | # Image 2 | 3 | ### UIImage 4 | **앱에서 이미지 데이터를 관리하는 개체입니다.** 이미지 객체를 사용하여 모든 종류의 이미지 데이터를 표현 할 수 있으며, UIImage 클래스는 기본 플랫폼에서 지원하는 모든 이미지 형식의 데이터를 관리 할 수 있습니다. 5 | 6 | 이미지객체는 변경 불가능(immutable)하므로, 이미 존재하는 디스크에있는 이미지파일이나 프로그래밍으로 생성된 이미지 데이터에서 새로운 이미지객체를 만들어야 합니다. 7 | 8 | 이러한 속성은 변경할 수 없으며 초기화 시점에 이미지의 속성을 지정해야 합니다. 이는 또한 이러한 영상 개체가 모든 스레드에서 안전하게 사용된다는 것을 의미합니다. 9 | 10 | > 루프 등에서 UIImage를 이용할 경우, AutoReleasePool등을 이용하여 메모리관 리에 주의해야 합니다. 11 | 12 | 13 | ### CIImage 14 | 핵심 이미지 필터에서 처리하거나 생성할 이미지의 표현입니다.CIImage는 이미지를 나타내는 불변(immutable)객체이며 **bitmap 이미지 자체가 아닌 관련된 이미지 데이터만 있으며, 이미지를 생성하는데 필요한 모든 정보가 있습니다.** CIImage 객체는 이미지를 생성하는 데 필요한 모든 정보를 가지고 있지만 Core Image 실제로 그렇게 할 때까지(실제로 이미지를 생성할 때 까지) 이미지를 렌더링하지 않습니다. 15 | 16 | CIContext 및 CIImage 객체는 변경 불가능합니다. 즉, 각 객체를 스레드간에 안전하게 공유 할 수 있습니다. 여러 스레드가 동일한 GPU 또는 CPU CIContext 객체를 사용하여 CIImage 객체를 렌더링 할 수 있습니다. 17 | 18 | 단, CIFilter 객체는 스레드간에 안전하게 공유 할 수 없습니다. 앱이 다중 스레드 인 경우 각 스레드는 고유 한 CIFilter 객체를 만들어야합니다. 19 | 20 | 21 | ### CGImage 22 | 23 | 비트맵 이미지 또는 이미지 마스크, 즉, pixel array 를 가진 rectangle bitmap 이미지를 의미합니다. 24 | 25 | Quartz advanced drawing engine을 기반으로 하는 Core Graphics 프레임워크 내부에 존재합니다. 26 | 27 | **CGImage는 비트맵만 나타낼 수 있습니다.** blend modes 및 마스킹과 같은 CoreGraphics의 작업에는 CGImageRef가 필요합니다. 실제 비트맵 데이터에 액세스하고 변경해야 하는 경우 CGImage를 사용할 수 있습니다. 또한 NSBitmapImageRepes로 변환할 수 있습니다. 28 | -------------------------------------------------------------------------------- /frameworks/image_tracking.md: -------------------------------------------------------------------------------- 1 | # Image Tracking # 2 | 3 | ## ARKit1 Recap ## 4 | 5 | - 알고 있는 정적인 2D 이미지에 대한 인지 6 | - 위치와 방향을 알 수 있음. 7 | - world tracking에 통합됨. 8 | - Xcode asset catalog에 의해 제공됨. 9 | 10 | 11 | ## ARKit2에서의 변화 ## 12 | 13 | - 이미지는 더 이상 정적일 필요가 없습니다. 이미지를 움직여도 인지가 가능합니다. 14 | - 매 프레임(1초당 60 프레임)마다 위치와 방향을 가져옵니다. 15 | - 여러 이미지에 대하여 동시에 tracking할 수 있습니다. 16 | 17 | 18 | ## Image Tracking 로직 ## 19 | 20 | 21 | 22 | 1. File 혹은 Asset 카탈로그로부터 ARReferenceImage를 만듭니다. 23 | 2. 각 Configuration에 따라(ARWorldTrakcing - detectionImages, ARImageTracking - trackingImages) 설정을 해줍니다. 24 | 3. ARSession에 configuration을 넣어 실행합니다. 25 | 4. 현재의 프레임을 보면, ARFrame 내에 ARImageAnchor라는 객체(이미지가 발견되었다는)가 생성됩니다. 26 | 27 | ### ARImageAnchor ### 28 | 29 | world-tracking AR session에서 발견된 이미지의 위치와 방향에 대한 정보. 30 | 31 | 아래의 코드는 ARImageAnchor에 대한 소스 코드입니다. 32 | 33 | ```swift 34 | open class ARImageAnchor : ARAnchor, ARTrackable { 35 | public var isTracked: Bool { get } 36 | 37 | open var transform: simd_float4x4 { get } 38 | 39 | open var referenceImage: ARReferenceImage { get } 40 | } 41 | ``` 42 | 43 | - isTracked : tracking을 하는지 여부를 판단. 44 | - transform : 탐지된 이미지에 대한 위치, 방향 등에 대하여 알려주는 4 x 4 행렬. 45 | - referenceImage : 발견된 이미지(ARReferenceImage 객체) 46 | 47 | 48 | 49 | ### Image tracking시의 configuration(WorldTracking, ImageTracking) ### 50 | 51 | ARWorldTrackingConfiguration 52 | 53 | - Image anchors in world reference frame 54 | 55 | 이미지의 anchor는 현실 세계의 좌표계로 표현됩니다. 56 | 57 | 카메라와 현실 세계의 좌표상의 원점은 모두 나타나게 됩니다. 58 | 59 | - Detected images can be tracked(***iOS 12.0 Beta***) 60 | 61 | 이전에 탐지된 이미지들은 tracking 될 수 있습니다. 62 | 63 | 아래는 WorldTrackingConfiguration으로 실행하는 코드입니다. 64 | 65 | ```swift 66 | // Image Tracking 67 | 68 | // Create a world tracking configuration 69 | let configuration = ARWorldTrackingConfiguration() 70 | 71 | // Set of images to be detected 72 | configuration.detectionImages = [catPhoto, dogPhoto, birdPhoto] 73 | 74 | // Optionally specify the maximum number of images to track in parallel 75 | configuration.maximumNumberOfTrackedImages = 2 76 | 77 | // Run the session 78 | session.run(configuration) 79 | ``` 80 | 81 | 82 | 83 | ARImageTrackingConfiguration(***iOS 12.0 Beta***) 84 | 85 | - Independent from world tracking 86 | 87 | tracking을 수행하기 위한 motion sensor에 의존하지 않습니다. 88 | 89 | 이 의미는, 이 configuration은 이미지들을 확인하려 시작하기 전까지는 초기화되지 않고, 또한 이것은 **world tracking이 실패하는 상황에 대해서도 성공**할 수 있습니다.(예를 들어 움직이는 엘리베이터나 열차 내에서도 가능합니다.) 90 | 91 | - Position and orientation for every frame. 92 | 93 | 매 프레임(초당 60 프레임)마다 위치와 방향에 대해서 측정합니다. 94 | 95 | ```swift 96 | // Image Tracking 97 | 98 | // Create an image tracking configuration 99 | let configuration = ARImageTrackingConfiguration() 100 | 101 | // Set of images to be tracked 102 | configuration.trackingImages = [catPhoto, dogPhoto, birdPhoto] 103 | 104 | // Optionally specify the maximum number of images to track in parallel 105 | configuration.maximumNumberOfTrackedImages = 2 106 | 107 | // Run the session 108 | session.run(configuration) 109 | ``` 110 | 111 | 112 | 113 | #### maximumNumberOfTrackedImages #### 114 | 115 | 위의 예시 코드들에서 알 수 있듯이, configuration 설정부터 image들을 세팅하는 부분까지 차이는 거의 없지만, 차이가 있다면 116 | 117 | **maximumNumberOfTrackedImages**가 서로 다릅니다. 118 | 119 | **ARImageTrackingConfiguration** 120 | 121 | 정의 : 움직임을 동시에 추적할 수 있는 최대 이미지 갯수. 122 | 123 | default값이 1이며, 만약 설정한 최대 tracking하는 이미지보다 많다면, tracking이 사라지거나, 다른 이미지가 제거 될때까지, 기존에 tracking하던 이미지들을 계속 tracking 합니다. 124 | 125 | **ARWorldTrackingConfiguration** 126 | 127 | 정의 : 움직임을 동시에 추적할 수 있는 최대 발견된 이미지의 갯수. 128 | 129 | default값이 0이며, 0보다 클 경우에 image tracking이 가능해집니다. 130 | 131 | 발견된 이미지가 그것과 연관된 실제 객체가 현실 세계로 이동하여도 계속해서 tracking합니다. 만약 설정한 최대값보다 더 많이 보인다면, tracking이 사라지거나, 다른 이미지가 제거 될때까지, 기존에 tracking하던 이미지들을 계속 tracking 합니다. 132 | 133 | **적은 수의 이미지만 동시에 tracking**할 수 있습니다. 더 많은 이미지를 tracking 하려면 **ARImageTrackingConfiguration**를 사용해야 합니다. 134 | -------------------------------------------------------------------------------- /frameworks/object_detection.md: -------------------------------------------------------------------------------- 1 | # Object Detection # 2 | 3 | ## 특징 ## 4 | 5 | - Detection of a known static 3D object 6 | 7 | 이미 인지하고 있는 멈춰있는 상태의 3D 객체를 찾는 것입니다. (움직이는 3D 객체에는 사용할 수 없습니다.) 8 | 9 | - Objects need to be scanned first 10 | 11 | 해당 객체는 먼저 스캔이 되어야합니다. 12 | 13 | 14 | - Well-textured, rigid, non-reflective 15 | 16 | 인식이 잘되게 특징을 잡을 만한 좋은 질감?과 단단하고, 반사가 일어나지 않는 객체를 써야 좋습니다. 17 | 18 | 19 | - position and orientation 20 | 21 | 위치나 방향 등에 대해서도 측정이 됩니다. 22 | 23 | 24 | - Integrated into world tracking 25 | 26 | 이것들은 모두 world tracking에 통합되어 있기 때문에, property 하나만 설정해주어도, **object Detecion**을 시작할 수 있습니다. 27 | 28 | 아래의 코드는 object detection을 실행하는 코드입니다. 단 3줄이면 바로 실행 할 수 있습니다. 29 | 30 | ```swift 31 | // Object Detection 32 | 33 | // Create a world tracking configuration 34 | let configuration = ARWorldTrackingConfiguration() 35 | 36 | // Set of objects to be detected 37 | configuration.detectionObjects = [ancientBust, clayPot] 38 | 39 | // Run the session 40 | session.run(configuration) 41 | ``` 42 | 43 | 44 | ## Object Detection 로직 ## 45 | 46 | 47 | 48 | 49 | 1. File 혹은 Asset 카탈로그로부터 ARReferenceObject를 만듭니다. 50 | 2. ARWorldTrackingConfiguration의 detectedObjects를 설정해줍니다. 51 | 3. ARSession에 configuration을 넣어 실행합니다. 52 | 4. 현재의 프레임을 보면, ARFrame 내에 ARObjectAnchor라는 객체(Object가 발견되었다는)가 생성됩니다. 53 | 54 | 55 | ## ARObjectAnchor ## 56 | 57 | 58 | 59 | 위의 이미지는 세션 동영상에서 가져온 ARObjectAnchor에 대해 잘 나타내고 있는 이미지 입니다. 60 | 61 | 아래는 ARObjectAnchor에 대한 코드입니다. 62 | 63 | ```swift 64 | open class ARObjectAnchor : ARAnchor { 65 | 66 | open var transform: simd_float4x4 { get } 67 | 68 | open var referenceObject: ARReferenceObject { get } 69 | 70 | } 71 | ``` 72 | 73 | - transform : 탐지된 객체에 대한 위치, 방향 등에 대하여 알려주는 4 x 4 행렬. 74 | - referenceObject : 발견된 객체(ARReferenceObject) 75 | 76 | 77 | 78 | ## Object Scanning ## 79 | 80 | - Accumulated scene information 81 | 82 | Object Scanning은 현실 세계에서 누적된 scene 정보를 추출합니다. 83 | 84 | **Accumulated scene information**은 평면 측정에 매우 연관이 큽니다. 85 | 86 | 평면 측정은 이 누적된 scene 정보를 사용하여, 위치의 수평 혹은 수직을 측정하는 것입니다. 87 | 88 | 결과적으로 이렇게 얻은 정보들을 통하여 3D 객체에 대한 정보를 얻습니다. 89 | 90 | - Transform, extent, center 91 | 92 | 객체를 찾을 영역을 명확히 하기위해, transform, extend, center라는 것을 표시하였습니다. 93 | 94 | 95 | - Supported by Xcode asset catalog 96 | 97 | 추출된 객체들은 Xcode의 asset catalog에 완전히 적용 가능합니다. 98 | 99 | - ARObjectScanningConfiguration 100 | 101 | 스캔을 위해 따로 ARObjectScanningConfiguration라는 새로운 Configuration이 만들어졌습니다. 102 | 103 | 104 | ### ARObjectScanningConfiguration ### 105 | 106 | 요약 : 후면 카메라로 AR로 탐지를 시작해서 3D 물체를 스캔 할 때 사용하기 위해 높은 정확도의 공간 데이터를 수집하는 Configuration. 107 | 108 | AR에서 현실세계의 3D 객체를 탐지하기 위해, ARWorldTrackingConfiguration이 해당 객체에 대해 ARReferenceObject라는 고화질 3D 스캔이 필요합니다. ARObjectScanningConfiguration으로 세션을 실행하면 객체 검색에 필요한 높은 정확도의 데이터 수집이 가능합니다. 이 구성으로 세션의 객체를 스캔 한 후 **createReferenceObject**를 호출하여 reference 객체로 사용할 세션의 내부 공간 매핑 데이터를 추출합니다. 109 | 110 | 객체 스캔 기능을 제외하면, **ARObjectScanningConfiguration은 ARWorldTrackingConfiguration과 유사**합니다. 즉, 6ODF, hit test와 평면 탐지를 지원합니다. 고정확도의 객체 스캐닝을 지원하기 위해, object-scanning 세션은 world tracking 세션의 다른 기능들을 제외시킵니다. 111 | 112 | **경고** 113 | 114 | ``` 115 | ARObjectScanningConfiguration는 아직 개발 시나리오에서만 사용합니다. 116 | 고정확도 공간 매핑은 높은 성능과 에너지 사용을 하며, 117 | reference 객체 스캐닝에 필요하지 않은, ARKit을 비활성화합니다. 118 | 실제 사용자에서 AR을 보여주길 원한다면, ARWorldTrackingConfiguration를 사용해야합니다. 119 | ``` 120 | 121 | ### ARReferenceObject ### 122 | 123 | 요약 : world-tracking AR세션 동안에, 현실세계 환경에서 인식되는 3D 객체. 124 | 125 | 126 | ARkit에서의 객체 탐지는 AR 세션이 알고 있는 3D 객체를 인식하게 된다면, 사용자에게 AR컨텐츠를 일으키게 됩니다. 에를 들어, 앱이 미술관에서 조각을 감지하고, 가상의 큐레이터를 제공하는 것이 있습니다. 127 | 128 | 탐지를 위해 알려진 3D 객체를 제공하기 위해, ARKit를 사용하여, 현실세계 객체를 스캔합니다. 129 | ARObjectScanningConfiguration을 사용하는 AR 세션을 실행하여, 높은 정확도의 공간 매핑 데이터를 수집할 수 있습니다. 130 | 131 | 이 세션에서, 기기 카메라를 다양한 각도에서 현실세계 객체로 향하게 하면 ARKit이 객체와 그 주변의 내부의 지도를 만들 수 있습니다. 132 | 133 | 인식할 객체를 나타내는 세션의 세계 좌표 공간 부분을 결정하고, 객체 인식에 사용할 수 있도록 ARReferenceObject로 해당 부분을 가져 오려면 **createReferenceObject** 를 호출합니다. 134 | 135 | 나중에 다른 곳에서 사용할 reference 객체를 저장하려면, **export(to:previewImage)** 를 사용하여, .arobject파일을 만듭니다. 136 | 137 | AR 세션에서 객체들을 탐지하려면, 세션 객체의 **detectObjects** 속성에 reference 객체 콜렉션을 전달해야합니다. 138 | 139 | **refence 객체를 앱에 넣으려면, Xcode 프로젝트의 애셋 카탈로그를 사용해야합니다.** 140 | 141 | 애셋 카탈로그에서 추가 버튼을 사용하여, AR 리소스 그룹을 만듭니다. 142 | 143 | .arobject를 리소스 그룹으로 끌어 넣어서, 애셋 카탈로그에 AR reference 객체 항목을 만듭니다. 144 | -------------------------------------------------------------------------------- /frameworks/realtime.md: -------------------------------------------------------------------------------- 1 | # Using CADisplayLink & AVPlayerItemVideoOutput 2 | 3 | CADisplayLink는 applicationdl drawing을 디스플레이의 새로 고침 빈도와 동기화 할 수있게 해주는 타이머 객체입니다. CADisplayLink의 인스턴스를 초기화 시 타겟 객체와 셀렉터를 지정할 수 있고, 디스플레이 루프를 디스플레이와 동기화하기 위해 add (for : forMode : ) 메소드를 사용하여 실행 루프에 추가합니다. 4 | 5 | CADisplayLink와 실행 루프가 연결되면 화면의 내용을 업데이트해야 할 때 타겟의 셀렉터가 호출됩니다. 디스플레이 링크가 실행 루프와 연결되면 화면의 내용을 업데이트해야 할 때 대상의 셀렉터가 호출됩니다. 대상은 디스플레이 링크의 타임스탬프 속성을 읽어 이전 프레임이 표시된 시간을 검색할 수 있습니다. 6 | 7 | ```Swift 8 | displayLink = CADisplayLink(target: self, selector: #selector(displayLinkDidRefresh(link:))) 9 | 10 | displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.commonModes) 11 | ``` 12 | 13 | AVPlayerItemVideoOutput을 사용하면 코어 비디오 픽셀 버퍼와 관련된 내용의 출력을 조정할 수 있습니다. 14 | 15 | ```swift 16 | @objc func displayLinkDidRefresh(link: CADisplayLink) { 17 | let itemTime = videoOutput.itemTime(forHostTime: CACurrentMediaTime()) 18 | 19 | if videoOutput.hasNewPixelBuffer(forItemTime: itemTime) { 20 | if let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: itemTime, itemTimeForDisplay: nil) { 21 | 22 | // do something with pixelBuffer 23 | } 24 | } 25 | } 26 | ``` 27 | 28 | 기본 설정 FramesPerSecond를 설정하여 디스플레이 링크의 프레임률을 제어할 수 있습니다. 그러나 실제 초당 프레임 수는 사용자가 설정한 기본 값과 다를 수 있습니다. 실제 프레임률은 항상 장치의 최대 새로 고침 빈도를 나타내는 요소입니다. 29 | 30 | 예를 들어 장치의 최대 새로 고침 빈도가 초당 60 프레임(maximumFramesPerSecond로 정의됨)인 경우 실제 프레임률에는 초당 15, 20, 30 및 60 프레임이 포함됩니다. 디스플레이 링크의 기본 프레임률을 최대값보다 높은 값으로 설정하면 실제 프레임률이 최대값입니다. 31 | 32 | 최대 프레임률의 점수가 아닌 선호 프레임률은 가장 가까운 인수로 반올림됩니다. 예를 들어 초당 최대 새로 고침 빈도가 60인 장치에서 기본 프레임률을 초당 26 또는 35 프레임으로 설정하면 실제 프레임률이 초당 30회 생성됩니다. 33 | 34 | preferredFramesPerSecond 값이 0이면 기본 설정 프레임률이 maximumFramesPerSecond 속성으로 표시되는 디스플레이 최대 새로 고침 빈도와 동일합니다. 35 | 36 | 응용 프로그램이 디스플레이 링크와 함께 완료되면 invalidate()를 호출하여 모든 런 루프에서 제거하고 대상과 연결을 끊어야 합니다 37 | 38 | ```Swift 39 | displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode.commonModes) 40 | ``` 41 | -------------------------------------------------------------------------------- /frameworks/saveVideo.md: -------------------------------------------------------------------------------- 1 | # Save Video 2 | 3 | ## PHAssetChangeRequest 4 | 5 | 사진 라이브러리 변경 블록에 사용하기 위해 사진 asset의 내용을 작성, 삭제, 변경 또는 편집하기 위한 요청입니다. 6 | 7 | 1. 작성 8 | 9 | 3가지의 방법이 존재합니다. 10 | 11 | - 1. class func creationRequestForAsset(from: UIImage) -> Self 12 | 13 | - 2. class func creationRequestForAssetFromImage(atFileURL: URL) -> Self? 14 | 15 | - 3. class func creationRequestForAssetFromVideo(atFileURL: URL) -> Self? 16 | 17 | - var placeholderForCreatedAsset: PHObjectPlaceholder? 18 | 19 | 2. 삭제 20 | 21 | - deleteAssets(_ : ) 함수 호출하여 asset을 삭제합니다. 22 | 23 | 3. 변경 24 | 25 | - init(for : ) 함수 호출하여 asset content 및 메타데이터를 수정합니다. 26 | 27 | cf) isFavorite property 설정을 통해 favorite asset으로 마킹할 수 있습니다. 28 | 29 | ```swift 30 | let saveVideoToPhotos = { 31 | PHPhotoLibrary.shared().performChanges({ 32 | let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL) 33 | assetPlaceholder = assetRequest?.placeholderForCreatedAsset 34 | }, completionHandler: { (success, error) in 35 | 36 | // code 37 | 38 | }) 39 | } 40 | 41 | // Ensure permission to access Photo Library 42 | if PHPhotoLibrary.authorizationStatus() != .authorized { 43 | PHPhotoLibrary.requestAuthorization({ status in 44 | if status == .authorized { 45 | saveVideoToPhotos() 46 | } else { 47 | print(status.rawValue) 48 | } 49 | }) 50 | } else { 51 | saveVideoToPhotos() 52 | } 53 | 54 | ``` 55 | 56 | ### Create assets 57 | 58 | #### 1. class func creationRequestForAsset(from: UIImage) -> Self 59 | 60 | 사진 라이브러리에 새 이미지 asset 추가 요청을 만듭니다. 61 | 62 | #### 2. class func creationRequestForAssetFromImage(atFileURL: URL) -> Self? 63 | 64 | 지정된 URL의 이미지 파일을 사용하여 사진 라이브러리에 새 이미지 asset 추가 요청을 만듭니다. 65 | 66 | #### 3. class func creationRequestForAssetFromVideo(atFileURL: URL) -> Self? 67 | 68 | 지정된 URL의 비디오 파일을 사용하여 사진 라이브러리에 새 비디오 asset 추가 요청을 만듭니다. 69 | 70 | #### var placeholderForCreatedAsset: PHObjectPlaceholder? 71 | 72 | 변경 요청을 통해 생성하는 asset에 대한 placeholder 객체입니다. 동일한 변경 블록 내에서 변경 요청으로 생성된 asset을 참조해야 하는 경우 이 속성을 사용할 수 있습니다. 예를 들어 아래의 경우 처럼, asset을 생성 후 해당 asset url을 completion의 인자로 넘겨줄 수 있습니다. 73 | 74 | ```swift 75 | 76 | PHPhotoLibrary.shared().performChanges({ 77 | let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL) 78 | let assetPlaceholder = assetRequest?.placeholderForCreatedAsset 79 | }, completionHandler: { (success, error) in 80 | if success { 81 | let localID = assetPlaceholder.localIdentifier 82 | let assetID = localID.replacingOccurrences(of: "/.*", with: "", options: NSString.CompareOptions.regularExpression, range: nil) 83 | let ext = "mp4" 84 | let assetURLStr = "assets-library://asset/asset.\(ext)?id=\(assetID)&ext=\(ext)" 85 | 86 | if let url = URL(string: assetURLStr) { 87 | print(url) 88 | completion(url) 89 | } else { 90 | print("something wrong") 91 | } 92 | 93 | } else { 94 | print(error.debugDescription) 95 | } 96 | }) 97 | ``` 98 | -------------------------------------------------------------------------------- /frameworks/save_load_maps.md: -------------------------------------------------------------------------------- 1 | # Saving and Loading Maps # 2 | 3 | 4 | ## ARWorldMap ## 5 | 6 | 간단히 말하자면, world-tracking AR 세션으로부터의 anchor들과 공간이 매핑되어있는 상태를 의미합니다. 7 | 8 | 9 | world map의 세션 상태에는 유저가 ARKit의 기기를 움직힌 물리적 공간에 대한 인식 뿐만 아니라, 세션에 더해진 ARAnchor 객체들 또한 포함합니다. 10 | 11 | **getCurrentWorldMap(completionHandler:)** 를 호출하여, 세션의 worldmap을 저장한 후, configuration의 initialWorldMap property에 저장한 worldmap을 할당할 수 있으며, **run(_:options)** 을 호출하여, 다른 세션에서 동일한 것들에 대하여 볼 수 있습니다. 12 | 13 | 아래는 샘플 코드입니다. 14 | 15 | ```swift 16 | // Saving and Loading World Maps 17 | // Retrieve world map from session object 18 | session.getCurrentWorldMap { worldMap, error in 19 | guard let worldMap = worldMap else { 20 | showAlert(error) 21 | return 22 | } 23 | } 24 | 25 | // Load world map and run the configuration 26 | let configuration = ARWorldTrackingConfiguration() 27 | 28 | configuration.initialWorldMap = worldMap 29 | 30 | session.run(configuration) 31 | 32 | ``` 33 | 34 | 이렇게 worldmap들을 저장하고, 그것들을 이용하여 새로운 세션을 시작함으로써, 앱은 새로운 AR적 능력들을 사용할 수 있습니다. 35 | 36 | ### *Multiuser AR experiences* ### 37 | 38 | 아카이브된 ARWorldMap 객체들을 근처 유저의 기기에 보냄으로써, 공유된 reference 프레임을 만들 수 있습니다. 두 기기가 동일한 worldmap을 추적하면, 두 유저들은 동일한 가상 content를 보고 상호 작용할 수 있는 네트워크 환경을 구축 할 수 있습니다. 39 | 40 | ### *Persistent AR experiences* ### 41 | 42 | 앱이 비활성화 상태일 때 world map을 저장하고, 앱이 다시 동일한 물리적 환경에서 시작하였을 때 복구합니다. 다시 실행된 world map의 anchor들을 이용하여, 동일한 가상의 content를 저장된 세션의 동일한 위치에 놓게 이용할 수 있습니다. 43 | 44 | 45 | ## Archiving World Map Data for Persistence or Sharing ## 46 | 47 | 요약 : 네트워크 전송 혹은 파일 저장을 위한 world map 객체들의 **Serialize**와 **deserialize**. 48 | 49 | ARWorld 클래스는 **NSSecureCoding** 프로토콜을 따르기때문에, **NSKeyedArchiver**와 **NSKeyedUnarchiver** 클래스들을 이용하여, world map을 바이너리 데이터 형태로 변환/ 바이너리 데이터 형태로부터 변환 을 할 수 있습니다. 50 | 51 | ARWorldMap을 file URL에 저장하는 방법 52 | 53 | ```swift 54 | func writeWorldMap(_ worldMap: ARWorldMap, to url: URL) throws { 55 | let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true) 56 | try data.write(to: url) 57 | } 58 | 59 | ``` 60 | 61 | file URL로부터 ARWorldMap을 로드하는 방법 62 | 63 | ```swift 64 | func loadWorldMap(from url: URL) throws -> ARWorldMap { 65 | let mapData = try Data(contentsOf: url) 66 | guard let worldMap = try NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: mapData) 67 | else { throw ARError(.invalidWorldMap) } 68 | return worldMap 69 | } 70 | ``` 71 | 72 | 73 | ARWorldMap을 다른 기기로 보내는 방법 : 다중 사용자 AR에 대한 reference 공유 프레임을 만듭니다. 74 | 75 | 1. 하나의 기기에서 **NSKeyedArchiver**를 이용하여, world map을 데이터 객체로 변환합니다.(네트워크를 통한 전송을 위해 만든 데이터를 파일에 쓸 필요는 없습니다.) 76 | 2. 네트워킹 기술 중 어느 것이든 선택하여, 다른 기기에 보냅니다. (문서에서는 **MultipeerConnectiviy** session을 이용하여, 전송하였습니다.) 77 | 3. 받는 기기에서는 **NSKeyedUnarchiver**를 이용하여, 데이터로부터 ARWorldMap을 만듭니다. 78 | 79 | 80 | ## worldMappingStatus ## 81 | 82 | 해당 프레임에서 world map을 생성 혹은 relocalizing의 실현 가능성 나타내는 property. 83 | 84 | world-tracking 세션은 사용자의 환경에서의 기기의 위치를 결정하는 world map을 내부적으로 빌드합니다. 85 | 86 | 세션 동안에 **getCurrentWorldMapWithCompletionHandler:** 를 언제든 호출할 수 있는 데, worldmap을 가져오는게 항상 일정하게 좋게 가져오지 않는다. 이 property를 사용하여, 세션이 ARWolrdMap을 생성하기에 충분한 데이터를 가졌는지 아닌지를 판단하여, 결정을 하면 좋을 것 같습니다. 87 | 88 | 89 | 90 | 91 | | MappingStatus | Description | 92 | |:--------|:--------| 93 | |**ARWorldMappingStatusNotAvailable** | world map이 이용 가능하지 않습니다. | 94 | |**ARWorldMappingStatusLimited** | world tracking이 현재 기기 위치 주변을 아직 충분히 매핑하지 않았습니다.| 95 | |**ARWorldMappingStatusExtending** | world tracking이 최근 방문한 지역들을 매핑하였지만, 현재 기기 위치 주변을 아직 매핑 중인 상태입니다.| 96 | |**ARWorldMappingStatusMapped** | world tracking이 보이는 장소에 대하여 충분히 매핑하였습니다.| 97 | -------------------------------------------------------------------------------- /frameworks/siri_shortcut.md: -------------------------------------------------------------------------------- 1 | 2 | # Siri Shortcut 3 | 4 | ## About 5 | 6 | 2018년 WWDC에서 애플은 개발자들이 자신의 앱 내에서 시리의 기능을 확장하고 확장할 수 있게 해주는 Siri Shortcut을 발표했습니다. 이전에는 SiriKit SDK의 전반적인 기능이 상당히 제한적이었습니다. Siri Shortcuts으로 개발자들은 Siri의 기능을 확장하고 앱을 호출하는 사용자 지정 음성 동작을 만들 수 있습니다. 즉, 모든 앱의 빠른 행동(Quick Action)을 미리 등록해서 사용할 수 있게 됩니다. 일종의 ‘바로가기’인 ‘숏컷’은 **사용자 맞춤식 음성 명령**으로, 시리에게 하나의 단어만으로 **특정 명령**을 수행토록 하는 간편한 기능입니다. 7 | 8 | 기존에는 시리에게 명령할 수 있는 '오늘 날씨 어때?' 또는 '러시아 월드컵 뉴스'를 물어보면 애플 시리와 제휴된 검색을 통해서만 제한된 정보가 제공되었습니다. 시리 숏컷은 사용자 맞춤으로 명령어 단축기를 설정할 수 있는 기능입니다. WWDC에서는 블루투스 칩을 원하는 소지품에 두면 위치를 찾아주는 '타일'앱을 예시로 들었습니다. 시리에게 '내 자동차 키좀 찾아줘'라고 하면 먹통이지만, 시리 숏컷으로 단축기 설정을 통해 키를 찾는 명령어를 타일 앱을 사용하도록 설정 하면 시리가 키가 있는 위치를 알려줍니다. 9 | 10 | 숏컷은 특정 장소에서 특정 행동을 빠르게 할 수 있도록 도와주는 기존의 워크플로우 기능과 유사합니다. 예를 들어 빠르게 녹음 모드로 들어갈 때, 인터넷 서핑을 하다 포켓에 저장할 때, 지정 전화번호로 빠르게 전화를 걸 때 등의 순간에 사용할 수 있습니다. 워크플로우와의 차이라면, 터치가 아닌 음성 인터페이스나는 것과, 동시에 여러 앱을 실행하는 것이 가능하다는 것 입니다. 11 | 12 |
13 |
애플 숏컷 라이브러리
15 | 16 | 17 | 18 | 시리는 특정 앱에 대한 숏컷을 제시하기도 하고, 사용자가 원하는 숏컷을 제작할 수도 있습니다. 사용자가 원하는 숏컷 제작의 예로 시리에게 '5분만 더 잘게'의 명령어를 등록하는 것을 들 수 있습니다. 19 | 20 | #### Example 21 | 22 | | 특정 행동 숏컷 등록 | 숏컷 명령어 등록 | 23 | |:--------|:--------| 24 | || face.gif | 25 | 26 | | 시리에게 해당 명령어 명령 | 특정 행동 수행 | 27 | |:--------|:--------| 28 | || face.gif | 29 | -------------------------------------------------------------------------------- /frameworks/vc.md: -------------------------------------------------------------------------------- 1 | # ViewController 2 | 3 | 4 | -------------------------------------------------------------------------------- /frameworks/video.md: -------------------------------------------------------------------------------- 1 | 2 | # Video 3 | 4 | ## AVAsset 5 | AVAsset은 **비디오 및 사운드와 같은 시각화 된 오디오 비주얼 미디어를 모델링하는데 사용되는 추상 불변 클래스**입니다. AVAsset은 Asset을 구성하는 트랙의 속성을 정의합니다. Asset에는 오디오, 비디오, 텍스트, 자막 등 단일 미디어 유형의 각 트랙을 함께 표시하거나 처리 할 수있는 하나 이상의 트랙이 포함될 수 있습니다. 6 | 7 | ## AVPlayer 8 | AVPlayer는 **미디어 Asset의 재생 및 타이밍을 관리하는 데 사용되는 컨트롤러 개체**입니다. 즉, AVPlayer는 미디어의 타임 라인 내에서 **재생, 일시 정지, 재생 속도 변경, 다양한 시점 탐색과 같은 플레이어의 전송 동작을 제어하는 ​​인터페이스를 제공**합니다. AVPlayer를 사용하여 QuickTime 동영상 및 MP3 오디오 파일과 같은 로컬 및 원격 파일 기반 미디어뿐만 아니라 HTTP 라이브 스트리밍을 사용하여 시청각 미디어를 재생할 수 있습니다. 9 | 10 | ## AVPlayerLayer 11 | AVPlayerLayer는 **AVPlayer 객체가 시각적 출력을 지시 할 수있는 CALayer의 하위 클래스**입니다. AVPlayerView 및 AVPlayerViewController와 달리, **재생 컨트롤을 제공하지 않고 단순히 화면에 시각적 내용을 표시**합니다(커스텀에 적합). UIView의 백업 레이어로 사용하거나 수동으로 레이어 계층 구조에 추가하여 비디오 내용을 화면에 표시 할 수 있습니다. 12 | 13 | ## CMTime 14 | AVFoundation에서는 미디어의 시간을 다룰 때 CMTime을 사용합니다. CMTime은 **분자 (int64_t value)와 분모 (int32_t timescale)가 있는 유리수로 표시**됩니다. 따라서 timescale이 4 인 경우 각 단위는 1/4 초를 나타냅니다. timescale이 10 일 경우 각 단위는 1/10 초를 나타냅니다. 15 | -------------------------------------------------------------------------------- /frameworks/view.md: -------------------------------------------------------------------------------- 1 | # View 2 | 3 | iOS에서 화면 UI 구성을 담당하는 핵심적인 요소 중 하나가 바로 View 입니다. View 는 UIKit 과 다른 프레임워크들을 통해 제공됩니다. View 라는 것은 하나의 공간입니다. 이 공간에는 여러가지 요소들이 담길 수 있고, 여러 components를 보여주는 용도로도 사용됩니다. 4 | 5 | ## View Architecture Fundamental 6 | 7 | iOS에서 모든 앱의 밑바탕에는 UIWindow 가 있습니다. 8 | 9 | > UIWindow - An object that provides the backdrop for your app’s user interface and provides important event-handling behaviors. 10 | 11 | UIWindow 는 그 자체로 보여주는 것은 없고, Container View로서만 작동합니다. Xcode로 iOS 프로젝트를 생성하면 UIWindow 가 생성됩니다. 이 UIWindow 는 사실 거의 건드릴 일이 없습니다만, Status Bar에 layer를 씌우거나 하는 작업을 위해서는 접근이 필요합니다. 그 때는 AppDelegate.swift에서 var window: UIWindow?와 같은 변수를 통해 접근할 수 있습니다. 12 | 13 | 14 | ### View Hierarchies and Subview Management 15 | 16 | View 는 앞서 언급한 것처럼, 다른 View 혹은 components들을 담을 수 있습니다. 이 때 하나의 View 에 다른 View 를 담게 되면 둘 사이는 부모-자식 관계 가 성립하고, 이 때 부모를 SuperView 라 하고, 자식을 SubView 라고 합니다. 스토리보드를 통해 예를 들어 보겠습니다. 스토리보드에서 하나의 View Controller를 생성하면 기본적으로 Top Layout Guide, Bottom Layout Guide, View라는 3 가지 인스턴스가 생성됩니다. 이 3 가지 인스턴스는 모두 UIWindow 의 SubClass이고, 이 때 UIWindow 와 View 는 특별히 SuperView 와 SubView 의 관계에 있다고 할 수 있습니다. 아래 그림에서 앞서 언급한 부모-자식 관계를 확인할 수 있습니다. 17 | 18 | 19 | 20 | SuperView 는 SubView 들을 배열의 형태로 저장합니다. 그래서 이 때 SubView 들이 겹치게 될 경우, 먼저 추가한 것이 아래 쪽에 위치합니다. 또한, SuperView 의 크기를 변화시키면 SubView 의 크기에도 영향을 미치므로, 오토레이아웃 설정시 SuperView 를 수정할 경우 SubView 도 수정해주어야 하는 것이 일반적입니다. 21 | 22 | 그리고 모든 View 에는 그에 상응하는 layer 가 존재합니다. layer는 렌더링, 애니메이션 등의 보이는 부분에 초점을 맞춥니다. layer의 기본 작업은 사용자가 제공하는 시각적 콘텐츠를 관리하는 것이지만 배경색, 테두리 및 그림자 같은 시각적 속성 역시 설정되어 있습니다. UIView는 이러한 Layer를 포함하는 Container이며 UIResponder에서 상속되어 사용자의 이벤트 등 역시 처리합니다. view 의 속성과 layer 속성을 적절히 섞어서 view 의 모양이나 애니메이션에 적절한 효과를 줄 수 있습니다. 23 | 24 | 25 | ### Reference: 26 | - https://hcn1519.github.io/articles/2017-06/iOS_uiview 27 | -------------------------------------------------------------------------------- /frameworks/view_vs_layer.md: -------------------------------------------------------------------------------- 1 | # View vs layer 2 | 3 | 모든 화면은 뷰(View) 단위로 구성됩니다. 최상위 윈도우가 있더라도 모든 화면은 뷰가 쌓인 구조로 표시됩니다. 4 | 5 | 하나의 뷰는 뷰 그리기 루틴(drawRect), 서브뷰(Subviews)들과 함께 레이어(Layer)라는 부품들로 구성됩니다. 레이어는 뷰에서 눈에 보여지는 부분을 다루는 중요한 기능입니다. 6 | 7 | 뷰에는 하나의 레이어만 있을 수 있습니다. 대신 레이어는 서브레이어(Sublayers)를 여럿 가질 수 있습니다. 그리고 이름답게 다층(multi layer) 구조로 구성 할 수 있습니다. 8 | 9 | 뷰를 여러개 쌓아서 만드는 것에 비해, 같은 모양을 레이어를 쌓아서 만드는게 훨신 가볍기 때문에 애니메이션 등에 유리합니다. 10 | 11 | 간단히 말해서, CALayer는 NSObject에서 상속되며, **렌더링, 애니메이션 등의 보이는 부분에 초점을 맞춥니다.** layer의 기본 작업은 사용자가 제공하는 시각적 콘텐츠를 관리하는 것이지만 배경색, 테두리 및 그림자 같은 시각적 속성 역시 설정되어 있습니다. 시각적 콘텐츠 관리 외에도, 계층은 해당 콘텐츠를 화면에 표시하는 데 사용되는 콘텐츠의 형태(위치, 크기 및 변환 등)에 대한 정보도 유지합니다. 12 | 13 | 14 | UIView는 **이러한 Layer를 포함하는 Container이며 UIResponder에서 상속되어 사용자의 이벤트 등 역시 처리합니다.** 15 | 16 | 정리하여 나타내자면 다음과 같습니다. 17 | 18 | **UIView** : Container 19 | 20 | 뷰가 어떻게 표현되어야 할지 Layer에 delegate 및 User Interaction 핸들 21 | 22 | - layer(CALayer)를 속성으로 가지고 있고 이것을 관리할 의무가 있음 23 | 24 | - 추가적으로 User interatcion 을 처리함 25 | 26 | **CALayer** : 보여지는 것 담당 27 | 28 | 애플 프레임웍에서 visual content를 어떻게 구성할것인가를 전적으로 담당(drawing, layout, animation) 29 | 30 | - CALayer 만 할수 있는것 (UIView 가 못하는것) 31 | 32 | - Drop shadows, rounded corner, colored border 33 | 34 | - 3D transforms and positioning 35 | 36 | - Nonrectangular bounds 37 | 38 | - Alpha masking of content 39 | 40 | - Multistep, nonlinear animations 41 | -------------------------------------------------------------------------------- /maintenance/api_design_guideline.md: -------------------------------------------------------------------------------- 1 | # Swift API Design Guidelines 2 | 3 | Swift를 좀 더 Swifty 하게 코딩할 수 있도록 돕는 가이드라인 4 | 5 | - 공식문서: https://swift.org/documentation/api-design-guidelines/ 6 | - 번역본: https://minsone.github.io/swift-internals/api-design-guidelines/ 7 | -------------------------------------------------------------------------------- /maintenance/crud.md: -------------------------------------------------------------------------------- 1 | # 인터페이스 설계: CRUD 2 | 3 | 누구나 실수를 합니다. 특히 범위가 넓고 요구사항이 많은 제품을 만들때는 열심히 확인해도 실수를 할 수 밖에 없습니다. 인터페이스 설계자가 사용자 인터페이스를 구성하면서 하는 실수 중 많은 부분을 차지하는 것이 서비스 플로우 상에서 중복 또는 누락이 발생하는 것입니다. 4 | 5 | 예를 들어서, 가계부앱을 만든다고 하면 내가 쓴 금액을 입력하는 화면도 있고 수정할 수 있는 화면도 있지만, 삭제 버튼이 없다면 사용자는 있지도 않은 삭제 버튼을 찾기 위해 머나먼 여정을 떠나거나 슬프게도 그 가계부 앱을 떠날 것입니다. 6 | 7 | 때문에 서비스 설계자는 개발자가 개발에만 집중할 수 있도록 가능한 한번에 서비스 플로우 중복과 누락없이 매끈하게 설계해 개발일정을 추가하는 등의 불필요한 낭비를 줄여야합니다. 8 | 9 | 서비스 플로우를 설계 할 때 역시 많은 사람이 중복과 누락 문제를 겪었고 해결방법 또한 존재합니다. 그 중 데이터모델링에서 중복과 누락을 방지하기 위해 정리된 CRUD가 있습니다. CRUD는 인터페이스 설계를 위해 정리된 것은 아니지만 인터페이스 설계시 가볍고 빠르게 사용할 수 있습니다. 10 | 11 | ## Create, Read, Update, Delete 12 | 13 | CRUD란, Create, Read, Update, Delete의 제일 앞 문자를 하나씩 따와서 만든 줄임말로, 데이터를 처리하는 시스템이 지속성을 갖기위해 갖춰야 하는 기본적인 데이터 처리 4가지 기능입니다. 즉 시스템에서 데이터를 Create, Read, Update, Delete를 할 수 없다면 정상적으로 작동하는 시스템이 아니라는 뜻입니다. 14 | 15 | 데이터베이스 에서 나온 용어지만 사용자 인터페이스를 설계할 때도 훌륭하게 사용될 수 있습니다. 사용자 인터페이스 자체가 시스템에서 왔다갔다하는 데이터를 사용자가 직접 눈으로 확인할 수 있도록 바꾼 것이기 때문에 시스템 개념과 인터페이스 개념의 구성원리는 다르지 않습니다. 16 | 17 | 이처럼 CRUD는 설계한 인터페이스가 운영될 수 있는 최소한의 기능을 챙기고 놓치지 않게 도와줍니다. 하지만 CRUD를 채웠다고 완벽한 제품, 서비스가 되는게 아니라 말 그대로 ‘최소한의 기능’을 채운 것 뿐입니다. CRUD를 기본으로 하되 추가할 기능은 추가하고 CRUD가 어울리지 않는 상황이라면 과감히 다른 방법을 사용해야 합니다. 18 | 19 | ### Reference: 20 | 21 | - https://medium.com/@kimbellin/crud로-빈틈없고-매끈하게-인터페이스-설계하기-97efd89f82fb 22 | -------------------------------------------------------------------------------- /maintenance/lint.md: -------------------------------------------------------------------------------- 1 | # Swift Lint Library 2 | 3 | 프로젝트가 커지다보면 협업하는 개발자가 많아지고, 각자 다른 코드 컨벤션 때문에 소스코드를 해석하는데 어려움이 생깁니다. 4 | 5 | ex) 중괄호 위치 6 | 7 | A 개발자 8 | ```swift 9 | class A { 10 | ... 11 | } 12 | ``` 13 | B 개발자 14 | ```swift 15 | class B 16 | { 17 | ... 18 | } 19 | ``` 20 | 21 | 이러한 스타일을 공통적으로 적용하기 위해 Swift Convention을 관리해주는 오픈소스로 SwiftLint가 존재합니다. SwiftLint는 Realm에서 개발한 스위프트 스타일과 컨벤션을 강제로 설정하는 툴입니다. 이를 통해 코드 스타일 및 컨벤션을 강제화해서 가독성이 좋고 일관된 코드를 사용할 수 있습니다. 22 | 23 | - github: https://github.com/realm/SwiftLint 24 | - 참고사이트: https://swifting.io/blog/2016/03/29/11-swiftlint/ 25 | -------------------------------------------------------------------------------- /maintenance/principle.md: -------------------------------------------------------------------------------- 1 | # 코드 작성 원칙 2 | 3 | 4 | ## 지역적변화 5 | 6 | 코드를 수정할 때 함께 수정되어야 하는 부분을 최소화 시켜서 지역적인 변화만 일으키게 하는 것이 중요하다고 합니다. 그렇게 되면 전체 구조를 정확히 이해하지 못해도 수정해야 할 부분이 적어서 해당 코드만 명확하게 수정할 수 있습니다. 코드의 관리 비용을 낮추어 주며 여러 패턴들의 근간이 되는 원칙입니다. 7 | 8 | ## 최소중복 9 | 10 | 중복된 코드는 대부분 코드에 대한 이해 부족이나 귀차니즘때문에 발생하고는 합니다. 흔히 말하는 “복붙”이 중복 코드를 만들어 내는 주범입니다. 그 외에도 여러 형태의 중복이 발생할 수 있는데 중복된 코드는 통상 한군데서 수정이 발생하면 함께 수정되어야 하는 경우가 많습니다. 그렇기 때문에 중복된 코드가 많으면 코드 관리비용을 높이는 결과를 가져옵니다. 11 | 12 | ## 로직과 데이터의 결합 13 | 14 | 로직과 데이터를 한곳에 유지하는 것이 유리합니다. 로직은 데이터를 기본으로 동작하기 때문에 이것이 분리되어있으면 데이터 수정 후 다시 로직을 수정하기위해 코드를 뒤져야 하므로 관리비용을 높이게 됩니다. 15 | 16 | ## 대칭성 17 | 18 | 코드는 대칭을 이루는 것이 좋습니다. 예로 add가 있으면 remove가 있는 것이 좋습니다. 객체의 생성과 소멸 역시 대칭성을 지켜야 합니다. 코드가 대칭성을 이루면 일단 이해하기 편해지며 중복을 제거하는데 유리합니다. 19 | 20 | ## 선언적표현 21 | 22 | 명령형 프로그래밍은 코드를 따라가면서 읽어야 한다는 단점이 있습니다. 하지만 선언적 표현을 적절히 사용하므로써 이해하기 쉬운 코드를 작성할 수 있습니다. 모든 코드를 선언적 표현으로 하기는 힘들지만 선언적표현은 분명 코드를 읽기 쉽게 해줍니다. Objective-C나 Swift가 매우 강력한 선언적표현을 지원한다고 볼 수는 없지만 기존의 기능만으로도 충분히 더 나은 가독성을 제공할 수 있습니다. 23 | 24 | 예를 들어 어떤 view들의 특정 메소드를 실행해야 한다고 할 때 그 view들을 가지는 array같은 것을 앞에서 선언해주므로서 해당 동작이 어떤 view들에게 해당되는지 한눈에 알아볼 수 있게 해줄 수 있습니다. 25 | 26 | 또는 dictionary에 key, value로 특정 메소드들을 넣어서 if나 switch를 사용하지 않고 메소드를 실행하는것 같은 스타일의 코딩 역시 특정 상태와 실행할 함수를 “선언적”으로 표현하므로써 조금 더 읽기 쉽게 해줍니다. 27 | 28 | ```swift 29 | private lazy var keyCodeHandlers: [KeyCode: () -> Void] = { 30 | return [.up: self.upArrowDown, 31 | .right: self.rightArrowDown, 32 | .left: self.leftArrowDown, 33 | .down: self.downArrowDown] 34 | } () 35 | ``` 36 | ## 변화율 37 | 38 | 변화율은 시간적 대칭성을 말합니다. 만일 어떤 객체의 필드가 변경되는데 최선의 방법은 한꺼번에 모두 변경 되는 것이지만 경우에 따라 그렇지 못한 경우도 있을 것입니다. 이런 경우 함께 변화되어야 할 부분끼리 분리를 해주므로써 개발자의 의도를 잘 전달할 수 있습니다. 39 | -------------------------------------------------------------------------------- /maintenance/spa_code.md: -------------------------------------------------------------------------------- 1 | # 스파게티 코드 2 | 3 | 대부분의 개발자들은 처음 개발을 시작할 때 들어본 말 가운데 스파게티 코드라는 것이 있을 것입니다. 복잡한 스파게티 코드를 읽고 정리하는 것은 스파게티 면발을 하나씩 정리하는 것 보다 훨씬 더 어려운 일이라는 것을 공감할 것입니다. 이미 스파게티가 되어버리면 유지보수가 어려워지고 그에 따라 많은 버그를 만들어 내는 것은 누구나 다 공감하는 사실입니다. 4 | 5 | ## 원인과 유형 6 | 7 | 유사한 말로 라자냐 코드 라는 말도 있는데 이는 OOP에서 지나치게 많은 상속 구조를 가지고 있어 복잡도가 증가했을 때를 비꼬는 말입니다. 스파게티 코드란 당장 내가 봤을 때 그 흐름을 따라가기 힘든 코드라고 정의해버릴 수도 있을 것 같습니다. 8 | 하지만 원인을 알아야 그것에 제대로 대처할 수 있듯이 스파게티 코드가 무엇인지 알아야 그리 하지 않을 수 있을 것입니다. 9 | 10 | ### 복잡한 제어문 11 | 12 | cyclomatic complexity를 이야기 하는 이유가 여기에 있다고 생각합니다. 코드의 branch가 많아지면 읽어 나가기 매우 곤란하게 됩니다. 함수 안에서 if문이 단 하나도 존재하지 않는다면 그 코드를 읽는 머리는 매우 편안할 것입니다. 통상 복잡한 제어문은 단지 제어문 만으로 이루어지지는 않습니다. 13 | 14 | ### 많은 변수 15 | 16 | 변수는 두가지 스타일로 사용되는것 같습니다. 하나는 가지는 값이 제어에 영향을 주는 경우이고 다른 하나는 단지 값을 저장하는 용도일 것입니다. 스파게티 코드에 영향을 주는 경우는 주로 첫번째 경우인 값이 제어에 영향을 주는 경우일 것입니다. 값이 제어에 영향을 준다는 것은 그 변수를 이용해서 if, switch, for등을 사용하는 경우 일 것입니다. 이 경우 그 변수의 값에 따라서 제어가 바뀌게 되는데 코드를 읽을 때 값을 계속 기억하고 있어야 하는 문제가 있습니다. 대부분의 경우 제어에 이용되는 변수가 많으면 만을 수록 코드의 흐름을 따라가기 힘듭니다. 17 | 18 | ### 잘못된 상속 19 | 20 | OOP에서 두 코드간의 커플링이 가장 심한 경우는 상속이 이루어진 경우입니다. 복잡한 상속관계를 가지는 클래스의 코드는 읽을 때 현재 코드와 슈퍼클래스의 코드를 오가면서 읽어야 합니다. 오버라이드 된 함수, 혹은 그렇지 않은 함수를 분리해서 생각하면서 읽어야 하고 어떤 함수는 직접 호출하기도 하지만 어떤 함수는 호출되기도 합니다. 상속이 가지는 기본적인 속성 자체만으로도 충분히 코드 읽기가 힘들어지는데, 여기에 잘못된 상속이 들어와버리면 정말 어려운 코드가 됩니다. 21 | 22 | 잘못된 상속의 경우는 매우 다양하게 발생하지만 예를 들어 feature envy에 기반한 경우를 이야기해보면 다음과 같은 상황이 있습니다. feature envy라 함은, 클래스 본연의 임무 이외에 기능이 불필요하게 해당 클래스에 들어간 경우인데 이것이 상속과 만나면서 복잡한 상황을 연출합니다. 자신의 class와 아무런 상관없는 동작을 하는데 super class의 도움을 받지 않고는 불가능한 경우가 발생합니다. 23 | 24 | ### 추상화 단계 부재 25 | 26 | 코드는 현 위치에서 동일한 추상화 단계를 사용해야 읽기가 쉽습니다. 올바른 추상화를 통해 코드의 결합도를 낮추는 동시에 응집도를 높일 수 있습니다. 27 | -------------------------------------------------------------------------------- /networking/message_stream.md: -------------------------------------------------------------------------------- 1 | # Communication 2 | 3 | ## Message vs Stream - oriented 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /networking/overview.md: -------------------------------------------------------------------------------- 1 | # Networking Overview 2 | 3 | 네트워킹의 세계는 복잡합니다. 사용자는 케이블 모뎀, DSL, Wi-Fi, 셀룰러 연결, 위성 업링크, 이더넷 및 심지어 전통적인 음향 모뎀과 같은 광범위한 기술을 사용하여 인터넷에 연결할 수 있습니다. 이러한 연결은 각각 대역폭, 지연 시간, 패킷 손실 및 신뢰성의 차이를 포함하여 고유한 특성을 가집니다. 4 | 5 | 6 | 7 | 네트워크는 본질적으로 신뢰할 수 없습니다. (셀룰러 네트워크는 두 배로 신뢰할 수 없습니다.) 결과적으로, 좋은 네트워킹 코드는 다소 복잡한 경향이 있습니다. 때문에 어플리케이션은 다음과 같은 사항을 따라야 합니다. 8 | 9 | - 작업을 수행하는 데 필요한 만큼의 데이터만 전송합니다. 10 | 11 | - 가능한 timeout을 피해야 합니다. 12 | 13 | - 사용자가 완료에 너무 오래 걸리는 트랜잭션을 쉽게 취소할 수 있도록 사용자 인터페이스를 설계합니다. 14 | 15 | - 네트워크 성능이 저하되면 우아하게(?) degrade 하십시오. (유튜브 비디오의 자동 품질 조정 등을 생각하면 될 듯합니다.) 16 | 17 | - 소프트웨어를 신중하게 설계하여 보안 위험을 최소화하십시오. 18 | 19 | 장치의 네트워크 환경은 즉시 변경될 수 있습니다. 프로그램 메인 스레드에서 동기식 네트워킹 코드를 실행하거나 네트워크 변경을 적절하게 처리하지 못하는 등 앱의 성능과 사용성에 악영향을 미칠 수 있는 간단한(이러한 파괴적인) 네트워킹 오류가 많습니다. 나중에 디버깅하는 대신 이러한 문제가 발생하지 않도록 프로그램을 설계하면 많은 시간과 노력을 절약할 수 있습니다. **네트워킹은 반드시 Dynamic하고 Asynchronous해야 합니다.** 20 | 21 | 22 | ## Network Levels 23 | 24 | 일반적으로 네트워크가 이루어지는 계층을 다음과 같이 표현합니다. (7계층 or 4계층) 25 | 26 | 27 | 28 | 1. Application Layer: 사용자와 네트워크 간의 연결, 데이터를 생성합니다. (HTTP, FTP 등) 29 | 30 | 2. Presentation Layer: 데이터 형식을 규정합니다. (JPEG, MPEG 등) 31 | 32 | 3. Session Layer: 인증 및 서비스를 제공합니다. (SSH, TLS 등) 33 | 34 | 4. Transport Layer: 프로세스 간의 데이터를 전송합니다. (TCP, UDP 등) 35 | 36 | 5. Network Layer: 스위칭, 라우팅 등 데이터 경로를 설정합니다. (IP 등) 37 | 38 | 6. Link Layer: 네트워크 기기 간의 데이터를 전송합니다. (Ethernet, LAN, Wifi 등) 39 | 40 | 7. Physical Layer: 시스템 간을 물리적으로 연결하고 전기적 신호를 변환합니다. (Modem, Cable 등) 41 | 42 | 이러한 네트워크 과정에서 Frontend 단에서 해주어야할 일은 DNS lookups, TCP connections, SSL negotiation, Send Data, Receive and Handle Data 등을 말할 수 있습니다. 43 | 44 | 45 | 46 | 47 | ### Reference: 48 | 49 | - http://blog.catchpoint.com/2016/11/03/naming-conventions/ 50 | 51 | - https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010220 52 | 53 | - https://digitalleaves.com/complete-guide-networking-in-swift/ 54 | -------------------------------------------------------------------------------- /networking/restful.md: -------------------------------------------------------------------------------- 1 | #RESTful이란? 2 | 3 | Rest의 요소: 리소스 / 메서드 / 메시지로 구성 4 | 5 | 1. 리소스: Uniform한 URI 6 | 2. 메서드: Http 메서드 7 | CRUD에 해당하는 4가지 사용 8 | (Post, Get, Put, Delete) 9 | Idempotent: 여러 번 수행해도 결과가 같음 10 | Post만 빼고 Idempotent 11 | 3. 메시지: JSON데이터 등 12 | 13 | 14 | Restful? 15 | 16 | 1. Uniform 인터페이스 17 | 18 | http표준만 준수하게 되면 어떤 플랫폼에서든 사용 가능 19 | 20 | 2. Stateless 21 | 사용자의 context를 서버쪽에 저장하지 않는다. 22 | 23 | 3. Cachable 24 | 웹에서 사용하는 캐싱기능을 그대로 사용할 수 있다. 25 | -------------------------------------------------------------------------------- /pattern/architecture.md: -------------------------------------------------------------------------------- 1 | # Swift 아키텍쳐 패턴 2 | 3 | 아키텍쳐 패턴이란 주어진 상황에서의 소프트웨어 아키텍쳐에서 일반적으로 발생하는 문제점들에 대한 일반화되고 재사용 가능한 정형화된 해결 패턴을 말합니다. 이러한 패턴을 적용하면 코드의 가독성, 유지보수, 협업 등이 쉬워지며, 비슷한 상황을 마주하였을 때, 더욱 빠르고 유연하게 대처할 수 있습니다. 4 | 5 | 일반적으로 여러 아키텍쳐 패턴들의 공통적인 특징이자 장점은 화면에 보여주는 로직(View)과 실제 데이터가 처리 되는 로직(Model)을 분리한다는 것 입니다. 6 | 7 | 종류로는 웹 개발시 많이 쓰이는 MVC부터 시작해서 파생되어 나온 MVP, MVVM, Viper 등등 많은 패턴들이 있습니다. 8 | 9 | ## 아키텍쳐를 선택하는 것을 소흘히 하게 되면 10 | 11 | - 하나의 class에서 목적이 다른 수많은 일을 하게 됨 12 | - 그런 대형 class에서 버그가 있을 시 디버깅하는 것이 어려워짐 13 | - 그러면, 중요한 세부사항을 놓치기 마련 14 | 15 | ## 나쁜 아키텍쳐의 예시 16 | 17 | - 데이터가 UIViewController에서 직접적으로 저장됨 18 | - UIView는 거의 아무것도 하지 않음 19 | - Model은 단지 dumb data structure 역할 20 | - Unit Test가 아무것도 커버하지 못함 21 | 22 | ## 그러면, 좋은 아키텍쳐란 23 | 24 | - Distribution : 엄격한 role에 의해 각 entities 사이에 잘 균형된 책임(단일 책임 원칙) 25 | - Testability : 테스트 가능한 코드 26 | - Ease of use : 낮은 유지보수 비용 (적은 코드 = 적은 버그) 27 | 28 | ## MV(X) Series 29 | 30 | MVC 31 | MVP 32 | MVVM 33 | VIPER 34 | -------------------------------------------------------------------------------- /pattern/assets/async_problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/async_problem.png -------------------------------------------------------------------------------- /pattern/assets/mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/mvc.png -------------------------------------------------------------------------------- /pattern/assets/mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/mvp.png -------------------------------------------------------------------------------- /pattern/assets/mvvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/mvvm.png -------------------------------------------------------------------------------- /pattern/assets/mvvm_rx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/mvvm_rx.png -------------------------------------------------------------------------------- /pattern/assets/observable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/observable.png -------------------------------------------------------------------------------- /pattern/assets/oop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/oop.png -------------------------------------------------------------------------------- /pattern/assets/relation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/relation.png -------------------------------------------------------------------------------- /pattern/assets/uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/uml.png -------------------------------------------------------------------------------- /pattern/assets/viper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tedigom/Swift/2da7d857082d1a4391012c7217f5bc807d21bea8/pattern/assets/viper.png -------------------------------------------------------------------------------- /pattern/dip.md: -------------------------------------------------------------------------------- 1 | # Dependency Inversion Principle (의존 역전 원칙) 2 | 3 | 이 원칙이 말하는 바는 크게 두 가지로 요약할 수 있습니다. 4 | 5 | 1. 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다. 6 | 7 | 2. 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다. 8 | 9 | 이 원칙은 '상위와 하위 객체 모두가 동일한 추상화에 의존해야 한다'는 객체 지향적 설계의 대원칙을 제공합니다. 10 | 11 | 여기에서 의존 관계란 무엇인지 짚고가야할 필요성이 있습니다. 12 | 13 | ## 객체들의 관계 패턴 14 | 15 | 객체지향설계는 객체를 구별하고, 관계를 구성하는 표현하는 것입니다. 객체 즉, 클래스의 인스턴스 혼자서는 아무런 의미가 없습니다. 여러 객채들 간의 관계를 잘 활용할 때 객체지향설계를 성공적으로 했다고 말할 수 있을 것 입니다. 운전자, 정비사, 자동차, 바퀴 등등의 객체를 예시로 생각할 때 각각의 객체의 관계를 생각하지 않고서는 올바르게 모델링을 할 수 없습니다. 이러한 객체간의 관계는 아래와 같이 크게 4가지로 나눌 수 있습니다. 16 | 17 | - 1. 의존(Dependency) 관계 18 | 19 | - 2. 연관(Association) 관계 20 | 21 | - 2.1 일반 연관 관계 22 | 23 | - 2.2 집합(Aggregation) 연관 관계 24 | 25 | - 2.3 구성(Composition) 연관 관계 26 | 27 | - 3. 일반화(Generalization) 관계 (= 상속[Inheritance] 관계) 28 | 29 | - 4. 실현(Realization) 관계 30 | 31 | 위의 관계를 운전사, 정비사, 자동차 등의 예시에 빗대어 UML로 표현하면 아래와 같습니다. 32 | 33 | 34 | 35 | 36 | 37 | ### 1. 의존(Dependency) 관계 38 | 39 | 의존 관계는 **한 클래스 객체의 메소드에서 다른 클래스 객체를 사용하는 관계** 를 말합니다. 클래스의 속성으로 존재하는 것이 아닌, 메소드에 포함되기 때문에 레퍼런스 지속 시간이 일시적입니다. 연관관계와 가장 큰 구분되는 기준 역시 참조하는 클래스 인스턴스의 레퍼런스를 계속 유지하는가, 아니면 일시적인가 입니다. 주로 다음과 같은 세 가지 경우에 의존 관계로 표현합니다. 40 | 41 | 1. 한 클래스의 메소드가 다른 클래스의 객체를 인자로 받아 그 메소드를 사용한다.( 가장 일반적 ) 42 | 2. 한 클래스의 메소드가 또 다른 클래스의 객체를 반환한다. 43 | 3. 다른 클래스의 메소드가 또 다른 클래스의 객체를 반환한다. 이때 이 메소드를 호출하여 반환되는 객체의 메소드를 사용한다. 44 | 45 | 위의 예시에서 정비사는 수리하다 라는 서비스를 수행할 목적으로 자동차를 사용합니다. 이 경우, 자동차의 사양이 바뀌면 정비사가 자동차를 수리하는 행위에 영향을 받게 되지만 자동차는 어떤 정비사에게 수리되는지 상관하지 않습니다. 즉, 정비사는 자동차를 일방적으로 사용한다고 볼 수 있습니다. 이러한 정비사와 자동차 사이의 관계를 의존 관계라고 합니다. (정비사 ---> 자동차) 46 | 47 | 또한 운전자가 일시적으로 정비사를 고용하여 자동차를 수리하기 때문에 운전자와 정비사의 관계 역시 의존 관계라고 볼 수 있습니다. (운전자 ---> 정비사) 48 | 49 | ### 2. 연관(Association) 관계 50 | 51 | 연관 관계는 **한 클래스 객체에서 다른 클래스 객체를 포함하는 관계** 를 말합니다. 52 | 53 | 이러한 연관관계에서 중요하게 볼 점은 **연관 관계의 방향성( navigability ) 과 다수성( multiplicity )** 입니다. 방향성은 클래스 사이의 결합성을 이해할 수 있는 좋은 수단이 됩니다. 양방향적인 연관 관계는 결합성이 높다는 것을 의미하며, 단방향적인 연관 관계는 결합성이 낮다는 것을 의미합니다. 다수성은 관계 맺는 객체의 수를 표현합니다. 운전자와 자동차가 1대1 관계일수도 있지만, 운전자가 자동차가 없을 수도 있고, 여러 대의 자동차를 가질 수도 있다는 것을 의미합니다. 54 | 55 | 위의 예시를 살펴보면, 대부분의 어떤 특정한 운전자는 어떤 특정한 자동차와 관계를 맺게 됩니다. 이 경우, 운전자를 알고 있으면 그 운전자의 자동차를 알 수 있고, 그 반대도 가능합니다. 즉, Attribute의 개념으로 접근할 수 있다고 볼 수 있습니다. 56 | 57 | #### 2.1 일반 연관 관계 58 | 59 | 위의 일반적인 연관 관계입니다. 그러나 조금 특별한 연관 관계가 존재합니다. 예로 자동차와 부품들(엔진, 타이어 등)을 말할 수 있습니다. 자동차가 전체라면 이러한 부품은 부분 즉, **전체-부분 (whole-part)관계** 가 성립합니다. 이러한 특수 연관 관계를 **Life Cycle** 과 관련하여 크게 집합과 구성의 2가지 관계로 나눌 수 있습니다. 60 | 61 | #### 2.2 집합(Aggregation) 연관 관계 62 | 63 | 집합 연관 관계는 전체-부분 관계가 성립하는 연관 관계 중 **관계 맺는 두 클래스 인스턴스 생명 주기가 같지 않은 관계** 를 말합니다. 예시로 설명하자면, 타이어는 자동차의 일부이며, 하나의 자동차에는 4개의 타이어가 포함되어 있습니다. 타이어는 소모품이므로 자주 교체될 수 있습니다. 64 | 65 | #### 2.3 구성(Composition) 연관 관계 66 | 67 | 집합 연관 관계는 전체-부분 관계가 성립하는 연관 관계 중 **관계 맺는 두 클래스 인스턴스 생명 주기가 같은 관계** 를 말합니다. 예시로 설명하자면, 엔진을 이야기 할 수 있습니다. 엔진도 자동차를 구성하는 부분이라는 점에서 타이어와 같지만, 자동차에서 엔진을 교체하는 경우는 거의 없기 때문에 대부분 자동차와 운명을 같이합니다. 68 | 69 | ### 3. 일반화(Generalization) 관계 70 | 71 | 일반화 관계는 **한 클래스가 슈퍼클래스를 상속받아 구조와 행위를 공유하는 관계** 를 표현합니다. 자동차라는 슈퍼클래스를 승용차, 승합차, 화물차 클래스가 상속받는 것이 그 예시입니다. 72 | 73 | ### 4. 실현(Realization) 관계 74 | 75 | 실현 관계는 **한 클래스가 인터페이스의 명세를 구현하는 관계** 를 말합니다. 실현 관계는 객체지향의 다형성을 지원하는 수단이 됩니다. 76 | 77 | ## DIP 78 | 79 | 위의 의존 관계에 대한 설명으로 생각할 때, 의존 역전 원칙을 더 깊이 이해할 수 있을 것 입니다. 위의 정비사와 자동차의 관계를 다시 예로 들고자 합니다. 정비사는 추상적으로 자동차를 정비하는데 어떤 경우에는 승합차를, 어떤 경우에는 승용차를, 어떤 경우에는 화물차를 정비할 것 입니다. 80 | 81 | 이때 구체적인 자동차(승용차, 승합차, 화물차 등)은 변하기 쉬운 것이고 정비사가 자동차를 정비한다는 관계는 (정비사가 정체성을 바꾸지 않는 이상) 변하기 어려운 것입니다. 82 | 83 | 즉, 올바르게 의존 역전 원칙을 지키려면 정비사는 승용차나, 승합차, 화물차에 의존하는 것이 아닌 "자동차"라는 추상화된 인터페이스에 의존해야 하는 것 입니다. 84 | 85 | 다시 말해, 이러한 의존 역전 원칙의 key point는 "자신보다 변하기 쉬운 것에 의존하지 마라."라고 할 수 있습니다. 자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 것 이 의존 역전 원칙입니다. 86 | 87 | ## DIP의 위배 88 | 89 | 만약 위의 예시에서 정비사가 승용차에 의존한다면, 정비할 자동차의 타입이 바뀔 때마다 코드를 계속 수정해주어야 할 것입니다. 즉, DIP의 위반이 OCP의 위반을 초래하는 결과를 가져올 가능성이 높습니다. 90 | 91 | ### Reference: 92 | 93 | - https://digndig.kr/java/875/ 94 | 95 | - https://books.google.co.kr/books?id=Nh-OAgAAQBAJ&pg=PT85&lpg=PT85&dq=%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5+multiplicity&source=bl&ots=NoV1bXoh9u&sig=4Ao2lLKT88klT2NKa73XP75drt4&hl=ko&sa=X&ved=2ahUKEwjgyKvvv9vdAhVVFogKHSPADRUQ6AEwAnoECAcQAQ#v=onepage&q=%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%20multiplicity&f=false 96 | -------------------------------------------------------------------------------- /pattern/hig.md: -------------------------------------------------------------------------------- 1 | # Human Interface Guide 2 | 3 | 다음과 같은 이유로 가이드가 존재합니다. 4 | 5 | - Human Interface 가 생겨난 맥락과 애플의 고민 이해하기 6 | 7 | - 사용자 관점, 디자이너 관점, 개발자 관점에서 앱 이해하기 8 | 9 | - 공식 용어를 이해하고 앱 요소 분석하기 10 | 11 | - 가이드라인에 따라 앱 설계하기 12 | 13 | - 커스텀 뷰, 커스텀 동작을 설계할 때 기준정하기 14 | 15 | - 화면 요소와 동작에 대해 품질 기준정하기 16 | 17 | - 기획자, 디자이너와 협업할 때 용어, 기준정하기 18 | 19 | 더 자세한 내용은 아래의 링크를 참조하시면 됩니다. 20 | 21 | > 애플 공식 문서 링크: 22 | 23 | https://developer.apple.com/design/human-interface-guidelines/ 24 | 25 | > 잘 정리하신 분이 있어서 링크를 공유합니다. 26 | 27 | https://miniring.gitbook.io/hig/ 28 | -------------------------------------------------------------------------------- /pattern/hig_widget.md: -------------------------------------------------------------------------------- 1 | # Widget 2 | 3 | 애플이 권장하는 가이드는 다음과 같습니다. 4 | 5 | - **한눈에 훑어볼 수 있게** 6 | 7 | 사람들은 간단한 업데이트를 받고 아주 단순한 작업을 수행하는데에 위젯을 사용함 8 | 그러므로 적절한 양의 정보와 인터랙션을 제공하는 것이 필수적 9 | 가능하면 한 번의 탭으로 완료 할 수 있는 작업을 제공할 것 10 | 이동(pan) 및 스크롤 제스처는 지원안함 11 | 12 | - **컨텐츠 표시는 신속하게** 13 | 14 | 사람들은 위젯을 볼때 아주 적은 시간을 들이며, 컨텐츠 로딩을 기다리지 않음 15 | 그러므로 정보를 캐시해서 새 정보 로딩중에도 항상 최근 정보가 표시되게 할 것 16 | 17 | - **충분한 여백과 패딩** 18 | 19 | 컨텐츠를 위젯 영역 끝까지 채우지 말고 적어도 몇 픽셀은 여백을 줄 것 20 | 위젯 상단의 앱 아이콘의 센터에 컨텐츠 좌측 정렬 라인을 맞추면 적당함 21 | 그리드 형태의 레이아웃이라면 아이템을 한 행에 4개 정도로 제한 22 | 23 | - **다양한 해상도 대응** 24 | 25 | 축소형은 대략 2.5행의 높이 26 | 확장형은 화면 높이보다 길어지지 않게 27 | 퀵액션 위젯은 축소형만 표시됨 28 | 축소형에서는 단독으로도 의미있는 필수 정보를 표시할 것 29 | 30 | - **배경 커스터마이징은 피할 것** 31 | 32 | 블러 처리된 밝은 기본 배경은 일관성과 가독성을 위한 것 33 | 사진 배경을 절대 사용하지 말것. 잠금 화면 및 홈 화면 배경과 충돌할 수 있음 34 | 텍스트는 검정 혹은 어두운 회색의 시스템 폰트 사용 35 | 36 | - **시스템 폰트는 가독성을 위해** 37 | 38 | 어두운 컬러는 기본 배경 컬러에 잘 맞음 39 | 앱을 실행시켜서 추가적인 작업을 할 수 있게 하라 40 | 위젯은 앱과 독립적으로 동작해야 함 41 | 공간만 차지하는 "앱으로 열기"같은 버튼을 넣지 말것. 그대신 컨텐츠 자체를 탭하게. 42 | 절대 다른 앱을 실행시키지 말것 43 | 44 | - **올바른 이름 선택** 45 | 46 | 위젯 컨텐츠 상단에 아이콘과 제목 표시됨 47 | 위젯이 하나라면 보통은 앱 이름과 일치시킴 48 | 위젯이 여러개라면 대표 위젯에 앱 이름과 동일한 이름, 나머지는 명확하고 간결하게 49 | 이름 앞에 앱 이름을 붙여주는 것도 좋음. 그 앱에서 제공하는 위젯이라는걸 알림 50 | 51 | - **로그인이 필요한 경우 알림** 52 | 53 | 로그인 기반의 컨텐츠라면, "로그인하면 해당 컨텐츠를 볼 수 있다"라고 알릴 것 54 | 퀵 액션용 위젯 선택 55 | 위젯이 여러개인 경우 하나를 선택해서 표시 56 | -------------------------------------------------------------------------------- /pattern/isp.md: -------------------------------------------------------------------------------- 1 | # Interface Segregation Principle (인터페이스 분리 원칙) 2 | 3 | “ 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙 ”는 원칙입니다. 4 | 5 | Swift에서는 protocol이 더욱 작고 잘게 나눠 진 것을 보실 수 있을 것입니다. 이렇게 인터페이스가 작고 구체적이어야 그 인터페이스를 지원하는 쪽의 코드가 더욱 간결해 질 수 있습니다. 6 | 7 | 인터페이스 분리 원칙은 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙입니다. 인터페이스 분리 원칙은 큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클라이언트들이 꼭 필요한 메서드들만 이용할 수 있게 합니다. 이와 같은 작은 단위들을 역할 인터페이스라고도 부릅니다. 인터페이스 분리 원칙을 통해 시스템의 내부 의존성을 약화시켜 리팩토링, 수정, 재배포를 쉽게 할 수 있습니다. 8 | 9 | 여기서 클라이언트라 함은 어떤 다른 객체를 사용하는 쪽을 지칭합니다. 통상 또 다른 객체가 됩니다. ISP에서는 **객체를 사용하는 측을 “클라이언트”, 사용되어지는 측을 “서버”** 라고 표현합니다. 즉 A라는 객체 내부에 B라는 객체를 사용하게 된다면 A는 클라이언트, B는 서버가 되는 것입니다. “사용”이라는 말은 상속을 해서 사용하든, 내부에서 해당 객체를 만들어서 사용하든 모든 경우에 해당합니다. 10 | 11 | 12 | 여기서 자신이 사용하지 않는 메서드에 의존하지 않아야한다는 말이란 서버측에서 여러개의 메서드를 제공하고 있는데 만일 클라이언트 측에서 특정 몇몇의 메서드만 사용하고 있는 상황에서 서버측에서 클라이언트가 사용하지 않는 메서드를 변경했을 때 클라이언트가 영향을 받지 않아야 한다는 것입니다. 즉, 클라이언트는 서버가 제공하는 최소한의 자신이 필요로 하는 인터페이스만 알게 해서 서버와의 의존도를 낮춰야 한다는 이야기입니다. 13 | 14 | 이것이 가능해지려면 다음의 내용을 따라야 합니다. 15 | 16 | “큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클라이언트들이 꼭 필요한 메서드만 이용할 수 있게 한다.” 17 | 18 | ## ISP 위배 19 | 20 | 하나의 인터페이스 / 프로토콜을 모든 클라이언트가 구현하는 경우가 ISP를 위배한다고 말할 수 있습니다. 아래는 그러한 예시입니다. 21 | 22 | ```swift 23 | protocol ArticleService { 24 | func subscribe() 25 | func write() 26 | func delete() 27 | } 28 | 29 | class Subscriber: ArticleService { 30 | func subscribe() { 31 | // some code 32 | } 33 | 34 | func write() { 35 | // some code 36 | } 37 | 38 | func delete() { 39 | // some code 40 | } 41 | } 42 | 43 | class Writer: ArticleService { 44 | func subscribe() { 45 | // some code 46 | } 47 | 48 | func write() { 49 | // some code 50 | } 51 | 52 | func delete() { 53 | // some code 54 | } 55 | } 56 | 57 | class Deleter: ArticleService { 58 | func subscribe() { 59 | // some code 60 | } 61 | 62 | func write() { 63 | // some code 64 | } 65 | 66 | func delete() { 67 | // some code 68 | } 69 | } 70 | ``` 71 | 72 | 이를 리팩토링하면 다음과 같습니다. 73 | 74 | ```swift 75 | protocol ArticleSubscribeService { 76 | func subscribe() 77 | } 78 | 79 | protocol ArticleWriteService { 80 | func write() 81 | } 82 | 83 | protocol ArticleDeleteService { 84 | func delete() 85 | } 86 | 87 | class Subscriber: ArticleSubscribeService { 88 | func subscribe() { 89 | // some code 90 | } 91 | } 92 | 93 | class Writer: ArticleWriteService { 94 | func write() { 95 | // some code 96 | } 97 | } 98 | 99 | class Deleter: ArticleDeleteService { 100 | func delete() { 101 | // some code 102 | } 103 | } 104 | ``` 105 | 106 | ## ISP in iOS 107 | 108 | Objective-C나 Swift 모두에서 공통적으로 모두 ISP의 흔적을 많이 찾아볼 수 있습니다. UITableView가 dataSource와 delegate 프로토콜이 별도로 분리되어 있는 것 역시 ISP의 흔적입니다. dataSource만 쓰고 delegate는 쓸 필요없는 경우가 꽤 존재하기 때문입니다. 109 | 110 | 그외도 각종 프로토콜들이 ISP를 기반으로 설계되었습니다. Swift의 String만 보더라도 Equatable, Comparable, Hashable 등 메서드 한둘만 제공하는 프로토콜들을 매우 많이 사용하고 있고 그것들의 논리적인 조합으로 String의 전반적인 동작들을 정의하고 있습니다. 111 | 112 | 실제로 POP를 제대로 하기 위해서는 ISP를 제대로 이해하고 습관화 시키는 것이 매우 중요한 일입니다. 고전적인 ISP는 abstract class의 다중상속을 많이 활용하는데 Swift에서는 그 역할을 protocol이 해주고 있기 때문입니다. 그래서 ISP에 따라 작게 분해된 인터페이스(protocol)를 기반으로 코딩하는 것이 POP입니다. 113 | 114 | ## SRP와 ISP 115 | 116 | SRP가 수정해야할 이유를 기준으로 클래스를 작게 분해 하는데 포커스를 맞추고 있다면 ISP는 인터페이스를 명확한 목적별로 잘게 나누고 인터페이스를 기준으로 코딩을 하라고 이야기합니다. 117 | 118 | 일단 가장 먼저 선행되어야 할 것은 서버 클래스를 SRP를 준수하게 잘게 분해하는 작업이 필요합니다. 일단 그것 만으로도 변경에 대한 영향을 많은 부분 줄여줄 수 있습니다. 119 | 120 | 만일 그럼에도 불구하고 서버 클래스가 비대하거나 서버 클래스의 인터페이스가 자주 변경이 되어야 할 상황이라면 인터페이스를 grouping해서 분리해내거나 서버 클래스를 바로 사용하지 않고 인터페이스 만을 사용하게 변경하면 그런 부분을 많이 줄여줄 수 있습니다. 121 | 122 | 구현과 인터페이스를 분리하는 일이 항상 최선이라고 말하기는 힘듭니다. 모든 클래스의 인터페이스를 protocol로 만들어서 사용하는 것은 분명 비효율적인 일입니다. 123 | 124 | 하지만 만일 특정 클래스의 인터페이스가 매우 비대해서 한꺼번에 제공하기에는 사용법이 어렵다던지 논리적으로 명쾌하지 못한 경우가 발생한다면 그것들을 목적에 맞게 잘게 나눈 protocol로 구분해서 제공하는 것이 훨씬 나은 접근법이 될 수도 있을 것 입니다. 125 | -------------------------------------------------------------------------------- /pattern/lsp.md: -------------------------------------------------------------------------------- 1 | # Liskov Substitution Principle (리스코프 치환 원칙) 2 | 3 | “ 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. ”는 원칙입니다. 4 | 5 | 예시를 들어 설명하자면, 라면을 끓이는 로직에 “라면”이라는 단어에 “신라면” 혹은 “진라면”이라고 넣어도 그 로직이 정상적으로 동작해야 한다는 것을 말합니다. (라면의 하위 타입 인스턴스로 신라면, 진라면을 대입한 것 입니다.) 6 | 7 | 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 이야기는 실제로 하위 타입으로 바꾸겠다는 것이 아니라 추상화에 의존해서 작성된 코드에 구체화된 객체(어떤 하위 타입의 인스턴스)를 넣어도 그 동작이 이루어져야 한다는 말입니다. 그러기 위해서는 부모의 기능을 자식이 거부해서는 안됩니다. 상속의 본질인데, 이를 지키지 않으면 부모 클래스 본래의 의미가 변해서 is a 관계가 망가져 다형성을 지킬 수 없게 됩니다. LSP란 상속의 툴이며, OCP를 위반하지 않도록 인도하는 원칙입니다. 이 원칙은 크게 다음과 같은 조건들을 포함합니다. 8 | 9 | - 서브 타입(자식 클래스) 은 언제나 자신의 기반타입(부모 클래스) 으로 교체 할 수 있어야 한다. 10 | 11 | - 클래스인 경우, 하위 클래스라면 상위 클래스의 한 종류여야 한다. 12 | 13 | - 인터페이스인 경우, 구현 클래스는 인터페이스(규약) 를 지켜야한다. 14 | 15 | 16 | ## LSP 위배 17 | 18 | 리스코프 치환 원칙이 잘 지켜지지 않는다면 다음과 같은 문제가 발생할 수 있습니다. 19 | 20 | 1. 클래스 계층이 명료하지 않게 됩니다. 서브클래스 인스턴스를 파라미터로 전달했을 때 메소드가 이상하게 작동할 수 있습니다. 21 | 22 | 2. 슈퍼클래스에 대해 작성된 단위테스트가 서브클래스에 대해서는 작동하지 않을 것 입니다. 23 | 24 | 가장 대표적으로 드는 예시가 정사각형과 직사각형의 예시입니다. (~~식상~~) 25 | 26 | ```swift 27 | class Rectangle { 28 | private var width: Int = 0 29 | private var height: Int = 0 30 | 31 | func getWidth() -> Int { 32 | return width 33 | } 34 | 35 | func getHeight() -> Int { 36 | return height 37 | } 38 | 39 | func setWidth(width: Int) { 40 | self.width = width 41 | } 42 | 43 | func setHeight(height: Int) { 44 | self.height = height 45 | } 46 | 47 | func getArea() -> Int { 48 | return width * height 49 | } 50 | } 51 | 52 | class Square: Rectangle { 53 | override func setWidth(width: Int) { 54 | super.setWidth(width: width) 55 | super.setHeight(height: width) 56 | } 57 | 58 | override func setHeight(height: Int) { 59 | super.setHeight(height: height) 60 | super.setWidth(width: height) 61 | } 62 | } 63 | ``` 64 | 65 | 개념적으로 정사각형은 직사각형에 속하기 때문에 Rectangle 클래스를 상속받고, 높이와 넓이가 같기 때문에 하나의 값을 세팅하면 자동적으로 높이와 넓이 모두를 같게 세팅하도록 해주었습니다. 이 자체만 보면 Square 클래스는 논리적으로 아무런 문제가 없습니다. 그러나 다음과 같은 테스트 함수에서 파라미터로 슈퍼클래스(Rectangle)가 전해지느냐 서브클래스(Square)가 전해지느냐에 의해 차이가 존재합니다. 66 | 67 | ```swift 68 | func testAreaSize(rect: Rectangle) { 69 | rect.setHeight(height: 5) 70 | rect.setWidth(width: 4) 71 | 72 | if rect.getArea() == 20 { 73 | print("success") 74 | } else { 75 | print("error") 76 | } 77 | } 78 | 79 | testAreaSize(rect: Rectangle()) // success 80 | testAreaSize(rect: Square()) // error 81 | ``` 82 | 83 | 이는 LSP 위반입니다. 상속 구조상 자식이 거부할 수 밖에 없는 기능을 부모가 제공하게 된다면 LSP는 깨지게 되고 부모만 가지고 추상화된 상태에서 코딩을 하는 것은 위험하게 됩니다. 이 경우 상속 구조 자체가 잘못된 상황입니다. 이처럼 OOP에서 상속은 매우 비용이 큰 작업이며 한번 상속관계로 만들어진 커플링은 쉽게 끊어내기 힘들다고 했습니다. 상속을 하게 될때 LSP를 더욱 진지하게 고민해야 하는 이유가 여기에 있습니다. 84 | 85 | 이와 같이 LSP를 위반하게 되면 앞서 설명드린 OCP도 제대로 동작하지 않을 수 있습니다. 모든 코드에서 하위 클래스들을 명시적으로 지정해서 코딩을 해야 하기 때문입니다. 하위 클래스들을 명시적으로 지정해서 코딩을 해야 한다는 것은 코드의 복잡도를 엄청나게 높여버립니다. 예를 들어 심지어 부모 클래스가 자식 클래스를 일일이 알아야 하는 경우도 발생합니다. 심각하면 객체를 만드는 의미조차 없는 지경으로 만들어 버립니다. LSP 위반이 많아지면 어떤 객체도 우리가 예측한 동작을 할거라는 기대를 가질 수 없습니다. 이와 같은 맥락에서 생각하였을 때, LSP의 원칙이 깨질 때의 주요현상으로 타입을 확인하는 기능을 일일히 명시적으로 사용하는 경우를 말할 수 있습니다. 아래는 특정 하위클래스의 타입을 확인하는 간단한 예시입니다. 아래의 경우 SpecialItem이라는 하나의 하위클래스의 타입만을 확인하지만, 예외의 상황이 많아질수록 매우 복잡한 if문이 탄생할 것 입니다. 86 | 87 | ```swift 88 | class Item { 89 | var price: Int = 0 90 | } 91 | 92 | class SpecialItem: Item { 93 | } 94 | 95 | class Coupon { 96 | func calcuateDiscountAmount(item: Item, discountRate: Int) -> Int { 97 | if item is SpecialItem { // LSP 위반 98 | return 0 99 | } 100 | return item.price * discountRate 101 | } 102 | } 103 | ``` 104 | 105 | 106 | LSP를 준수하면 추상화된 하나의 클래스로 부터 상속된 수많은 다른 클래스를 일일이 고민하지 않고 추상화된 인터페이스 하나로 공통의 코드를 작성할 수 있게 됩니다. 이는 곧 OCP의 완벽한 동작을 보장해준다는 이야기입니다. 위의 코드를 리팩토링하면 다음과 같습니다. 107 | 108 | ```swift 109 | class Item { 110 | var price: Int = 0 111 | func isDiscountAvailable() -> Bool { 112 | return true 113 | } 114 | } 115 | 116 | class SpecialItem: Item { 117 | override func isDiscountAvailable() -> Bool { 118 | return false 119 | } 120 | } 121 | 122 | class Coupon { 123 | func calcuateDiscountAmount(item: Item, discountRate: Int) -> Int { 124 | if !item.isDiscountAvailable() { 125 | return 0 126 | } 127 | return item.price * discountRate 128 | } 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /pattern/mvc.md: -------------------------------------------------------------------------------- 1 | # MVC (Model + View + Controller) 2 | 3 | MVC는 가장 대표적인 아키텍쳐 패턴으로서, Model과 View, 그리고 Controller 세가지 요소로 구성 되어 있습니다. 4 | 5 | ## Model 6 | >Model Objects Encapsulate Data and Basic Behaviors 7 | 8 | Model은 프로그램의 비즈니스 로직이나 데이터를 불러오거나 업데이트 하는 로직을 담당합니다. Model은 앱내의 데이터를 가지고 있으며, 그 데이터를 처리하는 방식에 대한 로직을 가지고 있습니다. 그래서 앱에서 어떤 데이터가 로딩되기 위해선 반드시 Model 객체를 거쳐야만 합니다. Model은 화면에 직접적으로 데이터를 로딩하는 역할을 하지 않습니다. 그래서 Model은 view와 직접적으로 연관이 없도록 디자인해야 합니다. 예를 들어 Person 객체의 생일과 관련한 데이터를 화면에 로딩한다고 할 때, 생일정보는 Model에 있지만, 그 생일이 화면에 표현되는 것은 모델이 관리하지 않습니다. 9 | 10 | Model에 속하는 객체들은 다음과 같습니다. 11 | 12 | - Network Code - 네트워크의 커뮤니케이션을 관리하는 객체(NetworkManager의 형태) 13 | - Persistence Code - CoreData, Realm에 저장되는 객체(디스크에 저장) 14 | - Parsing Code - 네트워크의 response를 통해 생성되는 객체 15 | 16 | ## View 17 | 18 | > View Objects Present Information to the User 19 | 20 | View는 실제 사용자가 눈으로 보는 모든 객체들을 다룹니다. View는 어떻게 보여질지에 대해서만 관장하고, 어떤 데이터를 보여줄지에 대해서는 알지 못합니다. View는 보통 재사용이 가능하며, 수정할 수 있고, 앱 사이의 일관성을 제공합니다. 21 | 22 | 23 | ## Controller 24 | 25 | > Controller Objects Tie the Model to the View 26 | 27 | Controller는 Model과 View 사이의 중간자 역할로서, 사용자는 컨트롤러를 사용하여 모델의 상태를 바꾸고, 그에 따른 뷰를 보여줍니다. Controller는 또한 앱의 환경설정을 담당하고, 다른 객체들의 life cycle을 관리합니다. 28 | 29 | Controller는 다음과 같은 일을 하는 것들입니다. 30 | 31 | - What should be accessed first: the persistence or the network? 32 | - How often should you refresh the app? 33 | - What should the next screen be and in what circumstances? 34 | - If the app goes to the background, what should be cleaned? 35 | 36 | 37 | mvc.png 38 | 39 | Model과 view는 서로 대화할 수 없고, 각각은 controller와 상호 소통합니다. 40 | 41 | - > Controller -> View 42 | 먼저 Controller는 IBOutlet 등을 통해 View에게 어떤 데이터를 보여줄지 구체적인 지시를 하게 됩니다. 43 | 44 | - > View -> Controller 45 | View는 유저 인터렉션의 상황에서 데이터 업데이트를 위해 IBAction이나 Datasource, Delegate를 통하여 Controller와 소통합니다. 46 | 47 | - > Controller -> Model 48 | 유저 인터렉션 등으로 인한 데이터 업데이트를 위하여 Controller는 Model class의 Instance를 만들어 Model 업데이트를 수행합니다. 49 | 50 | - > Model -> Controller 51 | 데이터가 변하여 컨트롤러가 뷰를 업데이트 해야하는 경우, 모델은 KVO와 Notification을 수행하여 컨트롤러와 소통합니다. 52 | 53 | MVC는 이해하기 쉽고 생산성이 매우 좋습니다. 그러나 iOS의 MVC는 Model-View-Controller의 분리가 완전하지 않아서 Model에 넣기도 애매하고 View에 넣기도 애매한 코드들이 모두 Controller에 위치하게 되기 때문에 Controller의 크기가 지나치게 커지는 경향이 있습니다. 이를 Massive View Controller 문제라고도 합니다. 54 | -------------------------------------------------------------------------------- /pattern/mvp.md: -------------------------------------------------------------------------------- 1 | # MVP 2 | 3 | 4 | 5 | 6 | - 겉보기에는 MVC 패턴과 유사하지만 MVC 패턴은 View와 Controller가 팽팽히 묶여있지만 MVP 패턴에서는 Controller가 중재자 역할만을 수행함 7 | - UIViewController를 View로써 다룸 8 | - Presenter는 ViewController의 Life Cycle에 관여하지 않음 9 | - Presenter는 쉽게 View를 Mocking 할 수 있음 10 | - Presenter는 layout코드가 없으나, data를 가지고 View를 업데이트하는 책임을 가지고 있음 11 | -------------------------------------------------------------------------------- /pattern/mvvm.md: -------------------------------------------------------------------------------- 1 | # MVVM (Model-View-View Model) 2 | 3 | mvvm.png 4 | 5 | MVVM 패턴을 그림으로 형상화하면 위와 같습니다. Controller는 더이상 Model에게 직접 명령하지 않고, Presentation Logic을 담당하는 View Model을 따로 두어 이를 통해 소통합니다. MVVM은 아래와 같은 몇 가지 규칙을 따릅니다. 6 | 7 | - Model은 다른 클래스들이 데이터 변경에 대한 notification을 내보내더라도 이들과 직접 통신하지 않는다. 8 | - View Model은 Model과 통신하며 여기서의 데이터들을 ViewController로 내보낸다. 9 | - View Controller는 View 생명주기를 처리하고 데이터를 UI 구성요소에 바인딩 할 때만 View Model 및 View와 통신한다. 10 | - (MVC 패턴에서처럼) View는 이벤트에 대해서만 View controller를 인식한다. 11 | 12 | 13 | MVVM은 이처럼 각 단위가 독립적으로 작성되고 테스트 될 수 있게 설계된 패턴입니다. 14 | 15 | 이 View Model은 View와 Data Binding을 통해 연결되어 있습니다. ViewModel은 유저 인터렉션의 상황에서 명령을 받아 Model에 변화를 주고 업데이트를 하는데, View와 연결된 데이터 바인딩으로 인해 View도 자연스레 업데이트됩니다. ViewModel은 View에 대해 아무것도 모르기 때문에 테스트가 쉽고 바인딩으로 인해 코드 양이 많이 줄어듭니다. 16 | 17 | 이러한 Data Binding을 위한 객체 간 소통 방법으로서 RxSwift의 Observable 패턴을 사용하면 간결하고 강력하게 아키텍쳐를 구축할 수 있습니다. 18 | 19 | mvvm_rx.png 20 | 21 | 22 | MVVM의 장점의 매우 간단한 예시로 계좌정보의 모델이 있고, 그 계좌정보를 가공해서 보여주어야 하는 상황을 말할 수 있습니다. 23 | 24 | 모델의 데이터은 아래와 같고, 25 | 26 | ```swift 27 | let amount = 6729383.99 28 | ``` 29 | 30 | 보여주어야 하는 라벨은 다음과 같다고 가정해보겠습니다. 31 | ```swift 32 | Your balance is $6,729,383.99 33 | ``` 34 | 35 | 보통 MVC에서는 해당 데이터를 View Controller에서 통화 단위나 "Your balance is" 등의 문구를 가공하여 Label 뷰의 텍스트로 보여줍니다. 그러나 아래와 같은 뷰 모델을 통해서 데이터를 가공하고 이를 정보 뷰에 보여준다면, 각각의 출력값이 명료하기 때문에 뷰 컨트롤러나 뷰 등의 독립적 단위를 테스트하기 쉬워집니다. 36 | 37 | ```Swift 38 | struct AccountViewModel { 39 | let displayBalance: String 40 | 41 | init(mode: BankAccount) { 42 | let formattedBalance = model.balance.currencyValue 43 | displayBalance = "Your balance is \(formattedBalance)" 44 | } 45 | } 46 | ``` 47 | 48 | 49 | ### Reference: 50 | https://www.objc.io/issues/13-architecture/mvvm/ 51 | -------------------------------------------------------------------------------- /pattern/solid.md: -------------------------------------------------------------------------------- 1 | # 디자인 패턴의 기본 (SOLID) 2 | 3 | ## OOP 4 | 5 | 객체 지향 프로그래밍의 줄임말으로서, 프로그램을 어떻게 설계해야 하는지에 대한 일종의 개념이자 방법론입니다. 프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식입니다. 객체는 하나의 '역할'을 수행하는 메소드와 데이터의 묶음입니다. 6 | 7 | 큰 문제를 작게 쪼개는 것이 아니라, 먼저 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-up) 해결법을 도입한 것입니다. 객체란 것을 일단 한번 독립성/신뢰성이 높게 만들어 놓기만 하면 그 이후엔 그 객체를 수정 없이 재사용할 수 있으므로 개발 기간과 비용이 대폭 줄어들게 됩니다. 즉, OOP의 장점을 정리하면 아래와 같습니다. 8 | 9 | 1. 코드의 재사용성이 높다. 10 | 11 | 2. 코드의 관리가 용이하다. 12 | 13 | 3. 신뢰성이 높은 프로그래밍을 가능하게 한다. 14 | 15 | 그러나 점차 이러한 객체지향 프로그램이 복잡해지면서 이를 간결하게 정리할 필요성이 생긴 관계로 '디자인 패턴'이라는 것이 생겼습니다. 디자인 패턴이란 프로그래밍 형식을 정하는 일종의 약속으로, 이는 특히 협업을 전제로 한 환경에서 특히 강조되고 있습니다. Foundation / UIKit 등의 프레임워크들 역시 OOP를 기본으로서 Delegate 등의 여러 패턴들을 도입하였다고 볼 수 있습니다. 때문에 여러 디자인 패턴들을 깊이 이해하기 위하여 OOP에 대한 이해가 필수적입니다. 16 | 17 | ## OOP의 특징 18 | 19 | 20 | 21 | ### Abstraction 추상화 22 | 23 | OOP의 가장 핵심적인 개념입니다. 객체에서 공통된 속성과 행위를 추출하는 것을 추상화(Abstraction)라고 합니다. 추상화 과정을 통하여 실세계 상황을 간결하고 명확하게 모델링하게 되며 구체화 과정을 통하여 추상적 모델을 프로그램으로 변환합니다. 예를 들어 사과, 배, 포도라는 객체가 있을 때 이것들을 과일으로 묶으며 추상화가 가능합니다. 24 | 25 | OOP의 원칙 중 하나인 “코딩은 구체적인 것에 의존하지 않고 추상화된 것에 의존해야 한다.(DIP)” 역시 그 궤를 같이합니다. 추상화는 다른 객체들과 구분되는 핵심적인 특징들에만 집중함으로써, 복잡도를 관리할 수 있도록 합니다. 하나의 대상에 대하여 목적이나 원하는 기능에 따라 여러 추상화 모델이 생성될 수 있습니다. 26 | 27 | 추상화를 통하여 이루고자 하는 목적은 두가지 입니다. 1. 어떻게 하면 실제 시스템을 간결하게 표현할 수 있는가?이고 또 다른 하나는 2. 어떻게 하면 실제 시스템의 정보를 모두 표현하는가? 입니다. 그러나 어떠한 시스템을 간결하며 자세히 표현한다는 것은 두 마리 토끼를 잡는 것과 같은 상황이기 때문에 이 목표들을 잘 조절하여 모델링을 수행하는 능력이 필요합니다. 28 | 29 | 아래의 캡슐화, 상속, 다형성 역시 추상화의 개념에 근간합니다. 예로 UITableViewCell을 상속받아 MyTableViewCell을 만든다고 가정할 때, 우리가 UITableViewCell을 상속받아 MyTableViewCell을 재정의했다고 해서 UITableView가 MyTableViewCell을 직접적으로 알아야 할 필요는 없습니다. 만일 그랬다면 MyTableViewCell을 만들면 UITableView도 재컴파일 되거나 상속받아 MyTableViewCell을 처리할 수 있게 만들어야 했을 겁니다. 단순히 UITableViewCell의 일부 기능을 재활용한 것이 아니라 우리가 cell을 마구 커스터마이징 하여도 UITableView 자체를 계속 재활용하고 있는 것입니다. 이것이 가능해지는 이유는 UITableView가 구체적인 MyTableViewCell이라는 클래스를 기반으로 동작하지 않고 추상화된 개념의 UITableViewCell을 기반으로 동작하고 있기 때문에 가능한 일입니다. 30 | 31 | ### Encapsulation 캡슐화 32 | 33 | 실제로 구현되는 부분을 외부에 드러나지 않도록 캡슐로 감싸 이용방법만을 알려주는 것 입니다. 캡슐화를 통해 데이터 구조와 데이터들은 1. 묶이고 2. 외부로부터 숨겨집니다. 34 | 35 | 먼저, 묶음으로 인해 프로그램을 바라보는 단위가 커집니다. 이전의 절차지향에서는 프로그램을 함수 단위로 구조화할 수 있으나, 프로그램 소스가 커지면 이해하기 어렵고 관리가 힘들어 질 수 있었습니다. 그러나 객체지향 프로그램에서는 프로그램 소스를 클래스 단위로 바라보게 됨으로써 좀더 복잡하고 커다란 소스코드도 쉽게 이해하게 되었습니다. 36 | 클래스 내부에 여러 함수를 내포할 수 있기 때문에 프로그램 소스 코드를 바라보는 단위가 커졌으며, 그로 인해 프로그램 관리가 좀 더 수월해진 것입니다. 37 | 38 | 두번째, 내부를 숨김으로써 내부를 좀더 자유롭게 변경할 수 있게 되었습니다. 이전의 함수 중심적인 구조적 프로그래밍 언어에서는 프로그램 내부에서 데이터가 어디서 어떻게 변경되는지 파악하기 어려웠고, 그로 인해 유지 보수가 힘들었기 때문에 자료를 중심으로 함수가 종속되는 구조가 되기도 하였습니다. 객체 지향에서는 클래스 내부의 데이터를 외부에서 참조하지 못하도록 차단하여 이러한 폐단을 없앨 수 있습니다. 이렇게 내부의 데이터나 함수를 외부에서 참조하지 못하도록 차단하는 개념을 정보 은닉Information Hiding)이라고 합니다. 캡슐화는 중요사항을 감춘 상태에서 외부에 그것을 사용할수 있는 방법을 설정하고 외부와 직접적으로 의사소통을 의미 합니다. 39 | 40 | ### Inheritance 상속 41 | 42 | 상위 개념의 특징을 하위 개념이 물려받는 것입니다. 상속을 통해 재사용으로 인한 코드가 줄어듭니다. 하위 클래스에서 속성이나 오퍼레이션을 다시 정의하지 않고 상속받아서 사용함으로써 코드가 줄어들기 때문입니다. 그리고 좀 더 범용성있게 사용할 수 있습니다. 하위 클래스는 상위 클래스가 가지고 있는 모든 자료와 메소드를 물려받아 자유롭게 사용할 수 있지만, 또한 자신만의 자료와 메소드를 추가적으로 덧붙임으로써 새로운 형태의 클래스로 발전하게 됩니다. 43 | 44 | ### Polymorphism 다형성 45 | 46 | 부모클레스에서 물려받은 가상 함수를 자식 클래스 내에서 오버라이딩 되어 사용되는 것 입니다. 웹 사전을 보면, 객체지향에서의 다형성은 '여러 클래스들이 동일한 이름의 오퍼레이션을 서비스하도록 하는 것'이라고 합니다. 이러한 다형성은 실제의 코드에서는 '하나의 클래스 내부에 같은 이름의 오퍼레이션을 여럿 정의하거나, 상위 클래스의 오퍼레이션을 하위 클래스에서 다시 정의함'으로써 구현합니다. 47 | 48 | 49 | ## OOP의 원칙: SOLID 50 | 51 | 어떻게 설계하고 코딩하는 것이 OOP에 맞게 잘 하는 것인가에 대한 고민과 연구는 오래전부터 계속 있어왔고 지금은 상당히 정리된 상태입니다. 여러 학자들이 주장한 수많은 이야기들 중에서 로버트 C. 마틴은 가장 핵심적이라고 판단 할 수 있는 5가지를 모아서 SOILD라는 이름을 붙였습니다. 각각의 문자는 두문자로 SRP, OCP, LSP, ISP, DIP를 지칭하고 있습니다. iOS의 프래임워크 뿐만 아니라 잘 설계된 대부분의 프래임워크과 코드들은 이 원칙을 따르고 있습니다. 각 원칙에 대한 더 자세한 내용은 각 원칙의 문서를 참조하시면 됩니다. 52 | 53 | 각각의 원칙들은 다음과 같습니다. 54 | 55 | 1. [Single Responsibility Principle (단일 책임 원칙)](./srp.md) 56 | 57 | “ 한 클래스는 하나의 책임만 가져야 한다. ”는 원칙입니다. 58 | 59 | 2. [Open / Closed Principle (개방-폐쇄 원칙)](./ocp.md) 60 | 61 | “ 확장에는 열려있으나 변경에는 닫혀 있어야 한다. ”는 원칙입니다. 62 | 63 | 확장이란 새로운 기능을 추가하는 것을 말하며 새로운 기능의 추가는 쉬워야 한다는 말입니다. 변경은 말 그대로 기존의 기능의 변경인데 닫혀있다는 말은 만일 변경이 발생하면 한 곳에서만 수정이 이루어져야(변경 지점이 닫혀있어 다른 곳에 영향을 주지 않아야 함)한다는 말입니다. 64 | 65 | Abstract class를 상속받아 구체화된 클래스를 가지는 형태의 패턴들이 기본적으로 사용하는 원칙입니다. (변경이 발생해도 구체화된 클래스 하나만 수정하면 되어야 함) 66 | 67 | 3. [Liskov Substitution Principle (리스코프 치환 원칙)](./lsp.md) 68 | 69 | “ 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. ”는 원칙입니다. 70 | 71 | 이 원칙은 개방-폐쇄 원칙과 일부 궤를 함께하고 있습니다. 72 | 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 이야기는 실제로 하위 타입으로 바꾸겠다는 것이 아니라 추상화에 의존해서 작성된 코드에 구체화된 객체(어떤 하위 타입의 인스턴스)를 넣어도 그 동작이 이루어져야 한다는 말입니다. 그러기 위해서는 부모의 기능을 자식이 거부해서는 안됩니다. 73 | 74 | 4. [Interface Segregation Principle (인터페이스 분리 원칙)](./isp.md) 75 | 76 | “ 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙 ”는 원칙입니다. 77 | 78 | Swift에서는 protocol이 더욱 작고 잘게 나눠 진 것을 보실 수 있을 것입니다. 이렇게 인터페이스가 작고 구체적이어야 그 인터페이스를 지원하는 쪽의 코드가 더욱 간결해 질 수 있습니다. 79 | 80 | 5. [Dependency Inversion Principle (의존관계 역전 원칙)](./dip.md) 81 | 82 | “ 추상화에 의존해야지 구체화에 의존하면 안된다 ”는 원칙입니다. 83 | 84 | 메소드를 콜하는 형태가 아니라 메소드가 불리는 형태의 구현이 된다면 많은 경우 DIP의 예가 됩니다. UIViewController를 예로 들 수 있습니다. 개발자는 UIViewController를 상속받아 필요한 ViewController를 만드는데, viewDidLoad 같이 시스템이 불러주는 많은 메소드들을 override해서 사용합니다. 이것 역시 DIP의 예가 됩니다. 85 | 86 | 즉, UIViewController를 다루는 시스템에서는 구체적인 ViewController들을 다루는 것이 아니라 추상화된 UIViewController를 이용해서 코딩이 되어있고 필요한 각 시점에 지정된 메소드들이 불리는 것입니다. 87 | 88 | 이렇게 DIP는 상속을 통해서 구현되기도 하지만 protocol을 통해서 구현되기도합니다. UITableView에서 tableView의 구체적인 동작을 제어하기 위한 delegate나 datasource와 같은 protocol 역시 DIP의 예입니다. 89 | 90 | ### Reference: 91 | 92 | - https://jungwoon.github.io/solid/2017/07/31/Solid-Principle/ 93 | 94 | - http://karenn.tistory.com/11 95 | 96 | - https://beerntv.wordpress.com/2017/01/18/0118-%E1%84%8E%E1%85%AE%E1%84%89%E1%85%A1%E1%86%BC%E1%84%92%E1%85%AA-%E1%84%8F%E1%85%A2%E1%86%B8%E1%84%89%E1%85%B2%E1%86%AF%E1%84%92%E1%85%AA-%E1%84%8B%E1%85%B3%E1%86%AB%E1%84%82%E1%85%B5%E1%86%A8/ 97 | -------------------------------------------------------------------------------- /pattern/srp.md: -------------------------------------------------------------------------------- 1 | # Single Responsibility Principle (단일 책임 원칙) 2 | 3 | "단일 클래스는 오직 하나의 책임을 가져야 한다. (클래스를 수정/변경하는 이유는 단 한개 여야 한다)"는 원칙입니다. 4 | 5 | 단일 책임 원칙은 클래스의 목적을 명확히 함으로써 구조가 난잡해지거나 수정 사항이 불필요하게 넓게 퍼지는 것을 예방하고 기능을 명확히 분리할 수 있게 합니다. 하나의 클래스가 두 가지 이상의 책임을 지니게 되면 클래스의 목적이 모호해지고 기능을 수정할 때 영향을 받는 범위도 커져서 유지보수가 힘들어지며 결국 작성한 본인조차도 이게 정확히 뭐하는 클래스인지 명확히 설명할 수가 없는 스파게티 코드가 되어버리곤 합니다. 6 | 7 | 이를 명확히 하기 위하여 '단일'과 '책임' 두 가지 키워드에 대한 이해가 필요합니다. 8 | 9 | ## 단일 10 | 11 | “단일”이란 결국 “한가지”를 말한다고 이해할 수 있습니다. 다만, “단일”의 범위가 문제가 될 수 있습니다. 정말 크게 봐버리면 앱 자체가 이미 한가지 기능을 가지고 있다고 이야기 해버릴 수도 있습니다. 정말 작게 본다면 앱의 특정한 부분의 아주 세부적인 특정 기능을 이야기 할 수 도 있을 것입니다. 12 | 13 | 이렇듯 “단일”이라는 표현은 매우 상대적인 부분입니다. 때문에 아래의 "책임"부분과 함께 고려하여 적당한 선을 개발자 스스로 선택해야 합니다. 14 | 15 | ## 책임 16 | 17 | SRP에서는 “책임”을 **변경을 위한 이유** 라고 판단합니다. 만일 특정 한가지의 기능을 변경하기 위해서 여러 클래스를 고쳐야 한다면 기본적으로 설계가 잘못 되었을 가능성이 큽니다. 동일한 수정은 한 곳에서 이루어져야 한다는 것입니다. 18 | 19 | 반대로, 특정 기능을 변경하기 위해 수정을 했으나 클래스의 대부분의 코드가 수정되지 않았다면 (클래스의 극히 일부만 수정되었다면) 이 역시 SRP 위반 사례가 될 수 있습니다. 즉, 그 클래스는 그 기능 이외의 여러가지 기능들을 가지고 있는 상태라고 인식할 수 있습니다. 20 | 21 | 만일 하나의 클래스가 있는데 논리적으로 생각했을 때는 단일 기능이라 판단되지만 차후 변경의 관점에서 특정 부분만 변경될 가능성이 높거나 빈번하다면 그 변경부만 별도의 클래스로 분리하는 것이 SRP를 준수하는 방법이 됩니다. 제대로 된 설계에서는 클래스는 응집력을 가지고 있고 결합도가 낮으며 특정 기능의 변경을 위한 수정이 한곳에 집중 되어야 합니다. 그런 경우 SRP를 준수했다고 할 수 있습니다. 22 | 23 | ## SRP 위배 24 | 25 | SRP를 위배한다는 것은 클래스가 이러한 단일 책임을 지지 않는다는 것을 말합니다. 26 | 27 | 아래는 하나의 UserSettingService class가 1. 변경과 2. 접근 권한의 두 가지 책임을 지는, SRP 위배의 예시입니다. 28 | 29 | ```swift 30 | struct UserSettingService { 31 | 32 | func changeEmail(_ user: User) { 33 | if(checkAccess(user)) { 34 | // Grant option to change 35 | } 36 | } 37 | 38 | func checkAccess(_ user: User) -> Bool { 39 | // Verify if the user is valid. 40 | } 41 | 42 | } 43 | ``` 44 | 45 | 위의 예시에서 SecurityService class를 추가하여 두 개의 책임을 나눠갖도록 리팩토링하면 아래와 같습니다. 46 | 47 | ```swift 48 | struct UserSettingService { 49 | func changeEmail(User user) { 50 | if(SecurityService.checkAccess(user)) { 51 | // Grant option to change 52 | } 53 | } 54 | } 55 | 56 | struct SecurityService { 57 | static func checkAccess(User user) -> Bool { 58 | // Verify if the user is valid. 59 | } 60 | } 61 | ``` 62 | 63 | 메서드의 SRP를 해치는 경우 자주 나타나는 대표적인 코드가 반복적인 분기문입니다. (수정에 대해 닫혀있지 않다는 점에서 OCP의 위배이기도 합니다.) 아래는 SRP 원칙을 지키지 못한 경우입니다. 64 | 65 | ```swift 66 | class Person { 67 | private static let MEN: Int = 1 68 | private static let WOMEN: Int = 2 69 | private var gender: Int = 1 70 | 71 | func wearUnderwear() { 72 | if (self.gender == Person.MEN) { 73 | // 아래 속옷만 입는다 74 | } else { 75 | // 아래,위로 속옷을 입는다 76 | } 77 | } 78 | } 79 | ``` 80 | wearUnderwear메서드가 Men과 Women의 행위를 모두 구현하려고 하기 때문에 단일 책임의 원칙을 위배한다고 볼 수 있습니다. 이를 리팩토링하면 다음과 같습니다. 81 | 82 | ```swift 83 | protocol Person { 84 | func wearUnderwear() 85 | } 86 | 87 | class Men: Person { 88 | func wearUnderwear() { 89 | // 아래 속옷만 입는다 90 | } 91 | } 92 | 93 | class Women: Person { 94 | func wearUnderwear() { 95 | // 위 아래로 속옷을 입는다. 96 | } 97 | } 98 | ``` 99 | 100 | 101 | 102 | ### iOS에서의 대표적인 SRP 위반 사례 103 | 104 | iOS에서 가장 대표적이고 공통적으로 존재하며 많이 언급되는 SRP위반사례는 가장 많이 사용하는 UIViewController의 subclass들입니다. 통상 massive view controller라는 용어로 비대해진 view controller에 대한 성토가 있고 그 해결책으로 MVP, MVVM, VIPER등의 패턴들을 이야기 하고 있습니다. 105 | 106 | 다음은 애플 문서에서 정의한 UIViewController입니다. 107 | 108 | Provides the infrastructure for managing the views of your UIKit app. 109 | 110 | “managing views”라고 정확하게 정의를 내리고 있습니다. 통상 ViewController의 코드는 꽤 길 것으로 생각됩니다. 해당 클래스에서 “managing views”에 해당하지 않는 부분인 데이터 패치 및 관리, 엄격하게는 View의 layout에 밀접하게 관여하는 것까지 SRP 위반이라고 볼 수 있을 것 입니다. 111 | 112 | ### Reference: 113 | 114 | - https://namu.wiki/w/%EA%B0%9D%EC%B2%B4%20%EC%A7%80%ED%96%A5%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%EC%9B%90%EC%B9%99 115 | 116 | - https://jungwoon.github.io/solid/2017/07/31/Solid-Principle/ 117 | 118 | - http://karenn.tistory.com/11 119 | -------------------------------------------------------------------------------- /pattern/viper.md: -------------------------------------------------------------------------------- 1 | # VIPER 2 | 3 | MV(X)에서 파생된건 아니지만 5개의 layer로 책임이 분배된 아키텍쳐(View, Interactor, Presenter, Entity, Router) 4 | 5 | 6 | 7 | - View 8 | - UIViewController에 해당 9 | - Presenter에게 유저 액션을 알림 10 | - Presenter 11 | - View와 Interactor의 중간 다리 역할 12 | - View에 대한 비즈니스 로직 처리 13 | - UIKit에 독립되어 있음 14 | - Interactor 15 | - Data(Entity)에 대한 비즈니스 로직 처리 16 | - Service, Manager class (네트워크 처리 등등..) 17 | - Entity 18 | - data Object 19 | - data access layer 아님(Interactor가 담당) 20 | - Router 21 | - VIPER 모듈간 연결고리 22 | -------------------------------------------------------------------------------- /security/cryptography.md: -------------------------------------------------------------------------------- 1 | # Cryptography 2 | 3 | 암호화 서비스는 전송 중인 데이터(보안 통신)와 유휴 데이터(보안 스토리지)를 보호하는 기반이 됩니다. 즉, 정보를 보호하기위한 수학적, 언어학적인 방법을 다루는 것이며, 컴퓨터과학, 수학, 언어학과 관련이 깊습니다. 암호학은 크게 두 가지 분류라 나누어 집니다. **1. 쌍방향 암호시스템과 2. 단방향 암호시스템** 이 그것이며, 쌍방향 암호시스템은 다시 **1.1 대칭 키 암호시스템과 1.2 공개 키 암호시스템 1.3스테가노그래피 등** 으로 나누어집니다. 4 | 5 | 정교한 수학을 사용하면 다음과 같은 이점이 있습니다. 6 | 7 | - 외부 관찰자가 이해할 수 없도록 데이터 암호화 및 암호 해독 8 | 9 | - 데이터가 원래 해싱, 서명 및 확인에 의해 전송되었기 때문에 수정되지 않았는지 확인합니다. 10 | 11 | ## Encryption and Decryption 12 | 13 | 암호화는 데이터를 다시 변환하는 방법을 알고 있는 사람을 제외하고 읽을 수 없는 형태로 변환하여 데이터를 보호하는 방법 중의 하나입니다. 14 | 15 | 암호화는 일반적으로 전송 중인 데이터와 유휴 상태의 데이터를 보호하는 데 사용됩니다. 신뢰할 수 없는 통신 채널을 통해 정보를 보내야 하는 경우 암호화를 사용하여 통신을 보호하는 것은 두 엔드포인트의 책임입니다. 마찬가지로 로컬 디스크에 정보를 저장할 때, 컴퓨터를 도난 당하더라도 제3자가 정보를 읽을 수 없도록 하기 위해 암호화를 사용할 수 있습니다. 16 | 17 | 암호라고 하는 암호화 기술은 여러 가지 방식으로 작동하고 다양한 용도로 사용할 수 있습니다. 18 | 19 | 20 | ## 1. 쌍방향 암호시스템 21 | 22 | 쌍방향 암호시스템이란 평문과 암호문 사이의 변환이 자유로운 암호체계입니다. 즉 특정 키를 통하여, Encryption과 Decryption이 가능하다는 것 입니다. 23 | 24 | ### 1.1 대칭키 암호시스템 25 | 26 | 대칭 키 암호 시스템은 암호를 해독하는 데에 사용하는 키와 암호를 만드는 데 사용하는 키가 같은 암호화 기법으로, 제작이 단순하지만 상대적으로 보안성이 낮으며, 또한 여러 사람이 사용할 경우 각자 메세지를 주고받기 위해서는 각자마다 다른 키가 필요한 점이 단점입니다. 27 | 28 | 대칭 암호화에서 단일 키(일반적으로 긴 랜덤 바이트 문자열)는 수학적으로 정보를 변환하는 데 사용되며 나중에 원래 정보를 검색하는 데 역방향으로 사용됩니다. 29 | 30 | 31 | 32 | 대칭 암호화는 보안 통신에 사용되는 경우가 많습니다. 그러나 두 엔드포인트는 동일한 비밀 키를 알아야 하므로 대칭 암호화만으로는 충분하지 않습니다. 33 | 34 | 카이사르 암호, 비즈네르 암호, 스키테일 등의 예가 있습니다. 35 | 36 | ### 1.2 공개 키 암호 시스템 37 | 38 | 공개 키 암호 시스템은 위의 대칭키 암호 시스템에서의 보안성과 기하급수적으로 늘어나는 키의 개수를 개선하기 위한 방법으로, **암호화에 사용되는 키와 복호화에 사용되는 키가 다릅니다.** 때문에 비대칭키 암호시스템이라고도 불립니다. 처리해야 하는 정보량이 많기 때문에 컴퓨터가 발명되고 난 뒤에 생겼습니다. 39 | 40 | 정보를 변환하기 위해 수학적으로 관련된 두 개의 키가 사용됩니다. 한 키로 암호화된 정보는 다른 키로만 해독할 수 있으며 그 반대의 경우도 마찬가지입니다. 일반적으로 이러한 키 중 하나(개인 키)는 기밀로 유지되며 다른 키(공용 키)는 광범위하게 사용할 수 있습니다. 타인이 메시지를 암호화할 때 사용하는 공개키(Public Key)와 암호문을 원래의 메시지로 복원할 때 쓰는 비밀키(Private key)로 구성돼 있어 두개의 키를 사용합니다. 41 | 42 | 두 키는 수학적으로 관련되어 있지만 다른 키에서 한 키를 추출하는 것은 계산적으로 불가능한 것으로 간주됩니다. 43 | 44 | 45 | 46 | 47 | 비대칭 암호화가 공유 통신 채널을 설정하는 데 사용되는 경우가 많습니다. 비대칭 암호화가 계산적으로 비싸기 때문에 두 엔드포인트는 비대칭 암호화를 사용하여 대칭 키를 교환한 다음 훨씬 더 빠른 대칭 암호화 알고리즘을 사용하여 실제 데이터를 암호화하고 암호를 해독합니다. 48 | 49 | 또한 신뢰도를 설정하는 데 비대칭 암호화를 사용할 수 있습니다. 개인 키로 정보를 암호화하면 다른 사용자가 공용 키로 해당 정보를 읽고 사용자가 해당 정보를 암호화했는지 확인할 수 있습니다. 50 | 51 | ### 1.3 Steganography 52 | 53 | 전달하려는 정보를 이미지, 오디오 등의 파일에 인간이 감지할 수 없도록 숨겨져 상대방에게 전달하는 기술의 총칭입니다. 기존의 암호화 방법은 메시지를 암호화하여 정보를 보호하는 반면에 스테가노그래피는 비밀정보를 매체에 은닉하여 그 정보의 존재 자체를 감추는 보안 기술입니다. 54 | 55 | 56 | 57 | ## 2. 단방향 암호시스템 58 | 59 | 단방향 암호시스템이란 평문에서 암호문으로 만드는 것은 자유지만 그 반대는 아닌 암호체계입니다. 대부분이 해쉬 암호화 체계로 이루어져 있습니다. 60 | -------------------------------------------------------------------------------- /security/overview.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | 클라우드 지원 및 고도로 네트워크로 연결된 현대 컴퓨팅 환경에서 보안은 적절한 소프트웨어 엔지니어링의 가장 중요한 측면 중 하나입니다. 4 | 5 | 보안에 대해 알아야 할 가장 중요한 것은 처음부터 애플리케이션 또는 서비스에 보안을 의식적으로 설계하고 설계부터 구현, 테스트 및 릴리스에 이르기까지 전체 프로세스의 의식적인 부분으로 만들어야 한다는 것입니다. 6 | 7 | 8 | 9 | 보안을 위해서 개인정보보호나, 파일 및 데이터의 신뢰성, 전송 중인 데이터 보호, 암호학 등 여러 가지 측면의 노력이 필요합니다. 10 | --------------------------------------------------------------------------------