[Effective Objective-C] 총 정리 |
출처 : Effective Objective-C
#1 : 오브젝티브-C 의 기원과 친숙해지라
오브젝티브-C 는 객체 지향 기능을 추가한 C 의 확장이다.
오브젝티브-C 는 동적 바인딩을 사용하는 메시징 구조를 이용한다.
동적 바인딩은 객체 타입이 실행 시간에 밝혀지는 것을 뜻한다.
메시지를 받았을 때 동작할 코드를 컴파일러가 아닌 런타임이 결정한다.
C 의 핵심 개념을 이해하고 있으면 오브젝티브-C 를 효과적으로 작성하는 데 도움이 된다.
특히 메모리 모델과 포인터를 잘 이해하고 있어야 한다.
항상 헤더를 포함하는 것을 최대한 미루라.
이는 보통 헤더에 클래스를 포워드 선언하고 구현 파일에 그것과 연관된 헤더를 포함하는 것을 의미한다.
이렇게 하면 클래스끼리 연관되는 것을 최대한 피할 수 있게 된다.
때때로 프로토콜을 따르는 것(protocol-conformance)를 선언할 때와 같이 포워드 선언이 불가능할 때가 있다.
이 경우 가능하다면 프로토콜을 따르는 것을 클래스 확장 카테고리로 바꿔보라.
그렇지 않을 경우, 할 수 있으면 프로토콜만 정의하는 헤더를 포함하라.
#3 : 메서드보다는 같은 일을 하는 리터럴 문법을 사용하라
문자열, 숫자, 배열, 사전을 생성할 때 리터럴 문법을 사용하라.
일반적인 객체 생성 메서드로 생성하는 것보다 더 명확하고 매우 간결한 방법이다.
배열의 인덱스, 사전의 키에 대한 접근은 첨자를 이용하라.
리터럴 문법으로 nil 을 배열이나 사전에 삽입하려고 하면 예외를 발생시킬 수 있으므로
배열이나 사전에 들어가는 값이 nil 이 아닌지 늘 확인하라.
#4 : 전처리기 #define 보다는 타입이 있는 상수를 사용하라
전처리기 정의는 피하라.
이것은 타입에 대한 정보가 없다.
컴파일하기 전에 단지 해당 정의를 찾아서 치환할 뿐이다.
또한 경고 없이 재정의 될 수 있다.
이로 인해 앱 내에서 값의 일관성이 깨지는 문제를 야기할 수 있다.
static const 를 사용해 구현 파일에 이 번역 단위만을 위한 상수(translation-unit-specific constant) 를 선언하라.
이러한 상수는 전역 심벌 테이블에 공개되지 않을 것이다.
그래서 그런 상수 이름은 네임스페이스를 필요로 하지 않는다.
헤더 파일에 external 을 이용해 전역 상수를 선언하라.
그리고 연관된 구현 파일에 값을 정의하라.
이러한 상수는 전역 심벌 테이블에 나타날 것이다.
그리고 그런 상수 이름은 네임스페이스가 있어야 한다.
일반적으로 관련된 클래스 이름을 접두어로 사용한다.
#5 : 열거형을 사용해 상태, 옵션, 상태 코드를 정의하라.
열거형을 사용하여 상태 머신의 상태, 메서드 인자로 쓰이는 옵션, 에러 상태 코드에 사용되는 값에 읽기 좋은 이름을 주라.
열거형 타입이 여러 가지 옵션이 동시에 사용될 수 있는 메서드 옵션을 정의한다면,
열거형의 값을 2의 제곱 크기로 선언하여 옵션들이 OR 연산을 할 수 있게 하라.
명시적 타입으로 열거형 타입을 선언하기 위해 NS_NUM 과 NS_OPTIONS 매크로를 사용하라.
매크로를 사용하면 컴파일러가 선택한 타입이 아닌 직접 선택한 타입이 사용되는 것을 보장한다.
열거형 타입을 switch 문에서 사용할 때 default 문은 구현하지 말라.
그렇게 하면 열거형에 신규 값을 추가할 때 도움이 된다.
컴파일러는 switch 문이 열거형의 모든 값을 다루지 않았을 때 경고를 주기 때문이다.
#6 : 프로퍼티를 이해하라
@property 문법은 객체의 데이터 캡슐화를 정의하는 방법을 제공한다.
저장되는 데이터를 위한 정확한 방법(semantics)을 제공하기 위해 속성을 사용하라.
인스턴스 변수에 대한 모든 프로퍼티에는 따라야 하는 시맨틱을 반드시 정의하라.
iOS 에서는 nonatomic 을 사용하라.
atomic 을 사용하면 여러 가지 성능 문제를 일으킨다.
#7 : 인스턴스 변수를 내부에서 접근할 때는 직접 접근하라
내부에서 인스턴스 변수를 읽을 때(read)는 직접 접근해 읽고, 반대르 쓸 때(write)는 프로퍼티를 이용하라.
초기화 메서드와 dealloc 에서는 항상 인스턴스 변수를 직접 읽고 쓰라.
데이터(즉 인스턴스 변수)가 지연 초기화를 이용한다면 프로퍼티를 이용해 데이터를 읽어야 한다.
#8 : 객체의 동등 비교를 이해하라
객체의 동등성을 비교하려면 그 객체의 isEqual: 과 hash 메서드를 구현하라.
같은 객체는 항상 해시값이 같아야 한다.
그러나 같은 해시값을 가진 객체가 꼭 동일할 필요는 없다.
객체가 동등한지 비교할 때 모든 프로퍼티를 비교하지 말고 꼭 필요한 프로퍼티만 비교하라.
hash 메서드를 구현할 때 최대한 빠른 결과를 나올 수 있게 구현해야 하지만 해시값 충돌도 최소화해야 한다.
#9 : 클래스 클러스터 패턴을 사용해 구현의 상세 내용을 숨기라. ( Java 의 Interface )
클래스 클러스터 패턴은 간단한 퍼블릭 퍼사드 뒤편에 상세 구현을 숨길 떄 사용할 수 있다.
클래스 클러스터는 시스템 프레임워크에서 널리 사용된다.
클래스 클러스터의 퍼블릭 추상 클래스의 하위 클래스를 만들 때는 항상 주의를 기울여야 한다.
그리고 문서가 있으면 반드시 먼저 읽어봐야 한다.
#10 : 연관 객체를 사용해 기존 클래스에 사용자 정의 데이터를 연관 지으라 ( Java 의 setTag )
연관 객체는 객체 두 개를 연결하는 방법을 제공한다.
연관 객체의 메모리 관리 속성으로 최소 소유(mimic owning) 또는 비소유(nonowning) 관계를 정의할 수 있다.
연관 객체는 다른 대안이 없을 때만 사용해야 한다.
연관 객체는 찾기 어려운 버그를 쉽게 만들기 때문이다.
#11 : objc_msgSend 의 역할을 이해하라
메시지는 리시버, 선택자, 파라미터들로 구성된다.
메시지를 호출하는 것은 객체에 메서드를 호출하는 것과 동일하다.
호출을 할 때 모든 메시지는 동적 메시지 디스패치 시스템을 통해 실행된다.
이 시스템이 구현을 찾고 실행을 한다.
#12 : 메시지 포워딩을 이해하라
메시지 포워딩은 객체가 선택자에 응답할 수 없다는 사실을 알았을 때 진행되는 절차다.
동적 메서드 해결은 런타임에 메서드를 클래스에 추가하고, 바로 사용할 때 이용된다.
객체는 해석할 수 없는 선택자를 다루기 위해 다른 객체를 선언할 수 있다.
완전 포워딩은 이전 두 방법으로 선택자를 처리할 수 없을 때 호출된다.
#13 : 불투명 메서드를 디버깅할 때 메서드 스위즐링을 사용하라
클래스의 선택자에 대한 메서드 구현은 실행 시간에 추가되거나 바뀔 수 있다.
스위즐링은 한 메서드 구현을 다른 것과 맞교환하는 것이다.
보통은 원래 메서드 구현에 기능을 추가로 넣기 위해 사용된다.
런타임이 메서드에 관여하는 것(즉 실행할 메서드 구현을 결정하는 행위)은 오직 디버깅할 때만 유용하다.
사용할 수 있다고 꼭 사용해야 할 필요는 없다.
#14 : 클래스 객체가 무엇인지 이해하라
클래스 계층은 Class 객체들로 구성되어 있다.
각 Class 인스턴스는 타입을 정의하는 포인터를 가지고 있다.
객체 타입을 컴파일 시간에 알 수 없을 때 내성( isMemberOfClass, isKindOfClass )을 꼭 사용해야 한다.
클래스 객체를 직접 비교하는 것보다는 가능한 한 내성 메서드를 사용하라.
객체가 메시지 포워딩을 구현했을 수 있기 때문이다.
#15 : 접두어를 사용해 네임스페이스 충돌을 피하라
회사와 앱 모두에 어울리는 클래스 접두어를 고르라.
그리고 코드 전반에 걸쳐 사용하라.
자신의 라이브러리에서 서드 파티 라이브러리를 사용한다면 그 라이브러리의 이름에 자신의 접두어를 붙이라.
#16 : 지정 초기화 메서드를 만들라
여러분 클래스의 지정 초기화 메서드를 구현하고 문서화하라.
다른 초기화 메서드들은 꼭 이 지정 메서드를 호출해야 한다.
지정 초기화 메서드가 상위 클래스의 지정 초기화 메서드와 다르다면 상위 클래스의 지정 초기화 메서드를 꼭 재정의하라.
하위 클래스에서 사용하지 않을 상위 클래스의 초기화 메서드는 예외를 발생시키도록 재정의하라.
#17 : description 메서드를 구현하라.
인스턴스를 설명하는 유용한 문자열을 제공하려면 description 메서드를 구현하라.
객체가 출력하는 내용을 디버깅할 때 좀 더 자세해지길 원한다면 debugDescription 을 구현하라.
#18 : 가변 객체보다는 불변 객체를 사용하라
가능하다면 객체를 불변(immutable)으로 ㅁ나들라.
프로퍼티를 내부에서 설정할 수 있게 하려면 클래스 확장 카테고리로 읽기 전용 속성을 읽기-쓰기로 확장하라.
가변 컬렉션을 프로퍼티로 노출하기보다는 컬렉션에 포함된 객체를 조작할 수 있는 메서드를 제공하라.
#19 : 명확하고 일관된 작명법을 사용하라
잘 맞고 올바르다고 느낄 수 있는 인터페이스를 생성하기 위해 오브젝티브-C 의 표준 작명 관례를 따르라.
메서드 이름은 간결하지만 정확하고, 문장처럼 왼쪽에서 오른쪽으로 읽을 수 있게 만들라.
타입 축약을 이용해 메서드 이름을 짓지 말라.
가장 중요한 점은 메서드 이름을 여러분의 코드 내에서나, 아니면 다른 것과 함께 사용할 때나 항상 일관되게 짓는 것이다.
#20 : 프라이빗 메서드 이름에 접두어를 사용하라
프라이빗 메서드 이름에 접두어를 붙이면 퍼블릭 메서드와 쉽게 구별할 수 있다.
메서드 접두어로 _ 을 사용하는 것은 피하라.
애플이 사용을 예약했기 때문이다.
#21 : 오브젝티브-C 에러 모델을 이해하라.
전체 앱을 종료시키는 심각한 에러에 대해서만 예외를 사용하라.
심각하지 않은 에러는 에러를 처리하는 델리게이트 메서드를 제공하거나 NSError 객체를 외부 파라미터로 제공하라.
#22 : NSCopying 프로토콜을 이해하라
객체를 복사할 필요가 있으면 NSCopying 프로토콜을 구현하라.
자신의 객체가 가변, 불변 둘 다 될 수 있으면 NSCopying 과 NSMutableCopying 프로토콜 둘 다 구현하라.
복사가 얕은 복사인지 깊은 복사인지 결정하라.
그리고 가능하다면 얕은 복사를 하라.
자신이 만든 객체가 깊은 복사가 필요하다면 깊은 복사 메서드를 추가하라.
#23 : 객체 간 통신에 델리게이트와 데이터 소스 프로토콜을 사용하라.
객체가 다른 객체에 특정 이벤트를 보내고 받을 필요가 있을 때 인터페이스(데이터를 주고 받는 규약)로 델리게이트 패턴을 사용하라.
델리게이트가 지원해야 하는 인터페이스를 정의하는 프로토콜의 메서드는 선택적 메서드로 정의하라.
객체가 다른 객체로부터 데이터를 가져와야 할 때 델리게이트 패턴을 사용하라.
이 델리게이트 패턴을 특히 ‘데이터 소스 프로토콜’ 이라고 한다.
필요하다면 프로토콜에서 어떤 메서드가 응답하는지 여부를 캐싱하기 위해 비트필드 구조체를 구현하라.
#24 : 카테고리를 사용해 클래스를 관리 가능한 다수의 조각으로 나누라
카테고리를 사용해 클래스 구현 파일을 다수의 관리하기 좋은 조각으로 나누라.
Private 이라는 카테고리를 만들어 private 메서드의 자세한 구현을 숨기라.
#25 : 서드 파티 클래스에는 반드시 카테고리 이름을 접두어로 붙이라.
자신이 소유하지 않은 클래스에 카테고리를 추가할 때는 항상 카테고리 이름에 접두어를 붙이라.
자신이 소유하지 않은 클래스에 카테고리를 추가할 때 카테고리의 메서드에도 접두어를 항상 붙이라.
#26 : 카테고리에는 프로퍼티를 사용하지 말라
데이터를 캡슐화하는 모든 프로퍼티 선언은 메인 인터페이스 파일에서 하라.
클래스 확장 카테고리를 제외하고 카테고리 내에 프로퍼티를 선언하기보다는 접근자 메서드를 사용하라.
#27 : 클래스 확장 카테고리를 이용해 상세한 구현을 숨기라
클래스에 인스턴스 변수를 추가하기 위해 클래스 확장 카테고리를 사용하라.
메인 인터페이스에 있는 읽기 전용 프로퍼티를 클래스 내부에서는 세터 접근자를 이용해 값을 설정할 수 있도록 하고 싶으면
클래스 확장 카테고리에 그 프로퍼티를 읽기 쓰기로 재선언하라.
프라이빗 메서드를 위한 메서드 프로토타입은 클래스 확장 카테고리 내에 선언하라.
클래스가 따르는 프로토콜 정보를 내부에 숨기고 싶으면 클래스 확장 카테고리를 사용하라.
#28 : 프로토콜을 이용해 익명 객체를 제공하라.
프로토콜은 객체의 타입을 익명으로 만들기 위해 사용될 수 있다.
프로토콜 메서드를 구현하는 타입은 id 타입으로 줄일 수 있다.
타입(클래스 이름)을 숨겨야 할 때는 익명 객체를 사용하라.
어느 클래스 타입이든 별로 상관없고 특정(또는 프로토콜에 정의되어 있는) 메서드에 응답하는 사실이 더 중요할 떄 익명 객체를 사용하라.
#29 : 참조 세기를 이해하라.
참조 세기 메모리 관리는 카운터를 증가/감소시키는 방법으로 동작한다.
객체는 적어도 1의 카운트를 가지고 생성된다.
리테인 수가 양수인 객체는 살아 있다.
리테인 수가 0으로 떨어지면 객체는 파괴된다.
객체의 생애 주기 동안 객체는 이 객체의 참조를 가진 다른 객체에 의해 리테인되고 릴리스된다.
리테인하는 것과 릴리스하는 것은 각각 리테인 수를 증가시키고 감소시킨다.
#30 : ARC 를 사용하여 참조 세기를 쉽게 만들라.
ARC 는 개발자가 대부분의 메모리 관리에 대해 신경 쓰지 않게 해준다.
클래스의 보일러플레이트 코드를 없애주는 ARC 를 사용하라.
ARC 는 적절한 곳에 retain, release 를 추가함으로써 객체 생애 주기 거의 대부분을 다룬다.
변수 식별자(qualifier)는 메모리 관리 시맨틱을 가리키기 위해 사용될 수 있다.
이전에는 retain, release 가 수동으로 배치되었다.
반환하는 객체의 메모리 관리 시맨틱을 알리기 위해 메서드 이름이 사용된다.
ARC 는 이것을 견고하게 만들었고 이 규칙을 따르지 않는 것응ㄴ 불가능하다.
ARC 는 오직 오브젝티브-C 객체만 다룬다.
이는 ARC 가 CoreFoundation 객체를 다루지 않는다는 것을 의미한다.
그래서 적절한 CFRetain / CFRelease 호출이 반드시 적용되어야 한다.
#31 : 참조를 릴리스하고 관찰 상태(observation state)를 정리하는 일은 dealloc 메서드에서만 하라
dealloc 메서드는 오직 다른 객체의 참조를 릴리스하고 키-값 관찰자나 NSNotificationCenter 알림 같은 것을 등록 해제하는 데만 사용되어야 한다.
객체가 파일 디스크립터 같은 시스텝 자원을 잡고 있으면 이러한 리소스를 반납하기 위한 메서드가 있어야 한다.
이러한 자원의 사용이 끝났을 때 클래스의 사용자가 close 메서드를 반드시 호출하게 해야 한다.
dealloc 메서드 내에서 비동기 작업을 하는 메서드나 절대로 될 수 없는 상태로 변하는 것을 가정하는 메서드는 호출하지 말아야 한다.
#32 : 안전한 예외 처리 코드를 작성하려면 메모리 관리를 주의 깊게 다루라.
예외가 잡혔을 때 try 블록 내에서 생성한 객체는 반드시 필요한 정리 작업이 수행되도록 보장하라.
기본적으로 ARC 는 예외가 발생했을 때 정리를 하는 코드를 만들어 내진 않는다.
이는 컴파일러 표식으로 동작하게 설정할 수 있다.
그러나 많은 양의 코드를 만들어 낼 것이고 런타임 성능에 좋지 않은 영향을 미칠 것이다.
#33 : weak 참조를 사용하여 리테인 순환을 피하라
참조를 weak 로 만들면 리테인 순환을 피할 수 있다.
weak 참조는 자동으로 nil 로 채워질 수도, 채워지지 않을 수도 있다.
자동으로 nil 로 설정되는 것은 ARC 에서 소개된 새로운 기능이다.
그리고 런타임에서 구현되었다.
자동으로 nil 로 채워지는 weak 참조를 읽는 것은 항상 안전하다(반드시 정상 객체 아니면 nil 이기 때문)
그 말은 절대로 할당 해제되는 참조를 포함하지 않는다는 것이다.
#34 : 오토릴리스 풀을 사용하여 최고 메모리 사용량을 낮추라.
오토릴리스 풀은 스택에 저장(arrange)된다.
오토릴리스 메시지를 받은 객체는 스택의 가장 위(top)에 있는 풀에 추가된다.
오토릴리스 풀들을 올바르게 사용하면 앱의 최고 메모리 사용량을 낮추는 데 도움이 된다.
새로운 @autoreleasepool 문법을 사용하여 오토릴리스 풀을 쓰면 비용이 훨씬 저렴하다.
#35 : 좀비를 이용해 메모리 관리 오류를 디버깅하라.
객체가 할당 해제되었을 때 할당 해제되는 대신 좀비로 바꿀 수 있다.
이 기능은 NSZomebieEnabled 환경 표식을 이용해 활성화 할 수 있다.
객체의 클래스를 특별한 좀비 클래스로 변경하기 위해 isa 포인터를 조작해서 객체를 좀비로 바꾼다.
봄비 클래스는 어떤 객체에 어떤 메시지를 보냈는지 알리는 메시지를 출력하고 앱을 종료함으로써 모든 선택자에 응답한다.
#36 : retainCount 를 사용하지 말라
객체의 리테인 수는 유용해 보이지만 보통은 그렇지 않다.
특정 시간의 절대 리테인 수는 객체 생애 주기의 완전한 그림을 보여주지 않기 떄문이다.
ARC 를 도입하면서 retainCount 메서드는 폐기되었다.
그리고 이 메서드를 사용하면 컴파일 에러가 일어난다.
#37 : 블록을 이해하라
블록은 C, C++, 오브젝티브-C 를 위한 렉시컬 클로저다.
블록은 선택적으로 파라미터를 받거나 값을 반환할 수 있다.
블록은 스택, 힙, 전역으로 할당할 수 있다.
스택으로 할당된 블록은 힙으로 복제할 수 있다.
이 지점에서 이 블록은 일반 오브젝티브-C 객체 같이 참조 수를 셀 수 있게 된다.
#38 : 많이 사용하는 블록 타입은 typedef 를 이용해 타입을 생성하라
타입 정의를 사용해 블록 변수를 쉽게 사용할 수 있게 만들라.
새로운 타입을 만들 때 다른 타입의 이름과 겹치지 않게 하기 위해 이름 짓기 규칙을 따르라.
블록 시그너처가 같은 타입 여러 개를 정의하는 것을 두려워하지 말라.
블록 시그너처를 한군데만 변경해서 특정 블록 타입을 전부 리팩터링 할 수 있길 원할 것이다.
#39 : 핸들러 블록을 사용해 코드가 여러 개로 나뉘는 것을 막으라
객체를 생성하는 곳에 핸들러의 비지니스 로직을 인라인으로 같이 선언하는 게 유용하다면 핸들러 블록을 이용해 하라.
핸들러 블록은 델리게이트와는 다르게 직접 객체에 연관될 수 있는 장점이 있다.
델리게이트를 사용할 대 관찰해야 할 인스턴스가 여러 개이면 인스턴스에 따라 바꾸어 처리해야 한다.
핸들러 블록을 사용하는 API 를 설계할 때 블록이 들어갈(enqueue) 큐를 API 파라미터로 전달하라.
#40 : 블록이 자신을 소유한 객체를 다시 소유함으로써 발생하는 리테인 순환을 조심하라
블록에 잡힌 객체가 직간접적으로 다시 그 블록을 리테인할 때 발생하는 리테인 순환을 조심하라.
특정 시점에서 반드시 리테인 순환이 깨질 것이라는 점을 보장하라.
여러분의 API 사용자에게 이 책임을 전가하면 안 된다.
동기화 문법을 제공하기 위해 디스패치 큐를 사용할 수 있다.
그리고 그냥 간단하게 @synchronized 블록이나 NSLock 객체를 이용해 동기화를 제공할 수도 있다.
동기화와 비동기화 디스패치를 함께 사용하는 것은 일반적인 락으로 하는 동기화와 동일한 기능을 제공할 수 있지만,
스레드에서 비동기 디스패치로 호출하면 잠금 없이 호출된다.
병렬 큐와 베리어 블록을 이용하면 동기화를 좀 더 효율적으로 할 수 있다.
#42 : performSelector 메서드군보다는 GCD 를 사용하라
performSelector 메서드군을 사용하면 메모리 관리가 어렵다.
어떤 선택자가 실행될 것인지 알 수 없다면 ARC 컴파일러는 적절한 메모리 관리 호출을 코드에 삽입할 수 없을 것이다.
이 메서드군은 반환 타입과 메서드에 전달할 수 있는 파라미터 개수가 매우 한정적이다.
선택자를 다른 스레드에서 실행하기 위해서는 블록을 사용하는 GCD 를 호출하는 방법을 이용하라.
#43 : GCD 가 필요한 때와 작업 큐가 필요한 때를 구분해서 알아두라
디스패치 큐는 멀티스레드와 태스크 관리의 유일한 해결책은 아니다.
작업 큐는 GCD 가 하는 대부분의 일을 할 수 있는 고수준 오브젝티브-C API 를 제공한다.
또한 이러한 큐들은 GCD 로 한다면 추가적인 코드를 작성해야 하는 좀 더 복잡한 일들을 쉽게 할 수 있다.
#44 : 플랫폼 확장의 이점을 얻으려면 디스패치 그룹을 사용하라
디스패치 그룹은 작업들을 그룹 짓기 위해 사용된다.
선택적으로 그룹이 실행이 끝났을 때 알림을 받을 수 있다.
디스패치 그룹은 병렬 디스패치 큐에 있는 다수의 작업을 병렬로 실행하려 할 때 사용될 수 있다.
이 경우 GCD 는 다수의 작업을 시스템 리소스에 기반을 두고 동시에 수행되도록 스케줄링한다.
이를 직접 구현하려면 많은 코드를 작성해야 한다.
#45 : 스레드 안전한 단일 시간 코드 실행은 dispatch_once 를 이용하라
스레드 안전 단일 코드(single-code) 실행은 흔히 하는 작업이다.
GCD 는 이를 위한 사용하기 쉬운 dispatch_once 함수라는 도구를 제공한다.
토큰은 static 이나 global 로 선언해야 한다.
반드시 한 번만 실행되어야 하는 각 블록을 위해 전달되는 토큰이 같은 것이어야 하기 때문이다.
#46 : dispatch_get_current_queue 사용을 피하라
dispatch_get_current_queue 함수는 기대한 것처럼 동작하지 않는다.
이 함수는 폐기되었다.
그래서 디버깅할 떄만 사용해야 한다.
디스패치 큐는 계층 구조로 구성되어 있다.
그렇기 때문에 현재 큐를 간단히 단일 큐 객체로 표현할 수 없다.
큐 전용 데이터는 dispatch_get_currnet_queue 를 사용해야 하는 곳을 대체할 수 있다.
이를 사용하면 재진입 코드 때문에 발생하는 데드락을 회피할 수 있다.
#47 : 시스템 프레임워크를 숙지하라
활용할 수 있는 시스템 프레임워크가 많다.
가장 중요한 프레임워크는 Foundation 과 CoreFoundation 인데 앱 개발에 필요한 많은 핵심 기능을 제공한다.
오디오, 비디오 처리, 네트워킹, 데이터 관리 같은 많은 공통 작업을 위한 프레임워크가 있다.
오브젝티브-C 가 아닌 오직 C 만으로 작성된 프레임워크가 있다는 것을 꼭 알고 있어야 한다.
그래서 좋은 오브젝티브-C 개발자가 되기 위해서는 C 의 기본 개념을 이해해야 한다.
#48 : 반복문에는 블록 열거를 사용하라
컬렉션을 열거하는 방법은 네 가지가 있다.
for 루프는 가장 기초적인 방법이다.
그 다음은 NSEnumerator 와 빠른 열거를 사용해 열거하는 것이다.
가장 현대적이고 진보된 방법은 블록 열거 메서드를 사용하는 방법이다.
블록 열거는 추가 코드 없이 GCD 를 활용하여 병렬로 열거를 할 수 있게 한다.
이는 다른 열거 방법으로는 하기 어려운 일이다.
만약 알고 있다면 블록 시그너처에서 정확한 객체 타입을 가리키도록 수정하라.
#49 : 커스텀 메모리 관리 시맨틱을 가진 컬렉션을 만들기 위해 무비용 전환을 사용하라
무비용 전환은 Foundation 의 오브젝티브-C 객체와 CoreFoundation 의 C 데이터 구조체 간에 캐스팅을 할 수 있게 한다.
CoreFoundation 으로 내려가 컬렉션을 생성하면 컬렉션이 자신의 데이터를 다룰 때 사용되는 다양한 콜백을 정의할 수 있다.
무비용 전환을 이용해 캐스팅하면 커스텀 메모리 관리 시맨틱을 갖는 오브젝티브-C 컬렉션을 만들 수 있다.
#50 : 캐시가 필요할 때 NSDictionary 보다는 NSCache 를 사용하라
캐시로 사용한 NSDictionary 를 NSCache 로 변경하는 것을 고려하라.
캐시는 최적화된 메모리 반납 기능(pruning behavior)을 제공하고 스레드 안전할 뿐 아니라 사전처럼 키를 복사하지 않는다.
객체가 캐시에서 제거될 기준 지표로 수 제한(count limit)과 비용 제한(cost limit)을 사용하라.
그러나 그러한 지표를 너무 곧이곧대로 의지해서는 안 된다.
이 지표들은 단지 캐시가 보조로 참조하는 값일 뿐이다.
캐시를 NSPurgeableData 객체와 함께 사용하라
이 객체는 자동 반납되는 데이터(autopurging data)를 제공한다.
이 기능은 이 객체가 자동으로 반납되면 캐시에서도 자동으로 제거된다는 것을 말한다.
캐시를 잘 사용하면 앱 응답 시간을 꽤 줄일 수 있다.
네트워크에서 가져오거나 디스크에서 읽는 것처럼 다시 계산하는 비용이 큰 데이터만 캐싱하라.
#51 : initialize 와 load 메서드는 간결하게 만들라
클래스가 load 메서드를 구현했고 그 메서드가 호출되면 클래스는 로드 단계(load phase)로 진입한다.
이 메서드는 또한 카테고리에도 있을 수 있다.
그것으로 인해 클래스는 항상 카테고리가 로드되기 전에 로드된다.
load 메서드는 다른 메서드들이 지키는 일반적인 재정의 방침을 따르지 않는다.
클래스가 최초로 사용되기 전에 initialize 메서드가 호출된다.
이 메서드는 일반적인 재정의 규칙을 따른다.
그래서 이 메서드는 클래스가 초기화되었는지 확인하는 최적의 장소다.
load 와 initialize, 이 두 메서드는 모두 간결하게 유지해야 한다.
그렇게 하면 앱이 계속 응답을 잘 하고 상호 참조가 발생할 가능성을 줄이는 데 도움이 된다.
컴파일 시간에 할 수 없는 전역 상태값을 설정하는 것은 initialize 메서드에서 하라.
#52 : NSTimer 가 타깃을 리테인한다는 사실을 기억하라.
NSTimer 객체는 타이머의 이벤트가 발생되거나 명시적인 invalidate 호출을 통해 타이머가 종료되기 전까지 자신의 타깃을 리테인한다.
반복하는 타이머를 사용하면서 타이머의 타깃이 타이머를 리테인하면 리테인 순환이 쉽게 발생한다.
이는 객체 그래프의 다른 객체에 의해 직간접적으로 생길 수 있다.
NSTimer 가 블록을 사용하도록 확장하면 리테인 순환을 깰 수 있다.
아직 공식적으로 NSTimer 인터페이스에 이 기능이 추가되지 않았기 때문에 현재는 카테고리를 이용해 이 기능을 추가할 수 있다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[ios] GCD ( Grand Central Dispatch ) Tutorial (0) | 2017.10.24 |
---|---|
[ios/design pattern] private 메서드 정의하기 (0) | 2017.10.23 |
[Effective Objective-C] 목차와 요약을 통해 한 눈에 알아보는 Effective Objective-C #49~#52 (0) | 2017.10.21 |
[Effective Objective-C] #52 NSTimer 가 타깃을 리테인한다는 사실을 기억하라 (0) | 2017.10.20 |
[Effective Objective-C] #51 initialize 와 load 메서드는 간결하게 만들라 (0) | 2017.10.19 |
댓글