본문 바로가기
프로그래밍 놀이터/iOS

[Objective-C] NSObject 클래스와 런타임 시스템

by 돼지왕 왕돼지 2017. 12. 29.
반응형

 [Objective-C] NSObject 클래스와 런타임 시스템


출처 : OS X 구조를 이해하면서 배우는 Objective-C Chap 8.

#elif, #else, #if, (sel)0, /usr/include/Objc, 32bit, 32비트, 64bit, 64비트, @defs, @selector(), action method, alloc, alloc init new, ANSI C, appkit, appkit framework, AppKit.framework, AppKit.h, application framework, application kit framework, application 프레임워크, arc 초기자 이외 self 대입, assign, autorelease, cgfloat, CLASS, class 형, Cocoa, cocoa framework, Cocoa Touch, cocoa.h, code resources, core data, core foundation, core foundation framework, core graphics, dealloc, defined, description, Double, Finalize, float, Foundation, Foundation Framework, Foundation.framework, foundation.h, hash, Headers, hidden arguments, IBAction, iboutlet, ilp32, imp, init, initialize, instanceMethodForSelector, instancesRespondToSelector, iNT, IOS, ISA, isa 변수, isEqual, iskindofclass, isMemberOfClass, issubclassofclass, llp64, long, long long, lp32, lp64, m32, M64, Mac OS X, mach 커널, macro 정의, meta class, methodForSelector, new, NSInteger, nsobjcruntime.h, nsobject 클래스, objc.h, Objective-C 와 Cocoa 환경, outlet, performSelector, pointer, preprocessor, printf, release, Resources, respondsToSelector, retain, retain cycle, retainCount, runtime system, scanf, SEL, Self, self 대입, superclass, system framework, system/library/frameworks, target action paradigm, typedef, UIKit Framework, uikit 프레임워크, uikit.h, umbrella framework, versions, void, weak, xcode, xml, [Objective-C] NSObject 클래스와 런타임 시스템, _cmd, __lp64__, 가비지 컬렉션, 객체 비교, 객체 생성, 구조체, 구조체 포인터, 데이터 모델, 디렉터리, 레거시 런타임, 루트 클래스, 매크로, 메모리 영역 관리, 메시지 송신 실행 속도, 메시지 탐색, 메시지 함수 구현, 메타 클래스, 모던 런타임, 부동소수점형, 새로운 런타임 시스템, 셀렉터와 sel 형, 송신된 메시지 대응, 숨은 인수, 실행 코드 첫 주소, 아울렛, 액션 메서드, 액션 패러다임, 옵션, 우산 프레임워크, 운영체제, 유닉스 라이브러리, 유지 순환, 인스턴스 변수, 인스턴스 생성과 해제, 자료형, 정수형, 조건부 컴파일, 직접 호출, 취약하지 않은, 캐시, 컴파일, 컴파일 지시자, 클래스 객체와 루트 클래스, 클래스 라이브러리, 클래스와 인스턴스,


Notice : 정리자(돼지왕 왕돼지)가 remind 하고 싶은 내용이나 모르는 내용 기반으로 정리하는 것이기 때문에 구체적인 내용은 책을 사서 보시기를 권장드립니다.


8.1. NSObject 클래스


* 8.1.1. 루트 클래스의 역할


-

런타임 시스템은 Objective-C 용 운영체제 같은 것으로, 객체 생성, 해제에 따른 메모리 영역 관리와 송신된 메시지에 대응하는 메서드 검색 등을 한다.



-

보통은 프로그램에서 런타임 시스템 기능을 직접 사용하지 않는다.

이런 기본 기능은 루트 클래스인 NSObject 에 있는 메서드로 제공된다.

다시 말해 루트 클래스는 런타임 시스템에 대한 인터페이스 역할이라고 할 수 있다.




* 8.1.2. 클래스와 인스턴스


-

NSObject 의 인스턴스 변수는 오직 하나뿐이다.

바로 Class 형 변수인 isa 이다.

모든 인스턴스 객체는 isa 에 따라 자신이 속한 클래스 객체를 참조한다.

isa 는 인스턴스와 클래스의 관계를 결정하는 무척 중요한 변수이므로, 서브 클래스에서 값을 변경해서는 안 된다.



-

인스턴스에 속한 클래스를 조사하려면 isa 를 참조하는 것이 아니라 인스턴스 메서드 class 를 사용해야 한다.



-

NSObject 메서드는 NSObject 자체에서 사용되기보다는 서브 클래스인 각종 클래스 및 인스턴스에서 공통으로 사용된다.


-(Class) class

     리시버가 속한 클래스의 클래스 객체를 돌려준다.


+(Class) class

     클래스 객체를 돌려준다.


-(id) self

     리시버 자체를 돌려준다.


-(BOOL) isMemberOfClass: (Class) aClass

     리시버가 인수 클래스 인스턴스인가 조사한다.


-(BOOL) isKindOfClass: (Class) aClass

     리시버가 인수 클래스 인스턴스인지, 혹은 그 클래스의 서브 클래스 인스턴스인지를 조사한다.


+(BOOL) isSubclassOfClass: (Class) aClass

     isKindOfClass: 와 같은 형태이나 class method 이다.


-(Class) superclass

     리시버가 속한 클래스의 슈퍼 클래스의 클래스 객체를 return


+(Class) superclass

     위와 동일한 녀석, but class method.




* 8.1.3. 인스턴스 생성과 해제


-

+(id) alloc

     리시버로 지정된 클래스 인스턴스를 생성한다.

     생성된 인스턴스 초기화를 완전히 하기 위해 늘 init 또는 init으로 시작하는 메서드 또는 초기자와 조합해서 사용해야 한다.

     서브 클래스에서 alloc 을 재정의해서는 안 된다.


-(void) dealloc

     인스턴스를 해제한다.

     release 결과로 호출된다.

     서브 클래스로 dealloc 를 재정의할 때 빼고는 프로그램에서 직접 호출해서는 안 된다.


-(oneway void) release

-(id) retain

-(id) autorelease

-(NSUInteger) retainCount

-(void) finalize



-

dealloc 에서 retainCount 까지는 수동 카운터 관리 방식으로 메모리 관리를 할 때 유효하고, ARC 에서는 사용할 수 없다.

finalize 는 가비지 컬렉션을 사용할 때 유효하다.




* 8.1.4. 초기화


-

-(id) init

     alloc 으로 생성된 인스턴스를 초기화한다.


+(void) initialize

     클래스 초기화.

     클래스 안에서 공통으로 쓰는 변수 초기 설정 등을 할 때 사용한다.

     이 메서드는 그 클래스에 처음으로 온 메시지보다 먼저 자동으로 실행된다.

     직접 호출해서는 안 된다.


+(id) new

     일반적으로 alloc 과 init 을 조합한 것이다.




* 8.1.5. 객체 비교


-

-(BOOL) isEqual: (id) anObject

-(NSUInteger) hash



-

객체는 원칙적으로 같은 id 값, 즉 객체를 가리키는 포인터가 같다면 동일하다고 본다.

서브 클래스는 여기에 “같은 값을 가진 것” 을 동일하다고 판단하도록 정의가 확장된다.

이 때 무엇을 가지고 “같은 값” 이라고 하는지는 자유롭게 정의할 수 있지만 같은 값을 가진 객체가 늘 같은 해시값을 돌려주도록 메서드 hash 도 재정의해야 한다.

하지만 같은 해시값을 돌려주더라도 같은 객체라고는 할 수 없다.




* 8.1.6. 객체 내용 설명


-

+(NSString*) description

     보통 클래스명을 돌려준다.


-(NSString*) description




8.2. 메시지 송신 구조

* 8.2.1. 셀렉터와 SEL 형


-

셀렉터의 자료형이 SEL 이다.

셀렉터 표기로 SEL 형 데이터를 프로그램에서 얻을 수 있는 컴파일러 지시자 @selector() 가 있다.



-

SEL 형의 변수가 유효한 셀렉터를 값으로 가지고 있지 않다는 것을 나타내고 싶을 때 전통적인 NULL 을 사용할 때가 많다.

혹은 (SEL)0 이라는 표현을 쓰기도 한다.



-

SEL 형 값을 사용해서 메시지 송신이 가능하다.

(id) performSelector: (SEL) aSelector

(id) performSelector: (SEL) aSelector withObject: (id) anObject



-

함수 포인터는 그 함수가 존재하는 메모리 주소를 나타낸다.

그 포인터에 대응하는 함수가 무엇인가를 컴파일 할 때 결정하게 되므로 지정하지 않은 함수는 실행되지 않는다.

SEL 형은 메서드명에 상당하는 것으로 리시버가 되는 객체가 무엇인지에 따라 메서드가 실행될 대 정해진다.




* 8.2.2. 메시지 탐색


-

모든 인스턴스에는 isa 라는 Class 형 인스턴스 변수가 있는데, 이것이 클래스 객체를 나타낸다.

우선 런타임 시스템은 메시지와 같은 셀렉터를 가진 메서드가 클래스에 있는지 확인한다.

있으면 그 메서드를 실행하지만, 없으면 클래스 객체 안에 슈퍼 클래스를 나타내는 포인터가 있으므로 그것을 따라가서 슈퍼 클래스에 메서드가 있는지 확인한다.

최종적으로 루트 클래스까지 가서 찾아봐도 해당 메서드가 발견되지 않으면 실행 에러가 발생한다.



-

메시지를 보낼 때마다 해당하는 메서드를 탐색하면 호출 처리가 무척 무거워진다.

따라서 런타임 시스템은 어떤 클래스가 어떤 셀렉터에 대응하는 메서드를 가지고 있고, 그 정의 장소가 어디인지 같은 정보를 캐시에 넣어둔다.

그래서 다음에 같은 메시지가 오면 다시 탐색하지 않고 캐시 정보를 이용할 수 있게 한다.



-

어떤 객체가 지정한 셀렉터에 대응하는 메서드를 구현했는지 어떤지는 동적으로 확인할 수 있다.


-(BOOL) respondsToSelector: (SEL) aSelector


+(BOOL) instancesRespondToSelector: (SEL) aSelector




* 8.2.3. 메시지 함수 구현


-

메시지 송신이 함수 호출에 비해 조금 느리다는 건 사실이지만 객체지향의 유연성을 포기하면서까지 속도를 올릴 만한 가치는 없다.

메시지 송신은 충분히 빠르다.



-

어떤 객체의 메서드에 대응하는 함수를 가리키는 포인터를 얻을 땐 그 객체에 대해 다음 메서드를 사용한다.


-(IMP) methodForSelector: (SEL) aSelector

     인수로 지정한 셀렉터에 대응하는 메서드를 확인해서 그 구현이 있는 함수 포인터를 돌려준다.

     이 메서드는 인스턴스 객체에서도, 클래스 객체에서도 사용할 수 있다.

     인스턴스 객체에 대해 사용하면 인스턴스 메서드에 대응하는 함수가 반환되고,

     클래스 객체에 대해 사용하면 클래스 메서드에 대응하는 함수가 반환된다.


+(IMP) instanceMethodForSelector: (SEL) aSelector

     인수로 지정한 셀렉터에 대응하는 인스턴스 메서드를 조사해서 그 구현이 있는 함수 포인터를 돌려준다.



-

IMP 형은 다음 정의와 같은 형식으로 메서드에 대응하는 함수 포인터를 나타낸다.


typdef id (*IMP)(id, SEL, …);



cf) 함수 포인터


-

함수의 포인터가 나타내는 건 그 함수 실행 코드의 첫 주소라고 생각해도 된다.

함수 포인터는 변수나 배열에 대입하거나 다른 함수에 인수로 넘길 수 있다.

구조체 멤버에 넣어 구조체를 객체처럼 다룰 수도 있다.

int funcA(double x, int *err); // 함수 signature


int (*p) (double, int *); // 함수 포인터


typedef int (*t_func) (double, int *); // t_func 라는 이름의 함수 포인터


t_func fptr = funcA;

x = (*fptr) ( 1.4142, &error ); // 함수 포인터를 강조하기 위해 보통 사용되는 방법

x = fptr( 1.4142, &error ); // 이렇게 쓸 수도 있지만 그냥 함수같아서 위의 방법으로 주로 사용



-

IMP funcp = [foo methodForSelector:@selector(setBox:title:)];

xyz = (*funcp)(foo, @selector(setBox:title:), param1, param2);

메서드에 대응하는 함수는 메서드에서 선언한 인수 말고도 반드시 리시버가 되는 객체와 메시지 셀렉터를 인수로 받는다.

명시되진 않았지만 이들은 메서드 속에서 참조가 가능해 숨은 인수(hidden arguments)라고 부른다.

첫 번째 인수인 리시버는 실제로는 self, 두번째 인수인 셀렉터는 _cmd 라는 변수명으로 참조가 가능하다.



-

IMP 형은 메서드 인수 형식을 명시하지 않으므로 실제 프로그래밍에서는 인수 형식이 구체적으로 정해진 함수 포인터에 캐스트해서 사용하는 게 안전하다.






* 8.2.4. self 에 대입


-

init 에서 self 에 반환값을 대입하지 않을 수도 있다.

-(id) init{

     [super init];

     return self;

}

그러나 이 작성법이 항상 올바른 것이 아니다.

초기화에 실패하는 것 말고도 슈퍼 클래스 초기자가 최초의 self 와 다른 객체를 초기화해서 돌려줄 가능성이 있다.


초기자를 정의할 때는 슈퍼 클래스 초기자에서 반환된 값을 self 에 대입한 다음 nil 이 아니라는 걸 확인하는 방법이 안전하다.



-

ARC 에서는 초기자 이외의 메서드에서 self 에 대입하면 컴파일 에러가 발생한다.




* 8.2.5. 메시지 송신 실행 속도


-

프로그램 내용에 따라 다르긴 하지만 Objective-C 프로그램은 같은 C 프로그램보다 조금 늦는 정도라고 생각하면 된다.




* 8.2.6. 클래스 객체와 루트 클래스


-

클래스 객체도 객체이므로 루트 클래스인 NSObject 를 상속한 클래스의 ‘인스턴스’로 다룬다.

클래스 객체 클래스를 메타 클래스(meta class)라고 부른다.



-

객체를 나타내는 id 형으로 클래스를 나타내는 Class 형은 둘 다 실체는 구조체 포인터로, 그 정의는 /usr/include/Objc 밑에 있는 objc.h 같은 헤더 파일에 정의되어 있다.



-

임의의 클래스 객체가 루트 객체를 상속한 메타 클래스 인스턴스라고 볼 수 있다.

이것은 “클래스 객체가 루트 객체의 인스턴스 메서드를 실행할 수 있다” 는 걸 의미한다.



-

모든 클래스의 인스턴스 객체는 루트 클래스의 인스턴스 메서드를 실행할 수 있다.

모든 클래스의 클래스 객체는 루트 클래스의 클래스 메서드를 실행할 수 있다.

모든 클래스의 클래스 객체는 루트 클래스의 인스턴스 메서드를 실행할 수 있다.




* 8.2.7. 타깃 - 액션 패러다임


-

Application 프레임워크, UIKit 프레임워크에서는 타깃-액션 패러다임 (target-action paradigm) 또는 타깃-액션 메커니즘을 사용한다.



-

Application 프레임워크의 타깃-액션 패러다임에서 메시지로 사용되는 건 다음과 같이 id 형 인수를 하나만 가지며 반환값이 없는 형식의 메서드 뿐이다.

이런 형식의 메서드를 액션 메서드(action method)라고 부른다.

-(void) XXXX:(id)sender;


메시지 인수에는 보통 그 GUI 부품의 id 가 넘어간다.



-

Application 프레임워크의 액션 메서드 형식은 한 종류뿐이지만 UIkit 프레임워크는 다음 세 가지 형식의 액션 메서드가 된다.


1. -(void) XXXXX:

2. -(void) XXXXX: (id)sender:

3. -(void) XXXXX: (id)sender forEvent: (UIEvent*) event;




* 8.2.8. Xcode 에서 액션 메서드와 아울렛 작성


-

IBAction 은 매크로로, void 로 정의된다.



-

인스턴스 변수 또는 읽기/쓰기가 가능한 선언 프로퍼티 중 GUI 부품과 다른 객체를 참조하기 위해 이용하는 것을 아울렛(Outlet)이라고 부른다.


이 IBOutlet 도 매크로로 아무것도 없는 공백으로 정의되어 있다.



-

IBOutletCollection(NSButton) NSArray* buttons;

이렇게 작성하면 Xcode 에서 여러 부품(이 경우는 버튼)에서 이 아울렛에 접속할 수 있다.

IBOutletCollection(NSButton) 도 매크로로 컴파일하면 인수를 포함해서 아무것도 없는 공백으로 변환된다.



-

ARC 를 사용해서 개발할 때 객체 사이의 유지 순환이 발생하지 않도록 주의해야 한다.

따라서 중요한 객체 결합 이외에는 약한 참조로 연결하는 것이 좋다.

선언 프로퍼티는 assign 이나 weak 로 지정해 둔다.




8.3. Objective-C 와 Cocoa 환경


* 8.3.1. Cocoa 환경과 Mac OS X


-

Cocoa 환경이라고 부르는 것은 보통 AppKit 과 Foundation 을 중심으로 한 부분으로 문맥에 따라서는 Core Foundation 과 Core Data 도 포함한다.




* 8.3.2. Cocoa Touch 와 iOS


-

Foundation 과 UIKit 은 프레임워크로, 주로 이 두 기능을 조합해서 만들 수 있는 GUI 환경을 Cocoa Touch 라고 부른다.




* 8.3.3. 프레임워크


-

프레임워크 중 무척 중요한 프레임워크는 Foundation 프레임워크와 Application 프레임워크(Appkit 프레임워크, Application Kit 프레임워크라고도 부른다.), Core Foundation 프레임워크, System 프레임워크이다.

Foundation 프레임워크에는 NSObject 를 비롯해 Objective-C 기본 라이브러리가 들어 있다.

Core Foundation 프레임워크는 Cocoa 의 기본 데이터 구조를 C 언어로 작성한 것이다.

Application 프레임워크에는 Cocoa GUI 기반이 되는 윈도우 환경 관련 클래스가 들어 있다.

System 프레임워크는 Cocoa 기반이 되는 Mach 커널 관련 및 유닉스 관련 라이브러리를 포함하며, 늘 프로그램 실행에 관련되므로 프로그램 컴파일시에 -framework 옵션으로 지정하지 않아도 된다.



-

Cocoa 프레임워크는 실제로는 Foundation 프레임워크, Application 프레임워크 및 Core Data 프레임워크를 묶는 역할을 한다.

여러 프레임워크를 뭉쳐서 하나의 프레임워크로 제공하는 구조를 우산 프레임워크(umbrella framework)라고 부른다.

단, 우산 프레임워크는 애플 사가 기능을 제공하기 위한 수단으로만 사용하고 있는 것으로, 다른 개발자가 우산 프레임워크를 제공하는 것은 추천하지 않는다.



-

iOS 는 Foundation 프레임워크와 UIKit 프레임워크의 두 가지로 Cocoa Touch 가 구현되어 있다.

Foundation 프레임워크는 Mac OS X 와 상당히 공통적인 부분이 있어 문자열과 배열 같은 기본적인 클래스라면 거의 같은 방법으로 프로그래밍이 가능하다.

UIKit 은 사용자 인터페이스를 담당하는 프레임워크로, 기본 GUI 부품과 화면 탭 같은 이벤트를 다루기 위한 클래스가 정의되어 있다.

Core Foundation 프레임워크 데이터 구조를 통해 다른 프레임워크나 디바이스용 기능을 다루기도 한다.




* 8.3.4. 프레임워크 구성과 헤더 파일


-

Mac OS X 디렉터리 구성은 각 버전에 따라 변하기도 하지만 시스템 제공 프레임워크는 /System/Library/Frameworks 에 있다.

또한 /Library/Frameworks 같은 각 앱용 프레임워크가 설치되기도 한다.

각각의 앱도 전용 프레임워크를 앱 패키지 속에 가질 수 있다.



-

예를 들어 Foundation 프레임워크와 Application 프레임워크는 각각 다음 경로에 존재한다.


Foundation 프레임워크

     /System/Library/Frameworks/Foundation.framework


Application 프레임워크

     /System/Library/Frameworks/AppKit.framework



-

iOS 개발환경을 설치한 Mac 이라면 iOS 프레임워크는 개발환경 관련 파일의 깊은 곳에 있다.

iOS 5 이라면 프레임워크는 다음 경로에 존재한다.

실제 기기와 시뮬레이터는 CPU 가 다르므로 프레임워크도 두 종류이다.


/Developer/Platforms/iPhoneOS.platform/Deverloper/SDKs/iPhoneOS5.0sdk/System/Library/Frameworks

/Developer/Platforms/iPhoneSimulator.platform/Deverloper/SDKs/iPhoneSimulator5.0sdk/System/Library/Frameworks



-

프레임워크는 하나의 디렉터리로 그 안에 다음과 같은 요소가 저장되어 있다.


1. 클래스 라이브러리 : 프레임워크와 같은 이름의 파일

2. CodeResources : 파일 해시값 등을 기록한 XML 파일

3. Headers : 헤더 파일이 저장된 디렉터리

4. Resources : 각 나라별 언어 파일 같은 각종 리소스가 저장된 디렉터리

5. Versions : 프레임의 각종 버전이 저장된 디렉터리


Mac OS X 은 대부분의 경우 1~4 는 5 안의 심볼릭 링크가 걸려 있다.

iOS 의 경우 리소스는 Resources 디렉터리가 아니라 프레임워크 디렉터리 바로 밑에 있다.



-

#import <Foundation/NSObject.h> 는 Foundation 이름의 디렉터리 밑에 헤더가 있는 것이 아니다.

임포트 할 때 이렇게 작성하면 컴파일러( 정확하게는 프리 프로세서 )가 해당하는 프레임워크 속에서 헤더 파일을 찾아 임포트해준다.



-

Foundation 프레임워크에는 Foundation/Foundation.h 라는 헤더 파일이 있다.

이 파일을 임포트하면 프레임워크의 모든 클래스 헤더 파일과 관계된 헤더 파일을 한꺼번에 임포트한 것과 같다.

마찬가지로 AppKit/AppKit.h, UIKit/UIKit.h 헤더 파일도 있다.



-

Mac OS X 에는 Cocoa/Cocoa.h 도 있다.

GUI 기능을 사용하는 앱을 만들 때 Cocoa/Cocoa.h 를 임포트하면 Foundation 프레임워크와 Application 프레임워크 및 Core Data 프레임워크 관련 모든 헤더를 임포트한 것과 같아져 무척 편리하다.




8.4. 새로운 런타임 시스템

* 8.4.1. 64비트 모델 대응과 모던 런타임


-

64비트 모델의 런타임 시스템은 32비트를 단순히 64비트로 대응한 것이 아니라 새로운 기능과 변경을 추가했다.

iOS 4.0 이후에 채용된 런타임 시스템에도 이런 새로운 기능이 있다.

Mac OS X 64비트 모델의 런타임과 iOS 런타임을 “모던 런타임” 이라고 부르며 Mac OS X 32 비트 모델 런타임을 "레거시 런타임"이라고 부른다.




* 8.4.2. 데이터 모델이란


-

대다수 UNIX 시스템과 마찬가지로 Mac OS X 은 프로그래밍 데이터 모델로 ILP32 와 LP64 를 채용한다.

ILP32 라는 명칭은 int형, long형, 포인터형이 32비트라는 뜻이다.

LP64는 long 형과 포인터형이 64비트라는걸 뜻한다.



-

ILP32로 long long 형의 비트 수는 정해져 있지 않지만 Mac OS X 에서는 64비트를 이용할 수 있다.



-

C 컴파일러에서는 -m32 또는 -m64 옵션으로 어떤 데이터 모델용으로 코드를 생성할 것인지를 지정한다.



-

LP32  - int : 16 / long : 32 / pointer : 32

ILP32 - int : 32 / long : 32 / long long : (64) / pointer : 32

LP64 - int : 32 / long : 64 / long long : 64 / pointer : 64

LLP64 - int : 32 / long : 32 / long long : 64 / pointer : 64




* 8.4.3. 64비트 모델과 정수형


-

Cocoa 환경은 NSInteger 형을 도입해서 32비트 모델에서는 int형, 64비트 모델에서는 long 형이 되도록 정의했다.


이러한 형은 헤더 파일 NSObjCRuntime.h 에 정의되어 있으며, 매크로 __LP64__ 는 64비트 모드에서 컴파일할 때 참이 된다.



-

NSInteger 형을 사용해서 프로그램을 만들 때 문제가 되는 것은 printf 와 scanf 함수에 있는 서식문자열의 지정 방법이다.


printf 는 다음과 같이 cast 를 이용해서 해결할 수 있다.

NSInteger n = …;

printf( “Input=%ld\n”, (long)n);


scanf 의 경우는 서식 자체를 매크로로 만드는 방법이 추천된다.

#if __LP64__

#define FMTd “ld"

#else

#define FMTd “d"

#endif


NSInteger n;

scanf(%” FMTd, &n);

printf(“Input=%” FMTd “\n”, n);




* 8.4.4. Core Graphics 부동소수점형


-

CGFloat 형을 도입해 float 형과 double 형의 어느 쪽이 실제로 사용되는지 의식하지 않아도 된다.




* 8.4.5. 취약하지 않은 인스턴스 변수


-

모던 런타임에서 인스턴스 변수는 취약하지 않은(non-fragile)이라고 하며 레거시 런타임 인스턴스 변수는 취약하다고(fragile) 한다.



-

프레임워크로 제공하는 클래스에 대해 앱 쪽에서 서브 클래스를 정의해서 사용하는 경우를 생각해보자.

프레임워크 쪽의 클래스가 변경되었을 때 지금까지의 런타임 시스템에서는 그것을 이용하는 앱 쪽 코드도 새로 컴파일해야만 했다.

이런 것이 취약한 바이너리 인터페이스 문제이다.



-

모던 런타임에서는 이 문제가 개선되어 인스턴스 변수의 변경 정도라면 앱을 재컴파일 하지 않아도 동작한다.

이것을 “취약하지 않은” 인스턴스 변수라고 한다.

하지만 허용된 것은 선언 순서의 변경과 새로운 변수 추가 정도로 변수 자체가 삭제되거나 형식이 변했을 때는 제대로 동작하지 않는다.



-

기존의 Objective-C 에서 클래스에서 선언한 인스턴스 변수 모음을 @defs 라는 컴파일 지시자를 사용해 구조체 선언으로 다루며 이용할 수 있었다.

취약하지 않은 인스턴스 변수 구현과 함께 Objective-C 2.0 에서는 그 기능을 폐지했다.



cf) 조건 지정 컴파일


-

ANSI C에서는 프리 프로세서 제어행으로 #else 와 함께 #elif 를 사용해 조건이 딸린 컴파일을 할 수 있었다.

조건식 안에는 defined 라는 연산자가 올 수 있다.

이 연산자는 인수로 매크로명을 받아서 그 매크로가 정의되어 있으면 참을 돌려준다.





반응형

댓글