[Objective-C] 기타
출처 : OS X 구조를 이해하면서 배우는 Objective-C 부록
A. Foundation 프레임워크 개요
* A.1 주요 클래스 개요
-
NSCountedSet : 변경 가능한 집합의 클래스, 같은 객체를 여러 번 포함할 수 있다.
NSOrderedSet, NSMutableOrderedSet : 여러 객체를 배열처럼 순서대로 저장하는데 같은 객체는 하나만 저장
NSIndexSet, NSMutableIndexSet : 배열의 인덱스로 사용하는 음수가 아닌 정수를 요소로 하는 집합. NSSet 과 상속 관계가 없다.
-
NSProcessInfo : 프로세스에 관련된 다양한 정보를 취득한다.
* A.2. 주요 프로토콜 개요
* A.3. 주요 함수와 형식
-
CGRectGetHeight(CGRect rect)
CGRectGetWidth(CGRect rect)
CGRectGetMaxX(CGRect rect)
...
CGRectGetMidX(CGRect rect)
...
CGRectContainsRect(CGRect aRect, CGRect bRect)
CGRectContainsPoint(CGRect rect, CGPoint point)
CGRectCGRectUnion(CGRect r1, CGRect r2)
...
B. Core Foundation 프레임워크 개요
* B.1. Core Foundation 이란
-
Core Foundation 프레임워크란 기본적으로 Cocoa Foundation 프레임워크를 기반으로 기본 데이터 조작 기능을 C 언어로 다시 작성한 것.
Core Foundation 에서 사용할 수 있는 자료형은 배열이나 집합 등의 컬렉션, 문자열, 바이트 배열, 날짜와 시각, URL 같은 Foundation 프레임워크와 공통인 것이 많다.
-
iOS 에서 QuickTime 이나 CoreGraphics, CoreVideo, AddressBook 같은 다양한 프레임워크에서 CoreFoundation 을 기본 자료형으로 이용한다.
Foundation 과 Application 또는 UIKit 프레임워크만으로 작성할 수 있는 앱이라면 일부러 Core Foundation 을 사용할 필요가 없다.
-
Core Foundation 의 기본은 C 언어이며 Objective-C 의 Foundation 프레임워크와 개념적으로는 공통된 부분이 많지만 유닉스 표준 라이브러리와 비교하면 꽤 복잡하다.
* B.2. 자료형과 형식명
-
Core Foundation 은 C 로 작성되었는데 데이터와 함수의 캡슐화를 구현하기 위해 그 자료형은 ‘객체’라고 부른다.
하지만 클래스에 해당하는 것은 없다.
기존 자료형을 상속해서 서브 클래스에 해당하는 자료형을 작성하는 기능도 없다.
-
Foundation 프레임워크와 같이 문자열, 배열, 사전 등의 자료형이 있어서 접두사 ‘CF’를 붙여 CFString, CFArray, CFDictionary 가 된다.
하지만 이것을 프로그램 안에서 형식명으로 사용하지 않는다.
문자열 객체를 다룰 때 프로그램에서는 CFStringRef 를 사용하고, 배열은 CFArrayRef, 사전은 CFDictionaryRef 를 사용한다.
CFStringRef 형은 문자열 객체의 실체를 가리키는 포인터이므로 NSString 으로 생각하면 NSString* 형에 해당한다.
-
Core Foundation 자료형의 실체는 구조체이지만 내부는 은폐되어 있어서 직접 조작할 수 없다.
제공되는 건 데이터를 조작하는 함수뿐이다.
이것을 Core Foundation 객체는 불투명형(opaque type)이라고 부르는 이유다.
opaque 는 안이 보이지 않는다는 의미이다.
* B.3. Foundation 객체와의 호환성
-
Core Foundation 객체는 Objective-C 의 Foundation 의 몇몇 클래스 인스턴스와 동등하게 취급할 수 있다.
런타임 시스템이 실제 데이터 구조로부터 어떤 객체인지 판단해서 적절히 처리하므로 서로 데이터 변환을 하지 않아도 된다.
이것을 ‘toll-free bridged(톨프리)’라고 부른다.
-
톨프리 관계에 있는 것은 캐스트를 해야 하지만 서로 변수에 대입하거나 메서드 인수나 반환값으로 사용할 수 있다.
-
톨프리 관계에 있는 것은 CFString과 NSString, CFArray 와 NSArray 등 주요 자료형이나 컬렉션을 포함해 많은 종류가 있다.
톨프리 관계에 있다면 레퍼런스에 적혀 있으며, Foundation 의 이런 클래스는 클래스 클러스터에도 있다.
-
톨프리 이외에도 Core Foundation 객체와 Foundation 프레임워크 객체는 몇몇 장면에서 동등하게 다룰 수 있다.
* B.4. CFType 과 파생 자료형
-
Core Foundation 에는 객체지향의 상속 기능이 없지만 Core Foundation 의 모든 객체를 총칭하는 자료형으로 CFType 이 있다.
프로그램 안에서 형식명으로 CFTypeRef 를 사용한다.
총칭이란 어떤 객체 대신 사용할 수 있다는 의미로, NSObject 또는 id 형의 관계와 비슷하다.
-
Objective-C 의 프로퍼티 리스트에 해당하는 CFPropertyList 자료형이 있는데, 이 자료형은 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, CFNumber 중 하나라는 걸 뜻한다.
-
CFMutableArray 는 CFArray 에서 파생(derive)한다고 표현한다.
객체지향의 상속 기능처럼 보이며 프로그램도 그렇게 생각하며 작성할 수 있다.
그러나 객체지향 언어의 상속 관계가 있는 것도 아니고 프로그래머가 새로운 서브 클래스에 해당하는 것을 만들 수 있는 것도 아니다.
어디까지나 Core Foundation 안에서만 자료형 사이에 상속과 비슷한 관계가 있다는 것 뿐이다.
-
객체가 실제로는 어떤 형식인지 알아보려면 다음 함수를 사용한다.
CFTypeID CFGetTypeID(CFTyperRef cf)
반환값 CFTypeID 는 부호 없는 정수로(uint), Core Foundation 의 각 자료형마다 값이 정해져 있다.
CFArray 에 해당하는 CFTypeID 값은 CFArrayGetTypeID() 함수로 알 수 있으므로 변수 obj 에 저장된 객체가 CFArray 인지는 다음과 같은 if 문으로 판단할 수 있다.
다른 자료형도 마찬가지로 CF___GetTypeID() 함수가 있다.
if (CFGetTypeID(obj) == CFArrayGetTypeID()){
// ...
}
* B.5. 명명규칙
-
Core Foundation 은 함수나 매크로명 앞에 대상이 되는 자료형을 밝히는 것이 원칙이다.
따라서 Objective-C 의 메서드명이나 매크로명과는 어순이 달라진다.
예를 들어
CFArrayAppendArray()
CFArrayGetCount()
CFArrayContainsValue()
Core Foundation 객체 전체, 즉 CFTypeRef 형을 대상으로 할 때나 자료형과 관계없을 때는 접두사 ‘CF’만 붙인다.
-
상수형은 첫머리에 소문자 ‘k’ 가 붙는다.
* B.6. 객체 생성과 메모리 관리
-
Core Foundation 객체는 동적으로 생성되는데 메모리 관리는 참조 카운터 방식을 사용한다.
Objective-C 와 같은 소유권 정책이 있다.
-
객체를 생성하는 함수로 새로운 객체를 만드는 것과 이미 존재하는 객체의 복사본을 만드는 것이 있다.
새로운 객체 생성은 Create, 복사는 Copy 를 함수명에 포함한다.
이런 함수를 호출해서 반환값으로 받은 객체에는 소유권이 발생한다.
반대로 반환값이 객체의 함수로 소유권의 전달이 발생하지 않을 때는 Get 같은 단어를 사용한다.
-
소유권 관련하여 아래 함수들이 있다.
CFRetain
CFRelease
CFGetRetainCount : 이 함수는 디버그용이며, 실제 프로그램에서 사용하면 안 된다.
-
Core Foundation 에는 CFArray 같은 컬렉션 객체가 있으므로 여기에 객체를 추가하거나 삭제할 때 Foundation 과 마찬가지로 참조 카운터의 증가와 감소가 발생한다.
-
앱이 수동 카운터 관리 방식으로 동작할 때 Core Foundation 객체는 Foundation 의 retain, release 메시지를 이용할 수도 있고, autorelease 메시지를 사용해서 자동 해제 풀을 이용 할 수도 있다.
하지만 id 형으로 캐스트해야 한다.
ex)
CFStringRef str = ...
[(id)str autorelease];
CFArrayAppendValue(arr, str);
* B.7. ARC 와 톨프리 객체의 형변환
-
ARC 를 사용할 때 컴파일러가 소유권 판단을 해서 retain 이나 release 에 해당하는 코드를 보충해주는 건 Objective-C 객체와 관련된 부분뿐이다.
Core Foundation 객체에 대해서는 아무것도 하지 않는다.
Core Foundation 쪽의 소유권 관리는 지금까지 해온 것처럼 수동으로 해야 한다.
-
ARC 는 Core Foundation 쪽 상황을 파악하지 않으므로 프로그래머가 적절하게 조절해서 소유권 관리에 모순이 생기지 않도록 해야 한다.
-
톨프리 관계에 있는 객체를 참조 카운터의 영향을 전혀 의식하지 않고 형변환하려면 다음처럼 캐스트한다.
CFDateRef cfdate = CFDateCreate(NULL, tm);
NSDate* d = (__bridge NSDate*)cfdate;
NSDate *nsdate = [[NSDate alloc] init];
CFDateRef e = (__bridge CFDateRef)nsdate;
-
위의 코드에서 위쪽 캐스팅은 강한 참조 변수에 대입하므로 참조 카운터를 하나 늘리도록 ARC 가 코드를 집어 넣어 카운터값이 2가 된다.
하지만 ARC 를 관리하는 것은 대입으로 늘어난 +1 뿐이므로 이대로는 객체가 해제되지 않는다.
이 대입 후 Core Foundation 쪽에서는 소유권을 관리하지 않고 ARC 쪽에 맡긴다고 하면 Core Foundation 쪽의 소유권을 파기해야 한다.
CFRelease() 를 사용할 수도 있지만 캐스트를 다음처럼 바꿔 쓰는 방법이 있다.
NSDate* d = (__bridge_transfer NSDate*)cfdate;
캐스트로 __bridge_transfer 를 지정하면 식 평가나 대입이 끝난 후 ARC 가 객체의 참조 카운터를 하나 줄인다.
결국 카운터값은 변화하지 않고 소유권은 Core Foundation 쪽에서 ARC 쪽으로 이동한 것처럼 보이게 된다.
이 캐스트에서 결과 형식은 Objective-C 객체여야만 한다.
-
위의 코드에서 아래쪽 캐스팅은 변수 nsdate 의 동작과 관련 없이 Core Foundation 쪽에서 객체를 계속 사용하려면 참조 카운터값을 하나 늘려둬야 한다.
CFRetain() 을 사용하는 방법도 있지만 다음처럼 캐스트를 작서할 수도 있다.
CFDateRef e = (__bridge_retained CFDateRef)nsdate;
이 캐스트에서 ARC 가 참조 카운터를 하나 늘린다.
-
정리하면, Core Foundation 쪽에서 생성한 객체를 ARC 쪽에 전달하려면 __bridge_transfer 를 지정한 캐스트를 사용하고 반대로 ARC 가 관리하는 객체를 Core Foundation 쪽에 전달하려면 __bridge_retained 를 사용한다.
참조 카운터에 변경이 없는 변수에는 __bridge 를 사용한다.
-
__bridge_retained, __bridge_transfer 를 캐스트로 사용하는 대신 인라인 함수를 사용할 수도 있다.
CFTypeRef CFBridgingRetain(id x);
id CFBridgingRelease(CFTypeRef X);
* B.8. 객체 사용자화
-
Core Foundation 자료형에는 상속 같은 구조는 없지만 객체를 생성할 때 그 객체가 어떻게 동작할지 사용자화할 수는 있다.
CFArray 객체를 생성하는 함수 CFArrayCreate() 를 보면..
CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks)
allocator 는 메모리 획득, 해제를 하기 위한 객체이다.
NULL 을 넘기면 미리 정해진 방법으로 메모리 관리를 하지만 다른 방법으로 malloc/free 를 사용해서 지정할 수도 있다.
values 와 numValues 는 배열 요소와 개수이다.
callBacks 는 구조체 포인터로, 미리 준비해둥 구조체 데이터에 있는 kCFTypeArrayCallBacks 포인터를 전달하면 미리 정해진 방법이 채용된다.
이 구조체를 직접 준비할 수 있다.
구조체는 몇몇 함수 포인터를 포함하며 요소가 추가될 때의 유지 동작, 요소가 삭제될 때의 해제 동작을 담당하는 함수 등을 지정할 수 있다.
이것을 이용해서 유지도 해제도 하지 않는 배열이나 유지 대신 복사본을 만드는 배열을 작성할 수도 있다.
* B.9. 편리한 문자열 기능
-
Core Foundation 문자열형 CFString 은 프로그램 안에 문자열 상수로 작성할 때 CFSTR() 매크로를 사용한다.
이 매크로의 인수로는 C 언어 문자열 상수만 사용하며, 아스키 코드 문자 이외의 문자를 사용하면 동작을 보증할 수 없다.
-
함수 CFStringCreateWithFormat() 은 printf 함수와 같은 서식 문자열을 사용해서 CFString 문자열을 생성하는데 서식 문자열도 CFString 이어야 한다.
-
서식 문자열에는 %@ 를 포함할 수 있다.
이 서식에 대응하는 인수가 Objective-C 객체일 때는 description 메서드가 적용된 결과의 문자열이 삽입된다.
-
CFType 으로 정의된 디버그용 함수로 CFShow() 가 있다.
이 함수는 객체를 인수로 그 내용을 나타내는 문자열을 에러 출력(stderr) 에 출력한다.
* B.10. 다른 프레임워크와의 관계
* B.11. CoreFoundation 자료형
C. 코딩 지침
* C.1. 지침이 필요한 이유
* C.2. 단어 조합 규칙
* C.2. 접두어
-
밑줄(언더스코어, _ ) 로 시작하는 이름을 접두어로 사용해선 안 된다.
밑줄로 시작하는 이름은 애플 사가 API 를 구성하는 동안에 사용하는 인스턴스 변수나 비공개 메서드명으로 사용한다고 예약되어 있다.
* C.4. 이름에 사용하는 단어
-
간결한 이름이 좋다고 해도 단어를 줄이는 건 피해야 한다.
코드를 작성한 사람은 줄임말이란 걸 감지할 수 있어도 배경 지식이 없는 사람이 보면 달라지기 때문이다.
널리 사용되고 역사가 오래된 줄임말은 허용한다고 코딩 가이드라인에 명시한다.
매개변수명이나 임시 변수명에는 생략형을 써도 된다.
-
같은 의미의 단어가 몇 가지 있을 때는 Cocoa API 와 같은 단어를 사용한다.
-
전치사 사용법도 초기화할 때 파라미터를 지정하는 with, 위치를 표시하는 at, 출력 방향을 나타내는 to 등 전형적인 예가 몇 가지 있으므로 Cocoa API 가 어떻게 하는지 따라하면 된다.
복수형 사용법도 주의해야 한다.
* C.5. 클래스명과 프로토콜명
-
프로토콜명도 클래스명과 같은 클래스명과 혼돈할 때가 많으므로 프로토콜명에는 ‘~ing’ 를 붙여서 구별하기도 한다.
클래스명 NSLock 과 NSLocking 이라는 프로토콜 명이 그 예이다.
* C.6. 카테고리를 포함한 파일명
-
카테고리의 인터페이스와 구현 부분을 파일로 만들 때 <클래스명+카테고리명.m> 같이 파일명을 붙이는 것이 표준처럼 되어 있다.
NSString+Utils.m 이 그 예이다.
* C.7. 메서드명 관련 일반 규칙
-
getter 에 보통 ‘get’ 은 붙이지 않는다.
get 이 메서드명에 있다면 대부분 포인터가 가리키는 장소에 있는 값을 복사해서 반환한다는 것을 뜻한다.
예를 들어 NSDate 의 메서드 bytes 는 데이터의 바이트 배열의 첫머리 포인터를 돌려줄 뿐인데, 다른 메서드 getBytes: 는 인수 포인터가 가리키는 메모리 영역에 데이터를 복사한다.
-
단순 나열하는 인수의 키워드에는 보통 and 를 붙이지 않는다. 나쁜 예이다.
하지만 메서드 구 개가 서로 다른 동작을 한다면 그 둘을 and 로 묶는 것은 괜찮다.
openFile:withApplication:andDeactivate:
* C.8. 접근자 규칙
* C.9. 객체 집합 조작
* C.10. 인수명에 대한 주석
* C.11. 인스턴스 변수명
* C.12. 함수명
* C.13. 형식명, 상수명, 문자열 상수명
-
무엇이 이미 발생한 것을 알리는 이름은 동사 과거형이 아닌 did + 동사원형 으로 하고,
반대로 지금부터 무엇이 일어난다는 것을 알리는 이름에는 will + 동사원형 으로 한다.
-
사전 키로 사용하는 문자열명 끝에는 Key, 비트합으로 마스크를 만드는 상수는 끝에 Mask 를 붙이는 등 명문화되지 않은 관습이 많다.
물론 그런 관습을 반드시 따라야만 하는 것은 아니다.
* C.14. 델리게이트의 메서드명과 알림명
-
'~를 해도 되는가’ 라고 문의하는 메시지가 오기도 하는데 이런 메시지명은 ‘should + 동사 원형’ 을 사용한다.
* C.15. 전역이자 유일한 이름
-
전역인 이름을 하나밖에 없는 것으로 하려면 자바 패키지명을 짓는 것과 같은 명명규칙을 추천한다.
com.company.appName
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[ios tutorial] UIImage RenderingMode ( Template Image ) (0) | 2018.01.28 |
---|---|
[도서 목차 정리] ] OS X 구조를 이해하면서 배우는 Objective-C (0) | 2018.01.13 |
[Objective-C] 키-값 코딩 (0) | 2018.01.11 |
[Objective-C] 병렬 프로그래밍 (0) | 2018.01.10 |
[Objective-C] 예외와 에러 (0) | 2018.01.09 |
댓글