디스패치 큐 제어하기
: GCD는 디스패치 큐에서 작업을 제어할 수 있는 많은 유용한 API를 제공한다.

dispatch_set_target_queue
: target을 세팅하기 위한 큐이다. 이것은 주로 새로 생성된 큐에 중요도를 설정하는데 사용된다.
: 시리얼과 콘커런트 디스패치 큐 모드 dispatch_queue_create함수를 사용해서 생성될 때, 스레드의 중요도는 글로벌 디스패치 큐의 기본 중요도와 같다. 생성된 이후 중요도를 수정하기 위해서는 이 함수를 사용할 수 있다.

dispatch_queue_t serialQueue = dispatch_queue_create("com.hsg2510.serial", NULL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(serialQueue, globalQueue)
// serialQueue의 중요도는 globalQueue의 중요도와 같아진다.
// dispatch_set_target_queue함수의 첫번인자 값으로 메인 큐, 글로벌 큐를 넣으면 안된다.

: 또한 디스패치 큐의 계층 구조를 만들수도 있다. 이는 관련자료를 찾아보기 바란다.

 dispatch_after
: 큐에 작업의 시작 타이밍을 설정하는 것이다. 지정된 시간 이후에 작업을 실행하려고 할 때 사용한다.

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), {
    NSLog(@"hi ~~ ");
 })
// dispatch_time은 첫번째 인자로 들어온 시간부터 2번째 인자의 시간만큼 지난시간 리턴한다.
// dispatch_after의 첫번째 인자는 해당 블록이 큐에 추가될 시간을 지정한다.
// 지정된 블록을 3초 후에 메인 디스패치 큐에 추가한다.
// "ull"은 C언어 문법에서 지정된 타입이다. unsigned long long
// RunLoop가 1/60초 간격으로 실행하는 경우, 블록은 3초후와 3초 + 1/60초 사이에서 실행된다. 많은 작업이 메인 디스패치 큐에 추가되거나 메인 스레드에 지연이 있으면, 그 뒤에 실행될 수 있다. 따라서 정확한 타이머 개념으로 사용하는 것은 문제지만, 대략적인 작업의 지연을 유도할 때 매우 유용하게 사용된다.


디스패치 그룹
: 디스패치 큐의 여러개의 작업들이 비동기적으로 실행될 때 모든 작업이 끝난뒤에 해야되는 작업이 있다면 디스패치 그룹을 사용하면 된다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{ NSLog(@"blk0"); });
dispatch_group_async(group, queue, ^{ NSLog(@"blk1"); });
dispatch_group_async(group, queue, ^{ NSLog(@"blk2"); }); 

dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"end"); });
dispatch_release(group);
//3개의 블록작업이 aync하게 동작되고 모든 작업이 끝나면 "end"가 출력된다.

또한 단순하게 디스패치 그룹과 관련된 모든 작업을 기다릴 수 있다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORTY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{ NSLog(@"blk0"); });
dispatch_group_async(group, queue, ^{ NSLog(@"blk1"); });
dispatch_group_async(group, queue, ^{ NSLog(@"blk2"); });

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
//dispatch_group_wait 함수의 두번쨰 인자값을 대기 시간을 지정하는 일시적 중단이다. FOREVER로 지정되어 있으니 그룹에 대한 작업이 끝날때까지 영원히 기다린다.

dispatch_after 함수로 배운바와 같이 1초를 기다리게 할 수 있다.

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);

if (result == 0)
{
    // 1초 뒤에 디스패치 그룹과 연관된 모든 작업이 종료됨.
}
else
{
    // 1초 뒤에도 디스패치 그룹과 연관된 작업이 모두 종료되지 않음.
}
// dispatch_group_wait 함수에 지정된 시간이 지나가는 동안 또는 그룹에 관련된 작업이 모두 종료될 때 까지 현재 스레드가 중지된다. 

DISPATCH_TIME_NOW를 사용해서 현재 그룹과 연관된 작업이 모드 종료되었는지 확인하는 용도로 쓸 수도 있다.

long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
// 예를들면, 메인 스레드에서 실행되는 여러 RunLoop의 각각 루프에서 모든 작업이 끝났는지 확인하는 용도로 사용할 수 있다. 


dispatch_barrier_async
: 큐에 다른 작업을 기다리는 함수이다.
: 데이터베이스 작업을 할 때, select작업은 serial로 실행될 필요가 없기 때문에 concurrent로 돌려도 된다. 하지만 도중에 update, insert작업이 들어가면 무조건 concurrent로 실행하면 안된다. 이때 유용하게 사용할 수 있는 함수이다. 

dispatch_queue_t queue = dispatch_queue_create("com.hsg2510.example", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, blk0_for_reading);
dispatch_async(queue, blk1_for_reading);
dispatch_async(queue, blk2_for_reading);
dispatch_async(queue, blk3_for_reading);
dispatch_async(queue, blk4_for_reading);

dispatch_release(queue);
// 읽기 작업 5개가 비동기로 실행된다.

만약 위 5개 작업 도중 update작업이 들어가야 될 경우 곤란하다. 예를들어 첫번째, 두번째 블럭은 update 이전 데이터가 조회되야 되고 3~5번째 블럭은 update이후 데이터가 조회되야 한다면 어떻게 할것인가? 이를 위해 dispatch_barrier_async함수를 사용할 수 있다.

dispatch_async(queue, blk0_for_reading);
dispatch_async(queue, blk1_for_reading);
dispatch_barrier_async(queue, blk_for_update);
dispatch_async(queue, blk2_for_reading);
dispatch_async(queue, blk3_for_reading);
dispatch_async(queue, blk4_for_reading);
// dispatch_barrier_async함수를 위 두개의 작업이 다끝나기를 기다렸다가 그때 작업을 큐에 추가한다. 그리고 밑 3개의 작업은 update작업이 끝난뒤에 큐에 추가된다.

효과적인 데이터베이스 또는 파일 엑세스를 구현하기 위해 콘커런트 디스패치 큐와 dispatch_barrier_async함수를 사용하길 바란다.


dispatch_sync
: 디스패치 큐에 동기적으로 블록을 추가한다. 그리고 dispatch_sync가 실행된 큐는 해당 작업이 끝날때까지 멈춘다.

dispatch_queue_t_ queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{ /*task*/ });
// task가 끝날때까지 dispatch_sync를 실행한 큐는 멈춤. dispatch_sync가 호출된 뒤에 지정된 작업이 끝나기전까진 함수는 리턴을 하지 않는다.

다음 코드를 메인 스레드에서 실행할 때, 데드락이 발생할 수 있다.
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{NSLog(@"Hello?");});
//메인 큐에 블록을 추가하는데 블록이 실행되기전 블록에 대한 작업이 끝날때까지 큐를 기다리게 한다. 즉, 블록이 실행될 리가 없으므로 데드락을 일으킨다.

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
       dispatch_sync(queue, ^{ NSLog(@"Hello"); });
 });
//이것 역시 데드락을 일으킨다. 

물론 같은 일이 시리얼 디스패치 큐에도 발생한다.
dispatch_queue_t queue = dispatch_queue_create("com.hsg2510.serialDispatchQueue", NULL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{ NSLog(@"Hello?"); });
 });
// 이것 역시 데드락을 일으킨다.

*dispatch_barrier_async에 대응하는 dispatch_barrier_sync가 있다. 디스패치 큐의 모든 작업이 완료된 후 dispatch_sync함수처럼 지정된 작업이 완료되기를 기다린다.
*dispatch_sync와 같은 sync API를 사용할 때 각별히 주의해야하고, 꼭 사용해야 할때만 사용하기를 바란다.


dispatch_apply
: dispatch_sync와 dispatch_barrier_async를 합쳐놨다고 생각하면 된다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
     NSLog(@"%zu", index);
});
NSLog(@"done");

//결과
//4 1 0 3 5 2 6 8 9 7 done
//첫 번째 인자값은 횟수이다. 두 번째는 타깃 디스패치 큐이고, 세 번째는 큐에 추가될 작업이고 인자로 index를 받는다.

NSArray의 각 항목에 대해 뭔가를 하기를 원할때 for-loop를 작성할 필요가 없다.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index) {
    NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});

글로벌 디스패치 큐의 배열에 모든 항목에 대한 블록을 실행하는 것은 매우 쉽다.
dispatch_apply 함수는 dispatch_sync 함수처럼 모든 작업의 실행을 기다린다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    //글로벌 디스패치 큐에서 dispatch_apply 함수는 모든 작업이 완료되기를 기다린다.
    dispatch_apply([array count], queue, ^(size_t index) {
        //NSArray 객체의 각 항목에 대해 동시에 뭔가를 한다.
        NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
    });
    // dispatch_apply 함수에 의해 모든 작업이 완료된다.
    
    // 메인 디스패치 큐에서 비동기적으로 실행된다.
    dispatch_async(dispatch_get_main_queue(), ^{
        //userface 업데이트와 같은 것 등을 메인 디스패치 큐에서 실행.
    });
});



dispatch_suspend/dispatch_resume
: 이 함수들은 큐의 실행을 일시 중지하거나 재개한다. 디스패치 큐에 많은 작업을 추가할 때, 때때로 그것들의 모든 것을 추가하는 것을 완료할 때까지 작업의 실행을 원하지 않을 수 있다.(블록이 다른 작업에 의해 영향을 받을 값을 캠쳐할 때 이러한 작업을 원할 수 있다)
이미 실행중인 작업들에는 영향을 주지 않는다. 디스패치 큐에는 있지만 아직 시작하지 않은 작업실행을 방지할 수 있다.

dispatch_suspend(queue);
dispatch_resume(queue);
//사용방법은 간단하다.


Dispatch Semaphore
: dispatch semaphore는 시리얼 디스패치 큐 또는 dispatch_barrier_async 함수보다 작은 부분에 대한 동시성 제어가 필요한 경우 유용하다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *array = [[NSMutableArray alloc] init];

for (int i = 0; i < 100000; ++i)
{
    dispatch_async(queue, ^{
        [array addObject:[NSNumber numberWithInt:i]];
    });
}
이 소스코드에서 NSMutableArray 클래스는 멀티스레딩을 지원하지 않기 때문에 많은 스레드에서 객체가 업데이트 될 때 크래시가 발생될 수 있다. 이 문제는 전형적인 레이스 컨디션이다. 이런 상황에 디스패치 세마포어를 사용하면 된다.
흔히 멀티스레딩 프로그래밍에서 말하는 세마포어 개수를 다루는 방식과 동일하게 디스패치 세마포어도 흐름 제어를 위한 플래그 개념이다. 플래스는 계속 진행할 수 있을 때 up되고, 진행할 수 없을 때 플래그는 down된다.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//세마포어를 생성한다. 인자값은 카운터의 초기값이다. 이름에 "create"가 포함되어 있으므로 ARC가 아닌 환경의 경우 dispatch_release로 그것을 release해야 한다. 또한 dispatch_retaion으로 소유권을 가질수 있다.

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//이 함수는 세마포어의 카운터가 1개 이상이 될 때 까지 기다린다. 1개 이상이 되면 카운터를 감소시키고 함수를 종료한다. 두번째 인자값은 대기시간이다. 함수 리턴값은 dispatch_group_wait함수의 값과 동일하다.

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_semaphore_wait(semaphore, time);

if (result == 0)
{
    //디스패치 세마포어의 카운터는 1개 이상이다.
    //또는 지정된 시간이전에 1개 이상이 된다.
    // 카운터는 자동적으로 하나 감소된다.
    // 여기에서 동시 제어가 필요한 작업을 실행할 수 있다.
}
else
{
    //디스패치 세마포어의 카운터가 0이기 때문에 지정된 시간까지 기다린다.
}

작업이 완료되고 나서는 디스패치 세마포어의 카운터를 1개 증가시키는 dispatch_semaphore_signal 함수를 호출해야 한다.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [NSMutableArray array];

for (int i = 0; i < 100000; ++i)
{
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
       
        [array addObject:[NSNumber numberWithInt:i];
        
        dispatch_semaphore_signal(semaphore);
    });
}

dispatch_release(semaphore);


dispatch_once
: 지정된 작업이 애플리케이션 라이프타임 동안 오직 한  번 실행되는지 확인하는데 사용된다.

static int initialized = NO;

if (initialized == NO)
{
    // 초기화
    initialized = YES;
}

위 코드를 dispatch_once함수로 더 우아하게 구현할 수 있다.

static dispatch_once_t pred;

dispatch_once(&pred, ^{
    // 초기화
});

두 소스코드는 별 차이가 없으나 멀티스레드 환경에서  첫번째 소스코드는 initialized값이 덮여쓰여지는 동시에 읽힐 수도 있는 약간의 기회가 있어서 100프로 안전하지 않다. 그러나 dispatch_once 함수로 구현을 하면 더는 걱정할 필요가 없다.

'IOS > Objective-C' 카테고리의 다른 글

Block의 모든것(1)  (0) 2017.01.02
ARC 규칙  (0) 2017.01.01
GCD(Grand Central Dispatch)(1)  (0) 2016.12.19
Objective-C의 동적 바인딩, 그리고 Message dispather와 Runtime  (0) 2016.12.18
Key-Value Coding(1)  (0) 2016.12.07
Posted by 홍성곤
,

GCD는 작업을 비동기적으로 실행하기 위한 기술 중 하나다. 이 기술은 보통 개발자들이 응용프로그램을 만들 때 직접 작성하는 스레드 관리 코드를 GCD자체가 관리하기 때문에 전통적인 멀티스레드 프로그래밍 방법보다 훨씬 쉽게 멀티 스레딩을 지원할 수 있도록 하였다.

GCD가 만들어지기 전에는 NSObject 인스턴스 메서드인 performSelectorInBackground:withObject, performSelectorOnMainThread등을 사용했다. 
performSelector계열이 NSThread 클래스를 사용한 코드보다는 쉽지만, GCD를 이용한 코드가 확실히 가장 쉽다. 게다가 훨씬 효율적이다.


CPU가 응용프로그램을 실행하는 방식
- 소스코드는 실행되기전 CPU 바이트코드로 변환된다. 응용프로그램은 맥이나 아이폰에 설치될 데이터와 바이트코드를 하나로 묶는다. 운영체제가 바이트 코드를 메모리에 할당하고 응용프로그램에 명시된 특정 주소를 시작으로 CPU가 바이트 코드를 한줄 한줄 실행한다.

하나의 CPU는 한 순간에 하나의 명령을 수행할 수 있다. 즉 한 순간에는 하나의 스레드만 작업할 수 있다는 얘기이다.
그렇다면 응용프로그램은 어떻게 멀티 스레딩을 지원할 수 있을까? 그것은 커널이 일정 간격으로 각 스레드를 스위치시키고, 시스템 콜 같은 OS 이벤트가 발생하면 실행중인 스레드의 실행상태를 각 스레드에 할당된 메모리 블록 중 하나에 저장한다. 즉, 이를 반복해서 CPU가 여러 경로를 실행하는 것처럼 동작한다. 이런 방식을 문맥 전환(context switch)라고 한다.
멀티 스레드 프로그램은 컨텍스트 스위치를 끊임없이 반복한다. 그러므로, 하나의 CPU가 가상으로 여러 스레드를 실행할 수 있게 된다. 여러개의 CPU 코어가 있을 때는 각 코어가 동시에 스레드를 실행할 수 있다. 즉, 실제로 스레드가 동시에 실행될 수 있는 개수가 CPU 코어의 개수이다.


GCD  기본
디스패치 큐(Dispatch Queues)
- 디스패치 큐는 실행할 작업을 저장하는 큐이다. dispatch_async 함수를 사용하여 작업들을 디스패치 큐에 추가할 수 있다.
- 디스패치 큐는 두가지 종류가 있다. 하나는 시리얼 디스패치 큐(serialDispatchQueue)이고 나머지 하나는 콘커런트 디스패치 큐(concurrentDispatchQueue)이다. 시리얼 디스패치 큐는 작업을 큐에 들어온 순서대로 동작시키고 콘커런트 디스패치 큐는 들어온 작업들을 비동기적으로 실행시킨다. 즉, 시리얼 디스패치 큐는 단일 스레드를 사용하고 콘커런트 디스패치 큐는 멀티 스레드를 사용한다.
다만 콘커런트 디스패치 큐는 작업 하나마다 스레드 하나를 할당하는 것이 아니라 커널이 스레드의 갯수를 정해주고 각 스레드가 작업을 분담해서 나눠서 처리한다.

디스패치 큐 얻기
- dispatch_queue_create
: 새로운 디스패치 큐를 생성한다.

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.hsg2510.MySerialDispatchQueue", NULL);
// 디스패치 큐의 이름은 프로그램 내에서 유일해야 되기 때문에 도메인 역순 표기법을 추천한다. 

시리얼 디스패치 큐가 만들어지고 작업이 추가될 때, 시스템은 각각의 시리얼 디스패치 큐를 위해 하나의 스레드를 생성한다. 
디스패치 큐는 Objective-c 객체로 취급되지 않기 때문에 수동으로 릴리스하기 위해 dispatch_release를 호출해 줘야 한다.

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.hsg2510.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue, ^{ NSLog(@"hi~") });
dispatch_release(myConcurrentDispatchQueue);
// 디스패치 큐에서 작업이 실행되고 작업이 끝나기를 기다리지 않고 바로 release한다. 괜찮은가? 괜찮다. 작업 블록이 dispatch_async 함수에 의해 디스패치 큐에 추가 되었을 때 작업블록이 dispatch_retain함수에 의해서 디스패치 큐의 소유권을 갖는다. 그리고 블록의 실행이 종료되었을 때, 블록은 dispatch_release함수에 의해서 디스패치 큐가 릴리즈된다.

- 메인 디스패치 큐 / 글로벌 디스패치 큐
: 디스패치 큐를 얻는 다른 방법은 시스템이 이미 제공하고 있는 디스패치 큐를 가져오는 것이다. 시스템은 당신이 생성 못하는 메인 디스패치 큐와 글로벌 디스패치 큐를 갖고있다.
메인 디스패치 큐는 메인 스레드에서 작업을 실행하는 큐이다. 이것은 시리얼 디스패치 큐이고 이 큐에 할당된 작업들은 메인 스레드의 RunLoop에서 실행된다.
글로벌 디스패치 큐는 콘커런트 디스패치 큐이다. 실제로 정말 특별한 경우가 아니라면 dispatch_queue_create함수를 사용하여 콘커런트 디스패치 큐를 생성할 필요가 없다. 이미 시스템은 4개의 글로벌 디스패치 큐가 있고 그것들은 각기 다른 중요도(high, default, low, background)를 가지고 있다. 


'IOS > Objective-C' 카테고리의 다른 글

ARC 규칙  (0) 2017.01.01
GCD(Grand Central Dispatch)(2)  (0) 2016.12.19
Objective-C의 동적 바인딩, 그리고 Message dispather와 Runtime  (0) 2016.12.18
Key-Value Coding(1)  (0) 2016.12.07
Collections의 weak reference  (0) 2016.10.02
Posted by 홍성곤
,


 우선 읽고 볼 문서
  • The Objective-C Programming Guide - 정리
  • Memory Management Programming Guide 
  • iOS Application Programming Guide
  • iOS Development Guide
  • Cocoa Fundamentals Guide
  • Resource Programming Guide
  • View Programming Guide for iOS
  • View Controller Programming Guide for iOS
  • Event-Handling Guide for iOS - 정리
  • Key-Value Coding Programming Guide
  • Key-Value Observing Programming Guide
  • Xcode Project Management Guide

 결국 읽어야 할 문서

  • Table View Programming Guide for iOS
  • Scroll View Programming Guide
  • Information Property List Key Reference
  • Drawing and Printing Guide for iOS / Core Graphics Framework Reference
  • Core Animation Programming Guide / Core Animation Cookbook
  • NSInvocation Class Reference
  • SDK Compatibility Guide
  • Settings Application Schema Reference
  • Concurrency Programming Guide
  • Interface Builder User Guide
  • Instruments User Guide, Shark User Guide
  • Secure Coding Guide
  • Internationalization Programming Topics
  • Cocoa Performance Guidelines
  • Error Handling Programming Guide, Exception Programming Topics
  • Property List Programming Guide / User Defaults Programming Topics

 필요하면 찾아볼 문서 (중요)

  • Core Data Programming Guide
  • CFNetwork Programming Guide / CFNetwork Framework Reference
  • Device Features Programming Guide
  • Location Awareness Programming Guide
  • Text, Web, and Editing Programming Guide for iOS
  • Camera Programming Topics for iOS
  • Keychain Services Programming Guide
  • Event Kit Programming Guide (calendar) / Address Book Programming Guide for iOS
  • OpenGL ES Programming Guide for iOS
  • Quartz 2D Programming Guide
  • Core Audio / Audio Queue Services Programming Guide / Audio Session Programming Guide
  • Undo Architecture
  • File-system performance guidelines

 생각나면 한번쯤 읽어 볼 문서 

  • iOS Human Interface Guidelines
  • Apple Human Interface Guidelines
  • Design Patterns: Elements of Reusable Object-Oriented Software
  • Bundle Programming Guide
  • Coding guideline for Cocoa (naming guideline)

 필요하면 찾아 볼 문서 (덜 중요) 

  • System Messaging Programming Topics in iOS
  • Core Telephony Framework Reference
  • Stream Programming Guide
  • URL Loading System Programming Guide / Apple URL Scheme References
  • Multimedia Programming Guide

  • Archives and Serializations Programming Guide
  • External Accessory Programming Guide
  • Timer Programming Topics
 가장 나중에 읽을 문서 (Mac OS X와 겹치는 것이 많음)
  • Cocoa Event-Handling Guide / How Cocoa Applications Handle Apple Events
  • Foundation Framework Reference
  • Cocoa Drawing Guide (View hierarchy)
  • Threading Programming Guide
  • Objective-C Runtime Programming Guide
  • Window Programming Guide
  • Model Object Implementation Guide
  • Code Loading Programming Topics
  • Preferences Utilities Reference (CF)
  • Distributed Objects Programming Topics
  • Cocoa Scripting Guide
  • Document-Based Applications Overview



출처 : http://pole2win.tistory.com/entry/apple-developer-document-guideline

'IOS' 카테고리의 다른 글

View and Window Architecture  (0) 2016.01.12
Posted by 홍성곤
,

정적 언어(C / C ++)와의 차이

- C / C++은 주로 "컴파일러"에 의존하는 정적인 언어라면, Objective-C는 "런타임"에 의존하는 동적인 언어이다.
- 동적인 언어의 특징은 Dynamic typing, Dynamic binding, Dynamic loading을 지원한다는 것이다.
   1) Dynamic typing : 코드에서 다루는 오브젝트의 type이 무엇인지 실행 도중에 결정됨.
   2) Dynamic binding : 코드상의 구문 statement가 어떤 method를 동작시킬 것인지 실행 도중에 결정됨
   3) Dynamic loading : 실행 도중에 동적으로 실행될 모듈을 메모리에 loading할 수 있음.

*물론, C / C++ 도 Dynamic loading을 지원하며 특히 Dynamic binding은 다형성을 지원하는 객체지향 언어라면 반드시 필요로 하는 기능이기 때문에 C++에서도 핵심적인 부분이다. 하지만 이러한 기능들을 사용하는 메커니즘이나 방법이 objective-c가 훨씬 더 유연하게 사용한다는 것이다.


동적 바인딩?

- 정적 바인딩은 컴파일러 혹은 링커가 프로그램 실행전에 프로그래머가 작성한 method call(또는 message sending)에 대해 실행될 함수를 미리 연결시켜 놓는것이다. 
- 동적 바인딩은 실행할 method call에 대한 함수를 프로그램 실행 도중 Runtime이 동적으로 결정한다는 것이다.


Objective-C의 Object 구조

- isa 포인터와 클래스 Object
: Objective-C의 모든 "인스턴스 Object"들은 isa라는 멤버 변수를 가지고 있다. isa의 타입은 Class타입인데 이것은 Objective-C의 "클래스 Object"를 가리키는 포인터이다. 그리고 id는 "인스턴스 Object"를 가리키는 포인터이다.

typedef struct objc_class* Class
typedef struct objc_object 
{
    Class isa;
} *id;

Objective-C에서는 클래스의 인스턴스 뿐 아니라 클래스 자체도 하나의 Object로서 존재한다. 
인스턴스 Object가 만들어지는 과정을 보면, 클래스 Object의 alloc메서드가 호출되면 인스턴스 Object에 대한 메모리를 할당하고 자신(클래스 Object)을 참조하고 있는 isa포인터를 멤버변수로 세팅한다. 


Dispatch table과 슈퍼 클래스

클래스 Object는 자신의 클래스가 정의하는 모든 method(인스턴스 method, 클래스 method)의 정보를 가지고 있다. 이를 Dispatch table이라 한다. 그리고 클래스 Object는 슈퍼클래스에 대한 포인터 isa를 멤버변수로 가진다.

Runtime은 인스턴스 Object가 메세지를 받으면 인스턴스 Object의 멤버변수 isa를 참조하여 자신의 클래스 Object를 찾는다. 그리고 클래스 Object의 Dispatch table을 참조해서 해당 메세지에 연결된 method 구조체를 참조해서 실제 함수를 실행한다.(method 구조체와 실제 함수 실행에 대한 부분은 추후 설명하겠다.) 만약 메세지에 해당하는 method 구조체를 찾지 못했을 경우 슈퍼클래스 Object를 가리키는 isa를 통해 슈퍼클래스의 Dispatch_table을 검색해서 매칭되는 method 구조체를 찾는다. 계속 적절한 method 구조체를 찾지 못하면 이 과정이 루트클래스까지 반복된다. 



루트 클래스와 메타 클래스

- Cocoa 프로그래밍 환경에서 루트 클래스는 항상 NSObject 또는 NSProxy이다.(대부분 NSObject)
- 클래스 Object도 isa포인트를 가지고 있다. isa포인트는 항상 자신을 생성한 클래스 Object를 가리킨다. 클래스 Object를 생성하는 클래스는 메타 클래스라고 불리는데 이것에 대한 정보는 아직 신경쓰지 않아도 된다.(추후 설명하겠음.)



Selector(SEL), Method, 구현(IMP)의 정체

- Selector는 Method의 이름이라고 생각하면 된다. Method는 Selector와 실제 구현(IMP)을 연결시켜주는 구조체이다.

typedef struct objc_method
{
    SEL method_name; //SEL은 보통 문자열의 포인터
    char *method_types; //인수의 개수와 타입, 반환값에 따라 정해지는 메서드 타입이다.
    IMP method_imp; //함수 포인터
}


- 위 그림에서 보다시피 메세지와 함수는 분리되어 있고 이를 Method 구조체가 연결하는 구조이다. 
- 클래스 Object의 Dispatch table이 이 Method 구조체들을 포함하고 있을것이다.



method 관련 Runtime함수들


- Method method = class_getInstanceMethod(Class class, SEL sel)
: Method 구조체를 얻어내는 함수

- SEL sel = method_getName(Method method)
: SEL를 얻어내는 함수

- IMP imp = method_getImplementation(Method method)
: IMP를 얻어내는 함수

- method_setImplementation(Method method, IMP imp)
: 해당 Method에 IMP를 설정

Selector에 대응되는 IMP변경 코드
void killEmployee(id self, SEL _cmd)
{
    NSLog(@"I'd rather kill you than pay you");
}

method_setImplementation(method, (IMP)killEmployee);



*참조
- http://pole2win.tistory.com/entry/Objective-C-objects
- http://pole2win.tistory.com/entry/Objective-C-dispatch


'IOS > Objective-C' 카테고리의 다른 글

GCD(Grand Central Dispatch)(2)  (0) 2016.12.19
GCD(Grand Central Dispatch)(1)  (0) 2016.12.19
Key-Value Coding(1)  (0) 2016.12.07
Collections의 weak reference  (0) 2016.10.02
NSString과 NSMutableString의 copy  (0) 2016.02.18
Posted by 홍성곤
,

iOS Architecture

IOS/공통 2016. 12. 18. 09:22

iOS 아키텍처는 4개의 layer로 이루어져 있다. 각 layer들은 frameworks를 포함하고 있다.

1. Cocoa Touch
- 대부분 UI관련 Framework들이 포함되어 있다. 대표적인 FrameWork가 UIKit Framework이다.

2. Media
- Graphics, Audio, Video 관련 Framework들이 포함되어 있다.

3. Core Services
- Core Foundation, Foundation Framework가 포함되어 있다. 이외에, iCloud, social media, networking frameworks등이 포함된다.

4. Core OS Layer
- iOS에서 사용할 수 있는 framework들 중 가장 low level feature다. APP에서 코드로 직접 이 layer를 사용하지 않더라도 다른 상위 layer에서 대부분 사용하는 frameworks들이다. security, external hardware accessory, bluetooth, local Authentication등에 관련된 framework들이 포함된다.

 

* 해당 layer에 어떤 framework들이 포함되어 있는지 보려면 아래 링크를 참조해라.
- https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/iPhoneOSTechOverview/Introduction/Introduction.html

'IOS > 공통' 카테고리의 다른 글

Cocoa Pods  (0) 2017.02.19
About Bundle  (0) 2017.02.14
UIApplicationMain?  (0) 2016.02.18
디버깅 (Crash 핸들링)  (1) 2015.11.05
[iOS 9] Search  (0) 2015.11.05
Posted by 홍성곤
,

- 애플 문서에서 KVO를 객체의 accessor methods를 통한 접근보다 더 flexiblie하고 general mechanism을 제공한다고 설명한다.

 

Introduction

Using Key-Value Coding Compliant Objects

1. Access object properties

2. Manipulate collection properties

3. Invoke Collection operators on collection objects

4. Access non-object properties

 

Other Cocoa Tchnologies Rely on Key-Value Coding

1. Key-value objserving

2. Cocoa bindings

3. Core Data

4. AppleScript

 

1. Accessing Object Properties

Listing 1-1)
@inetrface BankAccount : NSObject

@property (nonatomic) NSNumber *currentBalance;
@property (nonatomic) Person *owner;
@property (nonatomic) NSArray<Transaction *>* transactions;

@end

KVO를 통해 [myAccount setValue:@(100.0) forKey:@"currentBalance"]; 로 properties에 접근할 수 있다.
또한, keyPath string( ex. @"owner.address" )을 통해 property의 property를 참조할 수 있다. 즉, keyPath string을 통해 객체 맨 하단 hierarchy까지 접근하여 해당 property들을 참조할 수 있는것이다. 

 

Getting Attribute Values Using Keys

- valueForKey:
: 키에 대한 해당 값을 가져옴. 값이 존재하지 않으면 valueForUndefinedKey: 메서드가 불리게 되고 NSObject default로 구현된 동작은 NSUndefinedKeyException을 발생시키는 것이다. 값이 존재하지 않을때의 동작을 Custom하게 구현하려면 해당 메서드를 오버라이드 하면된다.

- valueForKeyPath:
: keyPath에 대한 값을 가져옴. 값이 존재하지 않을경우의 동작은 valueForKey: 같다.

- dictionaryWithValuesForKeys:
: Key들에대한 Value들의 array를 return한다.

* Listing  1-1)에서 property "transactions"이 payee라는 객체들을 포함하고 있다면, keyPath로 "transactions.payee"를 전달하면 transactions에 포함되있는 모든 payee객체들이 array로 return된다. 또한, accounts가 BankAccount의 array property라면 keyPath "accounts.transactions.payee"를 전달하면 accounts안에 모든 transaction의 모든 payee를 array로 return한다. 

 

2. AccessingCollection Properties

Collection properties에 대해서 KVC를 쓸때 해당 데이터를 immutable하게 다루는 것보다 mutable하게 다루는 것이 사용하는데 있어서 편리할때가 많다.
해서, KVC는 NSMutableArray, NSMutableSet, NSMutableOrderedSet을 return하는 메서드를 제공한다. 해당 메서드를 사용해서 adding, removing, replacing동작을 할 경우 해당 데이터를 reference하고 있는 객체에서도 동일하게 작업이 반영된다.

- NSMutableArray: mutableArrayValueForKey:, mutableArrayValueForKeyPath:
- NSMutableSet: mutableSetValueForKey:, mutableSetValueForKeyPath:
- NSMutableOrderedSet: mutableOrderedSetValueForKey, mutableOrderedSetValueForKeyPath:

 

3. Using Collection Operators

valueForKeyPath: 메서드를 호출할 때, keyPath안에 Collection Operator를 넣을 수 있다.

* Operator key path format
"keypathToCollection.@collectionOperator.keyPathToProperty"

Listing 3-1) Linsting 1-1)의 BankAccount.transactions에 포함되는 Tranaction객체.

@interface Transaction : NSObject

@property (nonatomic) NSString *payee;
@property (nonatomic) NSNumber* amount;
@property (nonatomic) NSDate* date

@end 


Aggregation Operators

@avg
: 각 값을 double형으로 변환해서 더한다음 평균값을 구해서 return. 각 값에 대해서 nil인 경우 0으로 변환해서 계산.

NSNumber *transactionAverage = [[self transactions] valueForKeyPath:@"@avg.amount"];

@count
: 객체의 갯수 값 return, right keypath 필요없음. 있으면 무시한다.

NSNumber *numberOfTransactions = [[self transactions] valueForKeyPath:@"@count"];

@max
: 값 중 제일 큰 값을 return. 값 비교는 "compare:" 메서드를 통해 수행함. 고로, right keyPath가 가리키는 property는 "compare:" 메시지에 응답할 수 있는 객체여야 한다.

@min
: 제일 작은 값 return. 동작방식은 @max랑 동일.

@sum
: 값들의 합을 반환. 각 값을 double형으로 반환한 후 계산. 각 값에 대해서 nil인 경우 0으로 변환해서 계산.

NSNumber *amountSum = [[self transactions] valueForKeyPath:@"@sum.amount"];

 

Array Operators
: valueForKeyPath: 메서드에서 Array Operators를 사용할때, leaf Object들 중 하나라도 nil인 경우 exception을 발생시킴.

@distinctUnionOfObjects
: 합집합, 중복제거

NSArray *distinctPayees = [[self transactions] valueForKeyPath:@"@distinctUnionOfObjects.payee"];

@unionOfObjects
: 합집합

NSArray *payees = [[self transactions] valueForKeyPath:@"@unionOfObjects.payee"];


Nesting Operators
: nesting collecction들에 대해서 동작하는 operators이다.
: valueForKeyPath: 메서드에서 Nesting Operators를 사용할 때, leaf Object들 중 하나라도 nil인 경우 exception을 발생시킴.

Listing 3-2)
NSArray *moreTransactions = @[ /* transaction data */ ];
NSArray *arrayOfArrays = @[ [self transactions], moreTransactions ];

@distinctUnionOfArrays
: 합집합, 중복제거

NSArray *collectedDistinctPayees = [arrayOfArrays valueForKeyPath:@"@distinctUnionArrays.payee"];

@unionOfArrays
: 합집합

NSArray *collectedPayees = [arrayOfArrays valueForKeyPath:@"@unionOfArrays.payee"];

@distinctUnionOfSets
: distinctUnionOfArrays의 NSSet버전.
: 중복 NSSet에 대해서 동작함.

 

4. Representing Non-Object Values

KVO의 getter를 통해 얻어오려는 값이 non-object인 경우 NSNumber(for scalars) 또는 NSValue(for structures - CGPoint, NSRange, CGRect, CGSize)로 변환해서 return한다.
비슷한 논리로 setter로 세팅하려는 property가 non-object인 경우, 넘어오는 파라미터에 "<type>Value" 메서드를 호출해서 property에 맞는 타입의 값을 세팅한다.

non-object의 property에 KVO setter값에 nil을 넣으면 setNilValueForKey: 메서드가 불린다. 이 메서드의 default동작은 NSInvalidArgumentException을 발생시킨다. Custom처리를 하려면 해당 메서드를 오버라이드 해야 한다.

Wrapping and Unwrapping Structures

CGPoint, NSRange, CGRect, CGSize이외에 사용자가 정의한 Structure도 가능하다.

Listing 4-1) A sample class using a custom structure

typedef struct
{
   float x, y, z;
} ThreeFloats

@interface MyClass
@property (nonatomic) ThreeFloats threeFloats;
@end

-> wrapping, unwrapping

NSValue *result = [myClass valueForKey:@"threeFloats"];
// NSValue로 자동 wrapping 된다.

ThreeFloats floats = { 1.0, 2.0, 3.0 };
NSValue *value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
[myClass setValue:value forKey:@"threeFloats"];
// 해당 property로 setting될때는 NSValue의 getValue: 메서드를 이용해 자동 unwrapping되서 저장된다.


5.Validating Properties

- 참조: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/ValidatingProperties.html#//apple_ref/doc/uid/10000107i-CH18-SW1


6. Accessor Search Patterns

- 참조: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA




 

* Performance?
: 애플은 KVC가 indirect level에서의 접근이기 때문에 direct method invocation(직접 method call)보다 flexiblity를 제공하지만 속도측면에서는 약간 느리다고 밝히고 있다. 그래서 KVC가 제공하는 이득을 볼 수 있을때만 사용하라고 권장하고 있다. 하지만 둘간에 performance차이는 아주 경미한 수준이라 performance에 민감한 프로그램이 아니라면 무시해도 괜찮을 것 같다.

Posted by 홍성곤
,


Objective-C에서 일부 Collection을 제외하고 대부분의 Collection이 객체를 strong reference한다.

하지만 특수한 경우에 weak reference하고 있는 Collection들이 필요하기 때문에 몇개의 weak reference를 할 수 있는 collection들이 존재한다. 해당 Collection들은 당연히 Mutable이다.

1. NSPointerArray

- 대응 Collection : NSArray, NSMutableArray

2. NSHashTable

- 대응 Collection : NSSet, NSMutableSet

3. NSMapTable

- 대응 Collection : NSDictionary, NSMutableDictionary


* 이들 Collectinon들은 강한 참조와 약한 참조를 선택 지정할 수 있을 뿐 아니라, 복사를 할것인지, 참조하는 대상이 객체인지 C언어의 문자열이나 구조체인지도 지정할 수 있다. 그리고 메모리를 확보하고 해제하는데 사용되는 함수를 지정하는 것도 가능하다.


Posted by 홍성곤
,

UIApplicationMain?

IOS/공통 2016. 2. 18. 08:57

app이 시작하는 main함수이다.

과연 UIApplicationMain함수의 네개 파라미터는 무엇을 의미할까?

1. argc : argv 배열의 총 수. iPhone 앱에서는 1.

2. argv : C문자열 배열. iPhone 앱에서는 argv[0]에 애플리케이션 파일의 위치가 unix 경로로 들어갑니다.

3. principalClassName : 앱 운용에 사용하고 싶은 오브젝트의 클래스 명. UIApplication을 상속한 클래스여야만 함. nil을 지정한 경우 UIApplication이 사용됨.

4. delegateClassName: 앱 보조 운용에 사용하고 싶은 오브젝트의 클래스 명. AppDelegate를 상속한 클래스가 아니면 안됨. 어짜피 우리는 AppDelegate.m을 조작할 수 있기 때문에 상속해서 쓸 필요는 없는듯 보임.



'IOS > 공통' 카테고리의 다른 글

About Bundle  (0) 2017.02.14
iOS Architecture  (0) 2016.12.18
디버깅 (Crash 핸들링)  (1) 2015.11.05
[iOS 9] Search  (0) 2015.11.05
[IOS 9] 개요  (0) 2015.11.05
Posted by 홍성곤
,

1.NSMutableString의 copy

결과가 어떻게 될까?

예상한대로 두 문자열의 메모리 주소는 다르다. copy했기 때문에 stringA 문자열의 복사본을 다른 메모리 주소에 만들고 그 주소를 stringB에 대입했기 때문이다.


2. NSString의 copy

결과가 어떻게 될까?

예상과 다르게 두 문자열의 메모리 주소는 같다. retain 한것과 결과가 똑같을 것이다. @"aa"의 retainCount도 2가 되었을것이다. 왜일까? 

NSString은 불변객체이기 때문에 @"aa" 라는 문자열 상수가 변경될 일이 없다. 그래서 굳이 복사본을 따로 만들어서 메모리를 낭비시키지 않고 retain과 똑같이 동작하도록 구현한 것이다.


*이해를 돕기 위해서 NSString과 NSMutableString을 예로 든것이다. 불변, 가변객체 구조를 가지고 있는 NSArray, NSMutableArray, NSDictionary, NSMutableDictionary 등도 위와 같은 메커니즘으로 동작한다.

'IOS > Objective-C' 카테고리의 다른 글

Key-Value Coding(1)  (0) 2016.12.07
Collections의 weak reference  (0) 2016.10.02
멀티 스레드 프로그램  (0) 2015.08.23
메소드의 동적 결합  (0) 2015.08.22
Property List  (0) 2015.08.20
Posted by 홍성곤
,

View and Window Architecture

IOS 2016. 1. 12. 21:24

View Architecture Fundamentals

View들은 렌더링과 애니메이션을 수행하기 위해 Core Animation layer들(일반적으로 CALayer 클래스이다)과 협력한다. 대부분의 동작들은 UIView interface를 통해 수행하지만, 렌더링 애니메이션에 있어서 좀 더 세세한 동작들은 layer객체들을 사용한다.

UIView객체 위에 CALayer가 덮여져 있는 형태이다. 다만 default상태에서는 clear한 형태라 보이지 않을 뿐이다.

Core Animation layer objects들은 굉장히 중요하다. 왜냐하면, 실제 뷰를 그리는 코드실행을 최소로 만들어 주기 때문이다. 처음 뷰를 그리는 코드가 실행되어 화면에 그려진 후에, 그 결과를 Core Animation 객체들이 캐싱하고 그 결과를 재사용한다. 이러한 재사용은 애니메이션 효과를 구현할때 특히 중요하다. 애니메이션 동작시 기존에 캐싱되있던 content들을 재사용하여 조작하면서 해당 효과를 구현하면 일일이 다시 그리지 않아도 되기 때문이다.


The View Drawing Cycle

view가 처음 화면에 나타날때 시스템이 View에게 contents를 그릴것을 요청한다. 시스템은 해당 content의 스냅샷을 캡쳐해놓고 재사용한다. 뷰의 content가 바뀌지 않으면 drawing code는 절대 불리지 않는다. 스냅샷 이미지는 뷰와 관련된 대부분의 동작의 재사용된다. content가 바뀌면 시스템에게 뷰가 바뀌었다고 알린다. 그러면 view는 위 동작(draw -> capture -> reuse)을 다시 반복한다.

 뷰가 바뀌면 "setNeedsDisplay" 또는 "setNeedsDisplayInRect"을 호출해서 "다음 기회"에 뷰를 다시 그려야 한다고 알린다. 여기서 "다음 기회"가 뜻하는 바는 현재 run loop가 끝나는 시점이다. 즉 다른 뷰들의 컨텐트들이 바뀌는것을 기다렸다가 컨텐트들이 바뀐 모든 뷰의 drawing code를 한꺼번에 실행한다.

system view들은 자신의 content들을 그릴때 "drawRect:"를 사용한다. UIview를 subClassing해서 커스텀 뷰를 만들면 "drawRect:"을 이용해 컨텐츠를 그릴 수 있다. 뷰의 Layer 객체를 이용해서 그릴수 있지만, 보통 "drawRect:"을 사용한다.

Content Modes

view가 렌더링 되면 그 결과는 layer객체의 bitmap에 저장된다. 그리고 view의 geometry가 바뀌면 bitmap을 다시 만들어야 될지 아니면 새 bounds에 맞춰 content도 늘려야 할지 아니면 단순히 코너 한구석에 밀착시키기만 할지등을 정하는데 이는 view의 content mode에 따라 달라진다.

content mode는 다음과 같은 경우에 적용된다.
- 뷰의 frame or bounds의 width와 heigt가 변한 경우.
- 뷰의 transform 속성에 scailing factor가 포함된 transform이 할당된 경우.

위 속성은 캡쳐된 view의 content들은 재사용한다. 그러나 UIVIewContentModeredraw 속성을 택하면 뷰는 컨텐트들을 항상 redraw한다. 즉, resizing, scaling할때 "drawRect:"이 강제로 불린다. 일반적으로 이 속성은 사용하지 않는것이 좋다.

contentStretch 속성을 사용하여 strechable한 영역을 설정할 수 있다. (CGRect으로 설정)
이 속성은 뷰의 컨텐트가 scale 될때 유효하다. 즉, UIViewContentModeScaleAspectFill, UIViewContentModeScaleAspectFit, UIViewContentModeScaleToFill 속성일때 유효하다.


Built-In Animation Support 

UIView의 속성에서 animatable한 것들
- frame
- bounds
- center
- transform : rotate or scale할때 사용
- alpha
- backgroundColor
- contentStretch

UIKitClass를 이용해서 Animation을 만들수도 있지만, Core animation layer들을 사용해서 만들기도 한다. Core animation layer들을 사용해서 Animation을 만들면 좀 더 세밀한 타이밍 조절과 더 다양한 애니메이션 속성들을 사용할 수 있다. (애플 문서 검색: "About Core Animation")


View Geometry and Coordinate Systems

iOS는 자기 자신의 default coordinate systems을 쓴다.
예를 들어, UIKit의 Coordinate system은 좌측 최상단이 0, 0이다. 하지만 Core Graphics, OpenGL ES는 좌측 최하단이 0, 0이다. 

*subview들의 clipsToBounds 속성과 상관없이 subview들의 이벤트는 superview의 bounds안에 이벤트만 인식한다.


Coordinate System Transformations

- (애플 문서 검색 : Translating, Scaling, and Rotating ViewsDrawing and Printing Guide for iOS )


The Runtime Interaction Model for Views

- event가 발생시점 부터 실제 뷰의 반영되는 시점까지의 sequence logic이다.


1. 유저가 스크린을 터치한다.
2. 하드웨어가 UIKit Framework에 터치 이벤트를 report한다.(iOS가 UIApplication 인스턴스 객체로 전달)
3. UIKit Framework는 해당 터치 이벤트를 UIEvent객체로 패키징 한다. 그리고 UIEvent객체를 핸들링 할 view로 보낸다. UIApplication 인스턴스가 UIWindow 인스턴스로 전달하고 UIWindow가 적절한 인스턴스로 이벤트를 전달한다.(핸들링 할 적절한 뷰를 찾는과정 - 애플문서 검색:Event HandlingGuide)
4. event-handling code가 구현된다. 예를들어 setNeedsLayout(layout update가 필요할 때), setNeedsDisplay(redraw가 필요할 때) 등등을 호출한다.
5. 4번의 코드 실행으로 인해 뷰의 geometry가 변경되었을 때, UIKit은 다음 룰에 따라 subviews들을 변경한다.
  5-1) autoResizingMask를 설정했다면, 그에 맞게 subview들을 변경한다.
  5-2) view가 layoutSubviews를 구현했다면, 호출한다. customView에서 해당 메서드를 override할 수 있다. 이 메서드가 drawRect메서드 이전에 불리기 때문에(모든 drawing code는 run roop가 끝나기 직전에 한꺼번에 불린다) draw작업이 필요없는 것들을 invalidate시켜 놓으면 불필요한 drawing작업을 줄일 수 있다. 
6. UIKit이 view에게 redraw가 필요한 부분은 redraw하라고 시킨다. view에 drawRect가 구현되어 있다면 UIKit은 drawRect을 호출한다.(drawRect 메서드 안에서는 view의 draw 작업만 최대한 빨리 실행하고 종료 되어야 한다. 이 메서드에 비즈니스 로직같은 draw작업 이외의 코드를 넣지 마라.)
standard system views들은 drawRect메서드를 구현해 놓지 않는다. 하지만, 그들의 draw로직은 이 타이밍에 실행된다.
7. 변경된 부분들이 반영되고 graphic hardware에 전달된다.
8. graphic hardware는 렌더링 된 content를 스크린에 display한다.

즉, 쉽게 요약하자면, event-handling method들이 불리고 이 메서드에서 뷰의 geometry가 변경되었으면 layoutsubviews가 불리고 그다음 drawRect이 구현되어 있다면 마지막으로 불린다.


Tips for Using Views Effectively

1. Views Do Not Always Have a Corresponding ViewController 
- 하나의 view가 굳이 대응되는 viewController를 가질 필요없다. 기본적으로 viewController는 여러개의 view hierarchy를 관리하는 객체이기 때문이다.
- viewController를 사용해야 한다면 viewController 사용룰에 맞게 사용해야 한다.(애플 문서, 
View Controller Programming Guide for iOS.)
2. Minimize Custom Drawing
- view drawing 작업은 performance를 상당히 떨어뜨리는 작업이므로 최소화 해야 한다.
3. Take Advantage of Content Modes
- content mode는 view가 redrawing하는 시간을 최소화 한다. 그러나 UIViewContentModeRedraw 사용을 피하라. setNeedsDisplay, setNeedsDisplayInRect를 사용해서 redraw하는걸로 구현하고자 하는 기능을 완성할 수 있다면, 차라리 이들을 사용하라.
4. Declare Views as Opaque Whenever Possible
- 가능하면 opaque속성을 YES로 설정하라. 이는 자신의 뒤에 위치한 뷰들에 대한 렌더링 작업을 할 필요가 없음을 UIKit에게 알리기 때문에, performance를 증가시킬 수 있다.
5. Adjust Your View's Drawing Behavior When Scrolling
- 스크롤할때 많은 뷰들이 나타난다. 이때 뷰들의 Drawing mode를 적절하게 조절하지 않으면 스크롤이 느려 질수가 있다. 즉, 스크롤이 느려지면 뷰들의 렌더링 qualtity를 낮추고 content mode를 조절해보는 것도 하나의 방법이다. 스크롤이 끝나면 원래의 quality와  content mode로 되돌려 놓으면 된다.
6. Do Not Customize Controls by Embedding Subviews
- UIControl을 상속받는 standard system control객체들에 add subview 하지않는 것이 좋다. 이미 standard system control 객체들은 충분히 커스터 마이징 할 수 있는 인터페이스를 가지고 있기 때문에 그것들을 활용해라. 예를들어, UIButton에 view나 label들을  add subview 하면 UIButton이 의도와는 다르게 작동될 수 있다. 지금은 제대로 동작하더라도, 추후에 애플에서 UIButton의 구현이 변경되었을때 오동작을 일으킬 수 있는 여지가 충분하다.



'IOS' 카테고리의 다른 글

애플의 개발자 가이드 읽는 순서  (0) 2016.12.18
Posted by 홍성곤
,