Upload
-
View
1.294
Download
0
Embed Size (px)
Citation preview
내용
‣ ���IOS�&�Swift�학습
‣ ���개발�시작
‣ ���도움이�되었던�것들
IOS�&�Swift�학습
저는...
‣ ���앞으로�Mac�App�개발하고�싶음
‣ ���함수형�프로그래밍�경험�有
‣ ���Android�개발�경험�有(그렇지만�안한지�1년�이상�경과�😖 )�
‣ ���장공의
‣ ���21살�젊은이
저는...
‣ ���함수형�프로그래밍�경험�有
‣ ���Android�개발�경험�有(그렇지만�안한지�1년�이상�경과�😖 )�
‣ ���장공의
⭐ (도움)
‣ ���앞으로�Mac�App�개발하고�싶음
‣ ���21살�젊은이
⭐ (도움)
학습�기간
‣ ���Swift3�공부�1�week
‣ ���IOS�공부�4�days
‣ ���카메라�앱�개발�2�month�+�ɑ
‣ ���개발�도중�필요한�부분�추가�학습
학습�기간
‣ ���Swift3�공부�1�week
‣ ���IOS�공부�4�days
‣ ���카메라�앱�개발�2�month�+�ɑ
‣ ���개발�도중�필요한�부분�추가�학습
개발�일정�때문에�학습에�많은�시간을�투자�할�수�없음
안드로이드�개발�경험을�살려�기본부터�빠르게�접근
And
Android�&�IOS
‣ ���Single�Thread�Model
‣ ���Lifecycle,�Data�유보�등
‣ ���SurfaceView�(==�CALayer
‣ ���Thread,�Handler,�Executors…�(==�GCD
‣ ���메모리�관리
‣ ���Optimizing,�Performance
Android�&�IOS
‣ ���Single�Thread�Model
‣ ���Lifecycle,�Data�유보�등
‣ ���SurfaceView�(==�CALayer
‣ ���Thread,�Handler,�Executors…�(==�GCD
‣ ���메모리�관리
‣ ���Optimizing,�Performance
LifecycleViewController의�생명주기
어디서�많이�본�것�같은�생명주기
LifecycleViewController의�생명주기
LifecycleViewController의�생명주기
onCreate()
LifecycleViewController의�생명주기
onResume()
onStart()
LifecycleViewController의�생명주기
onLowMemory()
LifecycleViewController의�생명주기
onStop()
onPause()
LifecycleViewController의�생명주기
onDestory()
LifecycleViewController의�생명주기
정확히�일치하는�것은�아니나�맥락은�비슷하다
GCD
Grand�Central�DispatchGCD�provides�and�manages�FIFO�queues�to�which�your�application�can�submit�tasks�in�the�form�of�block�objects.�Work�submitted�to�dispatch�queues�are�executed�on�a�pool�of�threads�fully�managed�by�the�system.�No�guarantee�is�made�as�to�the�thread�on�which�a�task�executes.�
Synchronous�and�Asynchronous�Execution�Each�work�item�can�be�executed�either�synchronously�or�asynchronously.�When�a�work�item�is�executed�synchronously�with�the�sync�method,�the�program�waits�until�execution�finishes�before�the�method�call�returns.�When�a�work�item�is�executed�asynchronously�with�the�async�method,�the�method�call�returns�immediately.�
Serial�and�Concurrent�Queues�A�dispatch�queue�can�be�either�serial,�so�that�work�items�are�executed�one�at�a�time,�or�it�can�be�concurrent,�so�that�work�items�are�dequeued�in�order,�but�run�all�at�once�and�can�finish�in�any�order.�Both�serial�and�concurrent�queues�process�work�items�in�first�in,�first-out�(FIFO)�order.�
System-Provided�Queues�When�an�app�launches,�the�system�automatically�creates�a�special�queue�called�the�main�queue.�Work�items�enqueued�to�the�main�queue�execute�serially�on�your�app’s�main�thread.�You�can�access�the�main�queue�using�the�main�type�property.
GCD
Grand�Central�DispatchGCD�provides�and�manages�FIFO�queues�to�which�your�application�can�submit�tasks�in�the�form�of�block�objects.�Work�submitted�to�dispatch�queues�are�executed�on�a�pool�of�threads�fully�managed�by�the�system.�No�guarantee�is�made�as�to�the�thread�on�which�a�task�executes.�
Synchronous�and�Asynchronous�Execution�Each�work�item�can�be�executed�either�synchronously�or�asynchronously.�When�a�work�item�is�executed�synchronously�with�the�sync�method,�the�program�waits�until�execution�finishes�before�the�method�call�returns.�When�a�work�item�is�executed�asynchronously�with�the�async�method,�the�method�call�returns�immediately.�
Serial�and�Concurrent�Queues�A�dispatch�queue�can�be�either�serial,�so�that�work�items�are�executed�one�at�a�time,�or�it�can�be�concurrent,�so�that�work�items�are�dequeued�in�order,�but�run�all�at�once�and�can�finish�in�any�order.�Both�serial�and�concurrent�queues�process�work�items�in�first�in,�first-out�(FIFO)�order.�
System-Provided�Queues�When�an�app�launches,�the�system�automatically�creates�a�special�queue�called�the�main�queue.�Work�items�enqueued�to�the�main�queue�execute�serially�on�your�app’s�main�thread.�You�can�access�the�main�queue�using�the�main�type�property.
그렇답니다.
‣ ���어셈블리�최적화를�통해�빠른�성능�제공
‣ ���스레드�생성과�관리를�안정적으로�처리
GCDGCD는�스레드를�직접�다루지�않고�동시성�문제를�처리하는�기술
‣ ���스레드�풀을�통해�스레드를�재사용�
‣ ���동기화�문제를�유연하게�처리
‣ ���메모리를�효율적으로�사용
‣ ���직관적이고�단순한�API
GCDGCD는�스레드를�직접�다루지�않고�동시성�문제를�처리하는�기술
‣ ���어셈블리�최적화를�통해�빠른�성능�제공
‣ ���스레드�생성과�관리를�안정적으로�처리
‣ ���스레드�풀을�통해�스레드를�재사용�
‣ ���동기화�문제를�유연하게�처리
‣ ���메모리를�효율적으로�사용
‣ ���직관적이고�단순한�API
GCD
func�removeVideoWithPaths(videoPathArray:�[String])�{���������DispatchQueue.global().async�{��[unowned�self]�in�������������for�path�in�videoPathArray�{�����������������self.dataManager.removeVideoWithPath(atPath:�path)�������������}���������}�����}
특정�Task를�백그라운드�스레드에서�실행하는�패턴
GCD특정�Task를�백그라운드�스레드에서�실행하는�패턴
func�removeVideoWithPaths(videoPathArray:�[String])�{���������DispatchQueue.global().async�{��[unowned�self]�in�������������for�path�in�videoPathArray�{�����������������self.dataManager.removeVideoWithPath(atPath:�path)�������������}���������}�����}
유연!�직관!�단순!
GCD백그라운드에서�작업을�실행한�후�UI를�업데이트하는�패턴
DispatchQueue.global().async�{��������//�Background�Task���������DispatchQueue.main.async�{���
//�UI�Update�Task�}�
}
GCD백그라운드에서�작업을�완료하고�Callback을�받는�패턴
func�backgroundTask(completeHandler:�@escaping�()�->�Void)�{�����DispatchQueue.global().async�{���������completeHandler()�����}�}
GCD백그라운드에서�작업을�완료하고�Callback을�받는�패턴
func�backgroundTask(completeHandler:�@escaping�()�->�Void)�{�����DispatchQueue.global().async�{���������completeHandler()�����}�}
closure를�인자로�받는�함수가�반환된�이후�해당�closure가�호출�되는�경우
closure가�함수에서�벗어나는�것을�허용한다는�것을�나타냄
GCD백그라운드에서�작업을�완료하고�Callback을�받는�패턴
func�backgroundTask(completeHandler:�@escaping�()�->�Void)�{�����DispatchQueue.global().async�{���������completeHandler()�����}�}
closure를�인자로�받는�함수가�반환된�이후�해당�closure가�호출�되는�경우
closure가�함수에서�벗어나는�것을�허용한다는�것을�나타냄
xcode�8�beta�6부터�@noescape가�기본값
이전에는�@escaping이�기본값
GCD백그라운드에서�작업을�완료하고�Callback을�받는�패턴
func�backgroundTask(completeHandler:�@escaping�()�->�Void)�{�����DispatchQueue.global().async�{���������completeHandler()�����}�}
closure를�인자로�받는�함수가�반환된�이후�해당�closure가�호출�되는�경우
closure가�함수에서�벗어나는�것을�허용한다는�것을�나타냄
xcode�8�beta�6부터�@noescape가�기본값
이전에는�@escaping이�기본값
@noescape�클로저는�변수�안에�저장할�수�없으며�당연히�함수에서�벗어날�수�없다.
GCD
var�receivedChickenList�=�[String]()�let��receivedChickenListSemaphore�=�DispatchSemaphore(value:�1)
for�_�in�1...10�{�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SpicyFriedChicken")�������������}�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SoyChicken")�������������}�}
func�sendChicken(chickenName:�String)�{���������receivedChickenListSemaphore.wait()���������receivedChickenList.append(chickenName)���������receivedChickenListSemaphore.signal()�}
세마포어�or�뮤텍스를�사용하는�패턴
GCD
var�receivedChickenList�=�[String]()�let��receivedChickenListSemaphore�=�DispatchSemaphore(value:�1)
for�_�in�1...10�{�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SpicyFriedChicken")�������������}�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SoyChicken")�������������}�}
func�sendChicken(chickenName:�String)�{���������receivedChickenListSemaphore.wait()���������receivedChickenList.append(chickenName)���������receivedChickenListSemaphore.signal()�}
세마포어�or�뮤텍스를�사용하는�패턴
GCD
var�receivedChickenList�=�[String]()�let��receivedChickenListSemaphore�=�DispatchSemaphore(value:�1)
for�_�in�1...10�{�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SpicyFriedChicken")�������������}�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SoyChicken")�������������}�}
func�sendChicken(chickenName:�String)�{���������receivedChickenListSemaphore.wait()���������receivedChickenList.append(chickenName)���������receivedChickenListSemaphore.signal()�}
Critical�Section
세마포어�or�뮤텍스를�사용하는�패턴
GCD
var�receivedChickenList�=�[String]()�let��receivedChickenListSemaphore�=�DispatchSemaphore(value:�1)
for�_�in�1...10�{�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SpicyFriedChicken")�������������}�������������DispatchQueue.global().async�{�����������������self.sendChicken(chickenName:�"SoyChicken")�������������}�}
func�sendChicken(chickenName:�String)�{���������receivedChickenListSemaphore.wait()���������receivedChickenList.append(chickenName)���������receivedChickenListSemaphore.signal()�}
성능저하요소�조심해서�사용
세마포어�or�뮤텍스를�사용하는�패턴
GCD지정�시간만큼�지연시킨�후�실행하는�패턴
let��delay�=�DispatchTime.now()�+�3
DispatchQueue.global().asyncAfter(deadline:�delay)�{�����print("goni")�}
let��repeat�=�5
DispatchQueue.concurrentPerform(iterations:�repeat)�{�i�in�����print(“goni�\(i)”)�}
지정�횟수만큼�Dispatch�Queue에�추가하는�패턴
ㅋ
GCD지정�시간만큼�지연시킨�후�실행하는�패턴
let��repeat�=�5
DispatchQueue.concurrentPerform(iterations:�repeat)�{�i�in�����print(“goni�\(i)”)�}
ㅋ
5을�전달하면�0,�1,�2,�3,�4�가�반복상수로�전달
지정�횟수만큼�Dispatch�Queue에�추가하는�패턴
let��delay�=�DispatchTime.now()�+�3
DispatchQueue.global().asyncAfter(deadline:�delay)�{�����print("goni")�}
GCD지정�시간만큼�지연시킨�후�실행하는�패턴
let��repeat�=�5
DispatchQueue.concurrentPerform(iterations:�repeat)�{�i�in�����print(“goni�\(i)”)�}
ㅋ
실행�순서는�보장되지�않음
5을�전달하면�0,�1,�2,�3,�4�가�반복상수로�전달
지정�횟수만큼�Dispatch�Queue에�추가하는�패턴
let��delay�=�DispatchTime.now()�+�3
DispatchQueue.global().asyncAfter(deadline:�delay)�{�����print("goni")�}
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
그룹�생성
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
그룹에�포함된�작업
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
그룹에�포함된�모든�작업의�실행이�완료되는�시점이�그룹의�실행이�완료된�시점
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
goniGroup.wait()
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
goniGroup.wait() 그룹에�포함된�모든�작업이�완료될�때까지�기다림
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
goniGroup.wait()
let��latency�=�10
let��result�=�goniGroup.wait(timeout:�DispatchTime(uptimeNanoseconds:�latency))
switch�result�{�case�.success:�����print("success")�case�.timedOut:�����print("timeOut")�}
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
goniGroup.wait()
let��latency�=�10
let��result�=�goniGroup.wait(timeout:�DispatchTime(uptimeNanoseconds:�latency))
switch�result�{�case�.success:�����print("success")�case�.timedOut:�����print("timeOut")�}
만료�시간을�직접�지정
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
GCDDispatch�Queue에�추가된�작업을�그룹으로�관리하는�패턴
let��goniGroup�=�DispatchGroup()
DispatchQueue.global().asyncAfter(group:�goniGroup)�{�����self.additionalTask()�}
goniGroup.wait()
let��latency�=�10
let��result�=�goniGroup.wait(timeout:�DispatchTime(uptimeNanoseconds:�latency))
switch�result�{�case�.success:�����print("success")�case�.timedOut:�����print("timeOut")�}
그룹�작업�완료�여부
Reference
Automatic�Reference�CountingSwift�uses�Automatic�Reference�Counting�(ARC)�to�track�and�manage�your�app’s�memory�usage.�In�most�cases,�this�means�that�memory�management�“just�works”�in�Swift,�and�you�do�not�need�to�think�about�memory�management�yourself.�ARC�automatically�frees�up�the�memory�used�by�class�instances�when�those�instances�are�no�longer�needed.�However,�in�a�few�cases�ARC�requires�more�information�about�the�relationships�between�parts�of�your�code�in�order�to�manage�memory�for�you.This�chapter�describes�those�situations�and�shows�how�you�enable�ARC�to�manage�all�of�your�app’s�memory.�Using�ARC�in�Swift�is�very�similar�to�the�approach�described�in�Transitioning�to�ARC�Release�Notes�for�using�ARC�with�Objective-C.�
Reference
Automatic�Reference�CountingSwift�uses�Automatic�Reference�Counting�(ARC)�to�track�and�manage�your�app’s�memory�usage.�In�most�cases,�this�means�that�memory�management�“just�works”�in�Swift,�and�you�do�not�need�to�think�about�memory�management�yourself.�ARC�automatically�frees�up�the�memory�used�by�class�instances�when�those�instances�are�no�longer�needed.�However,�in�a�few�cases�ARC�requires�more�information�about�the�relationships�between�parts�of�your�code�in�order�to�manage�memory�for�you.This�chapter�describes�those�situations�and�shows�how�you�enable�ARC�to�manage�all�of�your�app’s�memory.�Using�ARC�in�Swift�is�very�similar�to�the�approach�described�in�Transitioning�to�ARC�Release�Notes�for�using�ARC�with�Objective-C.�
가끔씩�장문이�튀어나오는�이유
Reference
Automatic�Reference�CountingSwift�uses�Automatic�Reference�Counting�(ARC)�to�track�and�manage�your�app’s�memory�usage.�In�most�cases,�this�means�that�memory�management�“just�works”�in�Swift,�and�you�do�not�need�to�think�about�memory�management�yourself.�ARC�automatically�frees�up�the�memory�used�by�class�instances�when�those�instances�are�no�longer�needed.�However,�in�a�few�cases�ARC�requires�more�information�about�the�relationships�between�parts�of�your�code�in�order�to�manage�memory�for�you.This�chapter�describes�those�situations�and�shows�how�you�enable�ARC�to�manage�all�of�your�app’s�memory.�Using�ARC�in�Swift�is�very�similar�to�the�approach�described�in�Transitioning�to�ARC�Release�Notes�for�using�ARC�with�Objective-C.�
가끔씩�장문이�튀어나오는�이유
사용법은�java.lang.ref�패키지와�비슷하다
ReferenceStrong�Reference�Cycles�Between�Class�Instances
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����var�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����var�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
ReferenceStrong�Reference�Cycles�Between�Class�Instances
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����var�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
ReferenceStrong�Reference�Cycles�Between�Class�Instances
Person�인스턴스는�nil로�초기화되는�옵셔널�apartment�속성을�가짐
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����var�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
ReferenceStrong�Reference�Cycles�Between�Class�Instances
ReferenceStrong�Reference�Cycles�Between�Class�Instances
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����var�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
Apartment�인스턴스는�nil로�초기화되는�옵셔널�tenant�속성을�가짐
ReferenceStrong�Reference�Cycles�Between�Class�Instances
var�john:�Person?�var�number73:�Apartment?
ReferenceStrong�Reference�Cycles�Between�Class�Instances
var�john:�Person?�var�number73:�Apartment?
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
ReferenceStrong�Reference�Cycles�Between�Class�Instances
각각의�인스턴스를�만들고�변수에�할당
var�john:�Person?�var�number73:�Apartment?
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
ReferenceStrong�Reference�Cycles�Between�Class�Instances
var�john:�Person?�var�number73:�Apartment?
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
두�인스턴스가�만들어지고�할당�된�후�강한�참조�발생
ReferenceStrong�Reference�Cycles�Between�Class�Instances
var�john:�Person?�var�number73:�Apartment?
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john�변수는�새로운�Person�인스턴스에�강한�참조�
number73�변수는�새로운�Apartment�인스턴스에�강한�참조
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
두�인스턴스를�연결하도록�Person는�apartment를�가지고
apartment는�tenant를�가지도록�한다
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
두�인스턴스를�서로�연결한�후�강한�참조의�모습
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
즉�두�인스턴스�사이에�강한�참조�순환이�일어난다
Person�인스턴스는�Apartment�인스턴스를�강한�참조
Apartment�인스턴스는�Person�인스턴스를�강한�참조
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
john�=�nil�number73�=�nil
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
john�=�nil�number73�=�nil
john과�number73�변수의�강한�참조를�끊을�때
ARC는�인스턴스의�할당을�해체하지�않는다.
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
john�=�nil�number73�=�nil
강한�참조�순환�때문에�Person와�Apartment�인스턴스의�참조�계수가�0이�아니기�때문
ReferenceStrong�Reference�Cycles�Between�Class�Instances
john�=�Person(name:�"John�Appleseed")�number73�=�Apartment(number:�73)
john!.apartment�=�number73�number73.tenant�=�john
john�=�nil�number73�=�nil
강한�참조�순환�때문에�Person와�Apartment�인스턴스의�참조�계수가�0이�아니기�때문
메모리�누수�현장
안드로이드에서도�이런�일이�빈번한�거�같은데..
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
안드로이드에서�Weak�References를�사용한�사례
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
‣ ���Message는�Target�Handler를�강한�참조
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
‣ ���Message는�Target�Handler를�강한�참조
‣ ���Activity가�종료되어도�MessageQueue에�Message가�있다면
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
‣ ���Message는�Target�Handler를�강한�참조
‣ ���Handler�는�GC�되지�않음
‣ ���Activity가�종료되어도�MessageQueue에�Message가�있다면
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
‣ ���Message는�Target�Handler를�강한�참조
‣ ���Handler�는�GC�되지�않음
‣ ���자연스레�Handler�가�참조하는�Activity�도�GC�가�되지�않음
‣ ���Activity가�종료되어도�MessageQueue에�Message가�있다면
ReferenceWeak�References
‣ ���MessageQueue�에�있는�Message
‣ ���Message는�Target�Handler를�강한�참조
‣ ���Handler�는�GC�되지�않음
‣ ���Handler�관련��Message가�모두�소비가�되고�나서야�GC
‣ ���자연스레�Handler�가�참조하는�Activity�도�GC�가�되지�않음
‣ ���Activity가�종료되어도�MessageQueue에�Message가�있다면
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
Activity에�대해�강한�참조가�아니라�약한�참조를�한다
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
Activity에�대해�강한�참조가�아니라�약한�참조를�한다
Message�Queue에�해당�handler�관련된�message�가�있어도,�Activity�는�GC
ReferenceWeak�References
public�class�WeakHandler�extends�Handler�{����private�final�WeakReference<HandlerMessage>�mActivity;����public�WeakHandler(HandlerMessage�activity)�{��������mActivity�=�new�WeakReference<HandlerMessage>(activity); ����}�����@Override����public�void�handleMessage(Message�msg)�{��������super.handleMessage(msg);��������HandlerMessage�activity�=�(HandlerMessage)�mActivity.get(); ��������if�(�activity�==�null�)�{�return;�} ��������activity.handleMessage(msg);����}}
Activity에�대해�강한�참조가�아니라�약한�참조를�한다
Message�Queue에�해당�handler�관련된�message�가�있어도,�Activity�는�GC
약한�참조로�메모리�누수�문제�해결
ReferenceWeak�References
관련�지식을�더�알고�싶다면...
&
D2�Java�Reference와�GC
객체�사용후�null�할당!
Java:�difference�between�strong/soft/weak/phantom�reference
다시�스위프트로�돌아갑시다
ReferenceWeak�References
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����weak�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����weak�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
ReferenceWeak�References
약한�참조는�참조한�다른�인스턴스는�참조�계수가�증가하지�않는다
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����weak�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
ReferenceWeak�References
약한�참조는�참조한�다른�인스턴스는�참조�계수가�증가하지�않는다
약한�참조하는�인스턴스는�옵셔널�타입
ReferenceWeak�References
class�Person�{�����let�name:�String�����init(name:�String)�{�self.name�=�name�}�����var�apartment:�Apartment?�����deinit�{�println(“\(name)�is�being�deinitialized")�}�}���class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����weak�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
약한�참조는�참조한�다른�인스턴스는�참조�계수가�증가하지�않는다
약한�참조하는�인스턴스는�옵셔널�타입
ARC는�인스턴스�참조가�할당�해제될�때,�자동적으로�약한�참조는�nil로�설정
ReferenceWeak�References
class�Apartment�{�����let�number:�Int�����init(number:�Int)�{�self.number�=�number�}�����weak�tenant:�Person?�����deinit�{�println("Apartment�#(number)�is�being�deinitialized")�}�}
Person�인스턴스는�강한�참조하는�Apartment�인스턴스를�가지지만
Apartment�인스턴스는�약한�참조하는�Person�인스턴스�가짐
ReferenceWeak�References
john�=�nil
ReferenceWeak�References
john�=�nil
Person�인스턴스에�대한�강한�참조가�더이상�없기에�인스턴스는�할당�해제
ReferenceWeak�References
john�=�nil
Person�인스턴스에�대한�강한�참조가�더이상�없기에�인스턴스는�할당�해제
약한�참조로�강한�참조�순환�해결
Reference
ARC�and�Memory�Management�in�Swift
Automatic�Reference�Counting
관련�지식을�더�알고�싶다면...
개발�시작
Architecture�Pattern
‣ ���MVC
‣ ���MVP
‣ ���MVVM
‣ ���VIPER
Architecture�Pattern
‣ ���MVC
‣ ���MVP
‣ ���MVVM
‣ ���VIPER
‣ ���시간은�없는데�너무�많다.
Architecture�Pattern
‣ ���MVC
‣ ���MVP
‣ ���MVVM
‣ ���VIPER
안드로이드에서�해본�거
‣ ���시간은�없는데�너무�많다.
Architecture�Pattern무작정�MVP
Android�MVP�=�RxJava�&�RxAndroid�+�Dagger�2�+�Retrofit�2�+�Butterknife...�
IOS�MVP�=�RxSwift�&�RxCocoa�+�Swinject�&��SwinjectStoryboard�+�Alamofire...�
대략�이렇게�구성
Architecture�Pattern무작정�MVP
Android�MVP�=�RxJava�&�RxAndroid�+�Dagger�2�+�Retrofit�2�+�Butterknife...�
IOS�MVP�=�RxSwift�&�RxCocoa�+�Swinject�&��SwinjectStoryboard�+�Alamofire...�
대략�이렇게�구성
네트워크는�사용하지�않았음
Architecture�Pattern무작정�MVP
Architecture�Pattern무작정�MVP
View:�일반적으로�ViewController�Presenter에서�받은�데이터를�사용자에게�표시하는�역할�사용자와�상호�작용이나�입력을�처리하고,�필요에�따라�Presenter에�위임
Architecture�Pattern무작정�MVP
View:�일반적으로�ViewController�Presenter에서�받은�데이터를�사용자에게�표시하는�역할�사용자와�상호�작용이나�입력을�처리하고,�필요에�따라�Presenter에�위임
Presenter:�DataManager에서�제공하는�Observable을�구독���DataManager에서�반환한�데이터를�적절하게�처리하고�이를�표시하기�위해���View에서�필요한�함수�호출
Architecture�Pattern무작정�MVP
View:�일반적으로�ViewController�Presenter에서�받은�데이터를�사용자에게�표시하는�역할�사용자와�상호�작용이나�입력을�처리하고,�필요에�따라�Presenter에�위임
Presenter:�DataManager에서�제공하는�Observable을�구독���DataManager에서�반환한�데이터를�적절하게�처리하고�이를�표시하기�위해���View에서�필요한�함수�호출
DataManager:�아키텍처의�핵심부분,�모든�서비스�or�헬퍼에�대한�참조를�가지고�있으며�
서비스�or�헬퍼의�데이터를�결합,�변환,�필터링하여�Presenter에서�원하는�출력을�생성
이를�사용하여�Presenter의�요청에�대응
Architecture�Pattern무작정�MVP
View:�일반적으로�ViewController�Presenter에서�받은�데이터를�사용자에게�표시하는�역할�사용자와�상호�작용이나�입력을�처리하고,�필요에�따라�Presenter에�위임
Presenter:�DataManager에서�제공하는�Observable을�구독���DataManager에서�반환한�데이터를�적절하게�처리하고�이를�표시하기�위해���View에서�필요한�함수�호출
DataManager:�아키텍처의�핵심부분,�모든�서비스�or�헬퍼에�대한�참조를�가지고�있으며�
서비스�or�헬퍼의�데이터를�결합,�변환,�필터링하여�Presenter에서�원하는�출력을�생성
이를�사용하여�Presenter의�요청에�대응
Model:�API,�DB�접근,�특정�비지니스�로직�등�다양하며�DataManager에�의해�사용
Architecture�Pattern무작정�MVP�-�View
protocol�ShareMvpView:�Mvpview�{����������func�playVideo(mergedVideofileUrl:�URL?)����������func�createActivityIndicatory(uiView:�UIView)�->�(UIActivityIndicatorView,�UIView)����������func�dimissShareViewController()����������func�showCompleteDialog()����������func�showShareSheet(videoUrl:�URL)����������func�enabledSaveButton(isEnabled:�Bool)�}
ViewController에�구현될�프로토콜
Presenter에서�View�갱신을�위해�작성된�함수
Architecture�Pattern무작정�MVP�-�View
ViewController에�ShareMvpView�프로토콜�구현
extension�ShareViewController:�ShareMvpView�{
//......��
func�dimissShareViewController()�{���������self.dismiss(animated:�true,�completion:�nil);�}�
func�showCompleteDialog()�{���������self.present(PopupDialog(title:�"Save�is�complete.",�message:�nil),��animated:�true,�completion:�nil)�}�����}
Architecture�Pattern무작정�MVP�-�View
ViewController에�ShareMvpView�프로토콜�구현
extension�ShareViewController:�ShareMvpView�{
//......��
func�dimissShareViewController()�{���������self.dismiss(animated:�true,�completion:�nil);�}�
func�showCompleteDialog()�{���������self.present(PopupDialog(title:�"Save�is�complete.",�message:�nil),��animated:�true,�completion:�nil)�}�����}
구현된�함수는��View갱신을�위해�Presenter에서�호출�된다.
Architecture�Pattern
let�saveButtonEvent�=�saveButton.rx.controlEvent(UIControlEvents.touchUpInside)�presenter.saveButtonClickEvent(event:�saveButtonEvent)����������let�homeButtonEvent�=�homeButton.rx.controlEvent(UIControlEvents.touchUpInside)�presenter.homeButtonClickEvent(event:�homeButtonEvent)����������let�shareButtonEvent�=�shareButton.rx.controlEvent(UIControlEvents.touchUpInside)�presenter.shareButtonClickEvent(event:�shareButtonEvent)
무작정�MVP�-�View
View에서�발생하는�유저액션을�Presenter에�위임합니다.
Architecture�Pattern무작정�MVP�-�Presenter
func�homeButtonClickEvent(event:�ControlEvent<Void>)�{���������event.debounce(0.2,�scheduler:�MainScheduler.instance)�������������.bindNext�{�����������������self.view?.dimissShareViewController()���������}.addDisposableTo(bag)�}
func�saveButtonClickEvent(event:�ControlEvent<Void>)�{���������event.debounce(0.2,�scheduler:�MainScheduler.instance)�������������.bindNext�{�����������������self.saveVideoWithURL()�{�
�����self.view?.showCompleteDialog()�����������������}���������}.addDisposableTo(bag)�}
유저액션을�위임�받은�Presenter
Architecture�Pattern무작정�MVP�-�Presenter
func�homeButtonClickEvent(event:�ControlEvent<Void>)�{���������event.debounce(0.2,�scheduler:�MainScheduler.instance)�������������.bindNext�{�����������������self.view?.dimissShareViewController()���������}.addDisposableTo(bag)�}
func�saveButtonClickEvent(event:�ControlEvent<Void>)�{���������event.debounce(0.2,�scheduler:�MainScheduler.instance)�������������.bindNext�{�����������������self.saveVideoWithURL()�{�
�����self.view?.showCompleteDialog()�����������������}���������}.addDisposableTo(bag)�}
유저액션을�위임�받은�Presenter
필요한�로직을�수행하고�View�update를�위해�ShareMvpView�의�함수�호출
Architecture�Pattern무작정�MVP�-�DataManager
class�DataMenager�{�����//.....�����private�let�musicService:�MusicService�����private�let�videoService:�VideoService������//�@inject�����init(musicService:�MusicService,�videoService:�VideoService)�{�
�self.musicService�=��musicService��self.videoService�=��videoService�
}�
����public�func�getMusics()�->�Observable<MPMediaItem>�{�������return�musicService.getMusics()�
����}�����//.....��}
Architecture�Pattern무작정�MVP�-�DataManager
class�DataMenager�{�����//.....�����private�let�musicService:�MusicService�����private�let�videoService:�VideoService������//�@inject�����init(musicService:�MusicService,�videoService:�VideoService)�{�
�self.musicService�=��musicService��self.videoService�=��videoService�
}�
����public�func�getMusics()�->�Observable<MPMediaItem>�{�������return�musicService.getMusics()�
����}�����//.....��}
모든�서비스에�대한�참조를�가짐
Architecture�Pattern무작정�MVP�-�DataManager
class�DataMenager�{�����//.....�����private�let�musicService:�MusicService�����private�let�videoService:�VideoService������//�@inject�����init(musicService:�MusicService,�videoService:�VideoService)�{�
�self.musicService�=��musicService��self.videoService�=��videoService�
}�
����public�func�getMusics()�->�Observable<MPMediaItem>�{�������return�musicService.getMusics()�
����}�����//.....��}
모든�서비스에�대한�참조를�가짐
Presenter에서�원하는�출력(데이터)을�서비스에�요청하여�반환
Architecture�Pattern무작정�MVP�-�Model
class�VideoService�{�����//.....�����public�func�getLastVideoThumbnail()�->�Observable<UIImage>�{���������return�Observable<UIImage>.create{�observableOfUIImage�in�������������if�let�lastVideoPHAsset�=�self.getLastPhAsset()�{�����������������PHImageManager..�{�image,�_�in���������������������if�let�image�=�image�{�������������������������observableOfUIImage.on(Event.next(image))�������������������������observableOfUIImage.on(Event.completed)���������������������}�else�{�������������������������observableOfUIImage.on(Event.error(NSError(domain:�“video..”���������������������������������������������}�����������������}�������������}�������������return�Disposables.create()���������}�����}�}
Architecture�Pattern무작정�MVP�-�Model
class�VideoService�{�����//.....�����public�func�getLastVideoThumbnail()�->�Observable<UIImage>�{���������return�Observable<UIImage>.create{�observableOfUIImage�in�������������if�let�lastVideoPHAsset�=�self.getLastPhAsset()�{�����������������PHImageManager..�{�image,�_�in���������������������if�let�image�=�image�{�������������������������observableOfUIImage.on(Event.next(image))�������������������������observableOfUIImage.on(Event.completed)���������������������}�else�{�������������������������observableOfUIImage.on(Event.error(NSError(domain:�“video..”���������������������������������������������}�����������������}�������������}�������������return�Disposables.create()���������}�����}�}
DataManager에서�요청하는�데이터에�대한�Observable을�반환
Permission권한�처리
func�showAlert(_�permission:�Permission,�_�title:�String)�{�������������let�alert�=�permission.prePermissionAlert�������������alert.title�=�"Please�allow�access�to�your�\(title)"�������������alert.message�=�nil�������������alert.cancel�=�"Cancel"�������������alert.settings�=�"Settings"���������}����
Permission권한�처리
func�showAlert(_�permission:�Permission,�_�title:�String)�{�������������let�alert�=�permission.prePermissionAlert�������������alert.title�=�"Please�allow�access�to�your�\(title)"�������������alert.message�=�nil�������������alert.cancel�=�"Cancel"�������������alert.settings�=�"Settings"���������}����
권한을�요청하고�설정화면으로�이동시키는�Alert�코드
Permission권한�처리
권한을�요청하고�거부하면�재요청�반복,�허가하면�다음�권한을�요청
이후�모든�권한이�허가되면�인트로�화면에서�이동하는�재귀호출�코드
func�requestAccess(_�permission:�Permission,�title:�String,�_�complete:�@escaping�()�->�Void)�{�����������������showAlert(permission,�title)�����������������permission.request{�status�in���������������������switch�status�{���������������������case�.authorized:�������������������������complete()�������������������������break���������������������default:�������������������������requestAccess(permission,�title:�title,�complete)���������������������}�����������������}�������������}
Permission권한�처리
권한을�요청하고�거부하면�재요청�반복,�허가하면�다음�권한을�요청
이후�모든�권한이�허가되면�인트로�화면에서�이동하는�재귀호출�코드
func�requestAccess(_�permission:�Permission,�title:�String,�_�complete:�@escaping�()�->�Void)�{�����������������showAlert(permission,�title)�����������������permission.request{�status�in���������������������switch�status�{���������������������case�.authorized:�������������������������complete()�������������������������break���������������������default:�������������������������requestAccess(permission,�title:�title,�complete)���������������������}�����������������}�������������}
요청�거부시�재요청
Permission권한�처리
권한을�요청하고�거부하면�재요청�반복,�허가하면�다음�권한을�요청
이후�모든�권한이�허가되면�인트로�화면에서�이동하는�재귀호출�코드
func�requestAccess(_�permission:�Permission,�title:�String,�_�complete:�@escaping�()�->�Void)�{�����������������showAlert(permission,�title)�����������������permission.request{�status�in���������������������switch�status�{���������������������case�.authorized:�������������������������complete()�������������������������break���������������������default:�������������������������requestAccess(permission,�title:�title,�complete)���������������������}�����������������}�������������}
함수는�레퍼런스
기저사례�
허가해주면�완료�함수�호출
Permission권한�처리
권한을�요청하고�거부하면�재요청�반복,�허가하면�다음�권한을�요청
이후�모든�권한이�허가되면�인트로�화면에서�이동하는�재귀호출�코드
requestAccess(.camera,�title:�"camera")�{�������������requestAccess(.mediaLibrary,�title:�"mediaLibrary")�{�����������������requestAccess(.microphone,�title:�"microphone")�{���������������������requestAccess(.photos,�title:�"photos")�{�������������������������DispatchQueue.main.async�{����������������������������self.present(self.mainViewController,�animated:�true,�completion:�nil)�������������������������}���������������������}�����������������}�������������}���������}
Permission권한�처리
권한을�요청하고�거부하면�재요청�반복,�허가하면�다음�권한을�요청
이후�모든�권한이�허가되면�인트로�화면에서�이동하는�재귀호출�코드
requestAccess(.camera,�title:�"camera")�{�������������requestAccess(.mediaLibrary,�title:�"mediaLibrary")�{�����������������requestAccess(.microphone,�title:�"microphone")�{���������������������requestAccess(.photos,�title:�"photos")�{�������������������������DispatchQueue.main.async�{����������������������������self.present(self.mainViewController,�animated:�true,�completion:�nil)�������������������������}���������������������}�����������������}�������������}���������}
해괴하게�작성해서�죄송합니다
‣ ��SwinjectStoryboard�등�IOS�확장�가능
SwInjectDependency�Injection�library
‣ ��Dagger2�보다�직관적
‣ ��그래프를�통해�의존성�주입
https://github.com/Swinject/Swinject
RxSwiftReactive�Programming�library
‣ ��기존�Rx유저라면�빠르게�적응
‣ ��손쉬운�동시성과�비동기�실행
‣ ��콜백�지옥에서�벗어나기�위한�방법
https://github.com/ReactiveX/RxSwift
‣ ��Swift�커뮤니티나�Apple�규칙�반영
‣ ��선호하는�규칙을�세밀하게�반영�가능
‣ ��코딩�스타일에�대한�일관성�유지
Swift�Lintlinter�library
https://github.com/realm/SwiftLint
앞으로�공부할�것들
VIPERClean�Architecture
‣ ���View:�displays�what�it�is�told�to�by�the�Presenter�and�relays�user�input�back�to�the�Presenter.
‣ ���Interactor:�contains�the�business�logic�as�specified�by�a�use�case.
‣ ���Presenter:�contains�view�logic�for�preparing�content�for�display�(as�received�from�the�Interactor)�and�for�reacting�to�user�inputs�(by�requesting�new�data�from�the�Interactor).
‣ ���Entity:�contains�basic�model�objects�used�by�the�Interactor.
‣ ���Routing:�contains�navigation�logic�for�describing�which�screens�are�shown�in�which�order.
https://www.objc.io/issues/13-architecture/viper/
POPProtocol�oriented�programming
�•�범용적인�사용�� ◦�클래스,�구조체,�열거형�등등�모든�타입에�적용�가능�� ◦�제네릭과�결합하면�더욱�파급적인�효과�
�•�상속의�한계�극복�� ◦�특정�상속�체계에�종속되지�않음�� ◦�프레임워크에�종속적이지�않게�재활용�가능�
�•�적은�시스템�비용�� ◦�Reference�type�cost�>�Value�type�cost�
�•�용이한�테스트�� ◦�GUI�코드�없이도�수월한�테스트�
https://realm.io/kr/news/protocol-oriented-programming-in-swift/
Swift�CompilerLLVM�&�Swiftc
�•�LLVM�� ◦�LLVM이란�Low-Level�Virtual�Machine�� ◦�Chris�Arthur�Lattner의�2002년�석사�논문에서�시작�� ◦�오픈�소스로�컴파일러를�제작�� ◦�Apple에서�후원한�컴파일러�� ◦�2007년도에�Apple에�팀�합병
�•�Swiftc�� ◦�LLVM과�거의�유사하나�최적화를�하는�2단계�더�있음�� ◦�프론트엔드는�swift�파일을�읽에서�토큰�처리와�의미분석�
중간언어�생성�및�타입�검사를�함�� ◦�중간�언어�최적화는�SIL�수준에서�분석과�변환,�ARC�처리�
와�Generic�코드�타입�지정�� ◦이후�LLVM�IR�수준을�최적화하고�타깃�기계에�맞는�binary�������생성
https://realm.io/kr/news/swift-internals-llvm-type-system-swift-foundation/
마지막으로IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
마지막으로
이번�개발은�안드로이드에서의�경험에�의존하여�비전문적(야매)으로�진행되었습니다.
경험이�큰�도움이�되긴�했지만,�진행속도에�맞추기�위해�얕게�공부한�부분을�다시�공부해야�합니다.
IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
마지막으로
이번�개발은�안드로이드에서의�경험에�의존하여�비전문적(야매)으로�진행되었습니다.
1년간�안드로이드와�마음이�많이�멀어져�있었는데�이번�계기로�더욱�멀어졌습니다.
Swift�&�IOS�정말�좋음!��
경험이�큰�도움이�되긴�했지만,�진행속도에�맞추기�위해�얕게�공부한�부분을�다시�공부해야�합니다.
IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
마지막으로
개인적으로�Realm의�Let�Swift!�세션에서�많은�도움을�받았습니다.
이번�개발은�안드로이드에서의�경험에�의존하여�비전문적(야매)으로�진행되었습니다.
1년간�안드로이드와�마음이�많이�멀어져�있었는데�이번�계기로�더욱�멀어졌습니다.
경험이�큰�도움이�되긴�했지만,�진행속도에�맞추기�위해�얕게�공부한�부분을�다시�공부해야�합니다.
IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
Swift�&�IOS�정말�좋음!��
특히�������������������������������������는�스위프트를�사용한다면�한�번쯤�읽어보시면�좋을�것�같습니다.스위프트�성능�이해하기
마지막으로
빨리�제가�Swift로�mac�용�앱을�개발하는�날이�오면�좋겠습니다.�(모바일개발을�벗어나고�싶음)
개인적으로�Realm의�Let�Swift!�세션에서�많은�도움을�받았습니다.
이번�개발은�안드로이드에서의�경험에�의존하여�비전문적(야매)으로�진행되었습니다.
1년간�안드로이드와�마음이�많이�멀어져�있었는데�이번�계기로�더욱�멀어졌습니다.
경험이�큰�도움이�되긴�했지만,�진행속도에�맞추기�위해�얕게�공부한�부분을�다시�공부해야�합니다.
IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
Swift�&�IOS�정말�좋음!��
특히�������������������������������������는�스위프트를�사용한다면�한�번쯤�읽어보시면�좋을�것�같습니다.스위프트�성능�이해하기
마지막으로
아직�제가�무엇을�모르는지도�모르는�상태입니다.�
하지만�빠른�시일�내에�무엇을�모르는지는�아는�단계까지�나아갈�수�있을�것�같습니다.
개인적으로�Realm의�Let�Swift!�세션에서�많은�도움을�받았습니다.
이번�개발은�안드로이드에서의�경험에�의존하여�비전문적(야매)으로�진행되었습니다.
1년간�안드로이드와�마음이�많이�멀어져�있었는데�이번�계기로�더욱�멀어졌습니다.
경험이�큰�도움이�되긴�했지만,�진행속도에�맞추기�위해�얕게�공부한�부분을�다시�공부해야�합니다.
IOS는�문서화가�정말�잘�되어있습니다.
커버해야하는�기기�수�또한�많지�않기에�IOS로�앱�개발을�시작하는�건�나쁘지�않은�선택�같습니다.
빨리�제가�Swift로�mac�용�앱을�개발하는�날이�오면�좋겠습니다.�(모바일개발을�벗어나고�싶음)
Swift�&�IOS�정말�좋음!��
특히�������������������������������������는�스위프트를�사용한다면�한�번쯤�읽어보시면�좋을�것�같습니다.스위프트�성능�이해하기