본문 바로가기

코딩코오딩

[Apple Doc] Resource Programming Guide : Nib Files

Nib 파일은 OS X와 iOS에서 앱의 생성에 중요한 역할을 합니다. Nib 파일을 사용해서 코드로 작성하는 대신 Xcode를 사용하여 그래픽 사용자 인터페이스를 만들고 조작할 수 있습니다. 즉각적으로 결과를 볼 수 있기 때문에 다른 레이아웃과 설정을 매우 빠르게 실험해 볼 수 있습니다. 또한 어떤 코드 재작성도 없이 사용자 인터페이스의 많은 외관을 변경할 수 있습니다. 

 AppKit이나 UIKit 프레임워크들을 사용하여 생성된 앱에 대해 nib 파일은 추가적인 중요성을 갖습니다. 이 두 프레임워크들은 윈도우나 뷰 그리고 컨트롤들의 배치와 이러한 아이템들의 이벤트 처리 코드를 통합을 위해 nib 파일의 사용을 지원합니다. Xcode는 사용자 인터페이스의 컨트롤과 이 컨트롤들에 응답해야 하는 객체들을 연결하는 역할을 도와주는 프레임워크들을 결합하는 작업을 합니다. 이러한 통합은 nib파일이 로드된 후 요구되는 설정의 양을 크게 줄여주며 나중에 사용자 인터페이스와 코드사이의 관계를 쉽게 변경할 수 있게 해 줍니다. 

 

참고: nib파일을 사용하지 않고 비록 Objective-C 앱을 생성할 수 있지만, 이렇게 하는 것은 매우 드물며 추천하지 않는다. 앱에 따라 nib파일을 피하려면  nib파일을 사용하여 얻을 수 있는 동일한 결과를 얻기 위해 대량의 프레임워크 동작을 교체해야 할 수 있습니다.

 


Anatomy of a Nib File

  nib 파일은 윈도우, 뷰, 컨트롤 그리고 다른 많은 것들을 포함하는 사용자 인터페이스의 시각적 요소를 표현합니다. 또한 윈도우와 뷰 들을 관리하는 비 시각적 객체들도 표현할 수 있습니다. 가장 중요한 것은 nib 파일은 이 객체들을 Xcode에서 설정된 것으로 정확하게 표현합니다.런타임에 이 표현은 앱에서 객체와 객체의 설정들을 재생성하는 데 사용됩니다. 런타임에 nib 파일을 로드하면 Xcode 문서에 있는 객체의 정확한 모조품을 얻습니다. nib 로딩 코드는 객체를 초기화하고 구성하며 nib 파일에서 생성한 내부 객체 간 연결을 재설정합니다.

  다음 섹션에서는 AppKit과 UIKit 프레임워크에서 사용되는 nib 파일이 어떻게 구성되는지, nib에서 찾을 수 있는 객체들의 타입 그리고 이 객체들을 어떻게 효과적으로 사용하는지에 대해서 설명합니다.

 

About Your Interface Objects

  인터페이스 객체들은 사용자 인터페이스를 구성하기 위해 nib 파일에 추가한 것입니다. 런타임에 nib파일이 로드되면 인터페이스 객체는 nib-loading 코드에 의해 실제적으로 초기화 됩니다. 새로운 Nib 파일 대부분은 기본적으로 하나의 인터페이스 객체(일반적으로 윈도우나 메뉴 리소스)를 가지고 있습니다. 그리고 인터페이스 설계의 일부분으로서 nib 파일에 더 많은 인터페이스 객체들을 추가합니다. 이것은 nib 파일에서 일반적인 타입의 유형이며 일반적으로 nib파일을 처음 생성하는 이유이기도 합니다.

  윈도우, 뷰, 컨트롤 그리고 메뉴와 같은 시각적 객체를 나타내는 것 외에 인터페이스 객체는 비 시각적 객체들도 표현할 수 있습니다. 거의 모든 경우에 nib 파일에 추가한 비시각적 객체들은 시각적 객체를 관리하는 데 사용되는 추가 컨트롤러 객체입니다. 비록 앱에서 이 객체들을 생성할 수도 있지만, nib 파일에 추가하고 설정하는 것이 보다 더 편리한 경우가 많습니다. Xcode는 nib 파일에 컨트롤러나 비시각적 객체를 추가할 때 특별히 사용하는 제네릭 객체를 제공합니다. 또한 Cocoa 바인딩을 관리하는 데 사용되는 컨트롤러 객체도 제공합니다.

 

About the File’s Owner

  nib 파일에서 가장 중요한 객체중 하나는 Files's Owner 객체입니다. 인터페이스 객체와는 달리 File's Onwer 객체는 placeholder 객체로 nib 파일을 로드할 때 생성되는 것이 아니며 코드를 통해 생성되고 nib-loading 코드로 전달됩니다. 이 객체가 중요한 이유는 앱 코드와 nib 파일의 콘텐츠 사이의 메인 링크이기 때문입니다. 조금 더 명확하게 말하면 nib 파일의 콘텐츠를 책임지는 컨트롤러 객체입니다.

  Xcode 에서는 File's Owner와 nib 파일의 다른 인터페이스 객체들 사이의 연결을 생성할 수 있습니다. nib 파일을 로드할 때 nib-loading 코드는 지정한 대체 객체를 사용하여 이 연결들을 재성성 합니다. 이것은 nib 파일에서 객체들을 참조할 수 있게 해 주며 자동적으로 인터페이스 객체로부터 메시지를 받을 수 있게 해 줍니다.

About the First Responder

  nib 파일에서 First Responder는 앱에서 동적으로 결정되는 Responder chain의 첫 번째 객체를 표현하는 placeholder 객체입니다. 이 Responder chain은 설계 시점에 결정될 수 없기 때문에 이 First Responder placeholder는 앱의 Responser chain으로 가야 하는 모든 액션 메시지를 위한 대리 목표(stand-in target) 역할을 합니다. 메뉴 아이템은 일반적으로 First Responder placeholder를 대상으로 합니다. 예를 들어 윈도우 메뉴에서 최소화 메뉴 아이템은 특정 윈도우뿐 아니라 가장 앱의 가장 앞쪽의 윈도우도 숨기고, 복사 메뉴 아이템은 어떤 뷰나 컨트롤의 선택뿐만 아니라 현재 선택된 영역을 복사해야만 합니다. 앱의 다른 객체들도 First Responder의 대상이 될 수 있습니다.

  메모리에 nib 파일이 로드되면 First Responser 객체를 교체하거나 관리할 필요는 없습니다. AppKit이나 UIKit 프레임워크가 자동으로 앱의 현재 설정에 기반하여 First Responder를 지정하고 유지합니다.

  AppKit 기반의 Responder chain과 앱에서 이벤트가 어떻게 분배되는지에 대한 더 자세한 정보는 Cocoa Event Handling Guide의 Event Architecture를 참조하세요. iPhone엡에서의 Responder chain과 액션을 처리하는 것에 대한 정보는 Event Handling Guide for iOS을 참고하세요.

 

 

About the Top-Level Objects

  앱에 nib 파일이 로드되면 Cocoa는 Xcode에서 만든 객체들의 전체 그래프를 재생성합니다. 이 객체 그래프는 nib 파일에서 있는 윈도우와 뷰, 컨트롤, 셀, 메뉴, 그리고 커스템 객체들을 모두 포함합니다. top-level 객체들은 부모 객체를 가지지 않는 객체들의 부분 집합입니다. top-level 객체들은 일반적으로 오로지 윈도우, 메뉴 바, nib 파일에 추가한 커스텀 컨트롤러 객체만을 포함합니다(File's Owner, First Responder, 애플리케이션은 placeholder 객체이고 top-level 객체로 간주되지 않습니다.).

  일반적으로 File's Owner객체에서 nib 파일의 top-level 객체의 참조를 저장하기 위해서 outlet을 사용합니다. outlet을 사용하지 않으면 직접 nib-loading으로부터 top-level 객체를 가져올 수 있습니다. 앱은 이것들의 사용이 끝나면 release 해야 하는 책임이 있기 때문에 이 객체들을 가리키는 항상 포인터를 유지해야 합니다. 로드 시점에 nib 객체의 동작에 대한 더 자세한 정보는 Managing the Lifetimes of Objects from Nib Files를 참조하세요.

 

About Image and Sound Resources

  Xcode에서 nib 파일의 콘텐츠에 외부 이미지와 사운드 리소스를 참조할 수 있습니다. 몇몇 컨트롤과 뷰는 기본 구성의 일부로 이미지를 표시하거나 사운드를 재생할 수 있습니다. Xcode 라이브러리는 Xcode 프로젝트의 이미지와 사운드 리소스에 접근하고 nib 파일에 이 리소스들을 연결할 수 있게 합니다. 이 nib file은 이 리소스를 직접 저장할 수 없습니다. 대신에 이 리소스의 파일 이름을 저장하고 nib-loading 코드는 이 파일들을 찾을 수 있습니다.

  이미지와 사운드 리소스를 참조를 포함하는 nib 파일을 로딩하면 nib-loading 코드는 실제 이미지나 사운드 파일을 메모리로 읽어 들이고 캐시 하게 됩니다. OS X에서는 이미지와 사운드 리소스는 캐시로 저장되고 나중에 필요하면 접근할 수 있습니다. iOS에서는 오직 이미지 리소스만 캐시로 저장됩니다. 이미지에 접근하기 위해서는 플랫폼에 따라 NSImage나 UIImage의 imageNamed: 함수를 사용합니다. OS X에서 사운드에 접근하기 위해서는 NSSound의 soundNamed: 함수를 사용합니다.


Nib File Design Guidelines

nib 파일을 만들 때, 파일에서 해당 객체를 사용하는 방법을 신중하게 고려하는 것이 중요합니다. 매우 간단한 앱은 사용자 인터페이스  컴포넌트 모두를 하나의 nib 파일에 저장할 수도 있습니다. 하지만 대부분의 앱은 컴포넌트들을 여러 개의 nib 파일에 분배하는 것이 더 좋습니다. 작은 nib 파일을 생성하면 인터페이스에 필요한 부분만 즉시 로드할 수 있습니다. 또한 문제를 찾기 위한 곳이 얼마 안 되기 때문에 문제가 발생했을 때 더 쉽게 디버깅할 수 있습니다. 

  nib 파일을 생성할 때 다음의 가이드라인을 염두에 두세요:

  • lazy 로딩을 사용하는 것을 염두에 두고 nib 파일을 설계하세요. 당장 필요로 하는 객체들만을 포함된 nib 파일을 로딩하도록 하세요.
  • OS X 앱을 위한 메인 nib 파일에서는 앱 메뉴 바와 optional 앱 delegate 객체만을 저장하는 것이 좋습니다. 앱이 실행된 후에야 사용할 수 있는 어떤 윈도우나 사용자 인터페이스 객체를 포함하는 것을 피하도록 하세요. 대신에 이러한 리소스를 별도의 nib 파일에 저장하고 필요에 따라 로드하세요.
  • 반복되는 사용자 인터페이스 컴포넌트(예를 들어 도큐먼트 윈도우)들은 별도의 nib 파일에 저장하세요.
  • 가끔씩 사용되는 윈도우나 메뉴는 별도의 nib 파일에 저장하세요. nib 파일을 분리하여 저장하는 것은 실제로 사용되어야 할 때만 리소스를 메모리에 로드하게 해 줍니다.
  • File's Owner를 nib 파일 외부의 모든 것에 대한 단일 연락 지점으로 만드세요. 자세한 내용은 Accessing the Contents of a Nib File을 참조하세요.

The Nib Object Life Cycle

nib 파일을 메모리에 로드할 때 nib-loading 코드는 nib 파일의 객채들의 생성과 적절한 초기화를 보장하기 위한 몇 가지 단계를 거치게 됩니다. 이러한 단계를 이해하는 것은 사용자 인터페이스를 관리하기 위한 다 하는 컨트롤러 코드를 작성하는데 도움이 됩니다.

 

The Object Loading Process

nib 파일의 객체를 로드하고 초기화하기 위해 NSNib 또는 NSBundle의 함수들을 사용할 때, 기본적인 nib-loading 코드는 다음과 같은 작업을 수행합니다.

  1. 메모리에 Nib 파일의 콘텐츠와 참조된 모든 리소스 파일을 로드합니다.
    • 전체 nib 객체 그래프에 대한 raw 데이터는 메모리에 로드됩니다. unarchived 되는 것은 아닙니다.
    • nib파일과 함께 연관된 모든 커스텀 이미지 리소스들도 로드되고 Cocoa 이미지 캐시에 추가됩니다. About Image and Sound Resources를 참조하세요.
    • nib 파일과 함께 연관된 모든 커스텀 사운드 리소스들이 로드되고 Cocoa 사운드 캐시에 추가됩니다. About Image and Sound Resources를 참조하세요.
  2. nib 객체 그래프 데이터를 unarchive 하고 객체들을 초기화합니다. 새 객체를 어떻게 초기화하는지는 객체의 타입과 어떻게 archive에 인코딩 되어있느냐에 따라 달라집니다. nib-loading 코드는 다음 규칙(순서에 따라)을 사용하여 사용할 초기화 메서드를 결정합니다.
    • 기본적으로 객체는 initWithCoder: 메서드를 수신합니다.

      OS X에서는 표준 객체 목록에는 시스템에서 제공하고 기본 Xcode 라이브러리에서 사용할 수 있는 views, cells, menus, and view controllers가 포함됩니다. 또한 커스텀 plug-in을 사용한 라이브러리에 추가된 third-party 객체도 포함합니다. 심지어 이 객체의 클래스를 변경하여도 Xcode는 표준 객체를 nib 파일로 인코딩하고 archiver에게 객체가 unarchive 될 때 커스텀 클래스로 교체하라고 알려줍니다.
       
      iOS에서는 NSCoding 프로토콜을 따르는 모든 객체는 initWithCoder: 메소드를 통해 초기화됩니다. 이것은 Xcode 라이브러리 또는 사용자가 정의한 클래스의 일부인 UIView와 UIViewController의 모든 하위 클래스를 포함합니다.
    • OS X에서는 사용자 정의 뷰는 initWithFrame: 메시지를 수신합니다.

      사용자 정의 뷰는 Xcode에 사용가능한 구현이 없는 NSView의 하위 클래스입니다. 일반적으로 이 뷰들은 사용자의 앱에서 정의하고 사용자 정의 시각 콘텐츠를 제공하는 데 사용됩니다. 사용자 정의 뷰는 기본 라이브러리 또는 통합된 third-party 플러그인의 표준 시스템 뷰(NSSlider와 같은)를 포함되지 않습니다.

      사용자 정의 뷰를 만나게 되면 Xcode는 nib 파일을 특별한 NSCustomView 객체로 인코딩합니다. 사용자 정의 뷰 객체는 빌드하기 위해 지정된 하위 뷰 클래스 정보를 포함하고 있습니다. 로딩 시점에 NSCustomView 객체는 alloc와 initWithFrame: 메시지를 실제 뷰 클래스에 보내고 생성된 뷰 객체를 사용자 정의 뷰로  교체합니다. 실제 효과는 실제 뷰 객체가 nib-loading 프로세스 중에 후속 작업을 수행한다는 것입니다.

      iOS에서 사용자 정의 뷰는 초기화를 위해서 initWithFrame: 을 사용하지 않습니다.
    • 앞의 단계에서 설명된 것 이외의 사용자 정의 객체는 init 메시지를 수신합니다.
  3. nib 파일의 객체들간에 모든 연결(Action, outlet, Binding)이 재생성됩니다. 이것은 File's Owner와 다른 placeholder 객체들 간의 연결을 포함합니다. 재생성 연결을 위한 접근 방법은 플랫폼에 따라 다릅니다.
    • Outlet 연결
      • OS X에서는 nib-loading 코드는 객체가 소유한 메소드를 우선 사용하여 outlet을 재연결하려고 시도합니다. 각 outlet 마다, Cocoa는 setOutletName:의 형태로 된 메소드를 찾고 해당 메서드가 있으면 호출합니다. 만약 이 메서드를 찾을 수 없는 경우에 Cocoa는 객체에서 outlet 이름과 일치하는 인스턴스 변수를 찾습니다. 그리고 직접 값을 세팅하려고 시도합니다. 만약 인스턴스 변수를 찾지 못하는 경우 연결이 생성되지 않습니다.

        또한 outlet을 설정하면 등록된 모든 observer에 대한 key-value observing(KVO) Notification이 생성됩니다. 이 notification은 모든 객체 간 연결이 재생성되기 전에 발생할 수 있으며 명확하게 객체의 awakeFromNib 메서드가 불려지기 전에 발생합니다.

      • iOS에서는 nib-loading 코드는 setValue:forKey: 메소드를 사용하여 각 outlet을 재연결하려고 합니다. 이 메서드는 마찬가지로 적절한 접근 메서드를 찾고 실패하면 다른 방법으로 대체됩니다. 이 메서드가 값을 설정하는 방법에 대한 더 자세한 내용은 NSKeyValueCoding Protocol Reference을 참조하세요.

        iOS에서 outlet을 설정하면 등록된 observer를 위한 KVO Notification이 생성됩니다. 이 Notification은 모든 객체간 연결이 재생성되기 전에 발생할 수 있으며 명확하게 객체의 awakeFromNib 메서드가 불려지기 전에 발생합니다.
    • Action 연결
      • OS X에서는 nib-loading 코드는 대상 객체를 연결을 생성하기 위해 객체의 setTarget: 그리고 setAction: 메소드를 사용합니다. 만약 대상 객체의 action 메서드가 응답하지 않으면 연결은 생성되지 않습니다. 대상 객체가 nil인 경우에는 Action은 Responder chain에 의해 처리됩니다.
      • iOS에서, nib-loading 코드는 UIControl 객체의 addTarget:action:forControlEvents: 메소드를 사용하여 Action을 구성합니다. 만약 대상 객체가 nil인 경우 액션은 Responder chain에 의해 처리됩니다.
    • Binding
      • OS X에서는 Cocoa는 bind:toObject:withKeyPath:options: 메소드를 사용하여 해당 객체와 대상 객체 간의 연결을 생성합니다.
      • iOS에서는 Binding을 지원하지 않습니다.
  4. 일치하는 selector를 정의하는 nib 파일의 해당 객체로 awakeFromNib 메시지를 보냅니다.
    • OS X에서는 이 메시지는 이 메소드를 정의하는 모든 인터페이스 객체로 보내집니다. 또한 이를 메서드를 정의한 File's Owner와 모든 placeholder 객체로도 보내집니다.
    • iOS에서는 이 메시지는 nib-loading 코드에의해 인스턴스화 된 인터페이스 객체에게만 보내집니다. File's Owner와 First Responder 또는 다른 placeholder 객체에게는 메시지를 보내지 않습니다.
  5. nib 파일에 "Visible at launch time" 속성을 사용하도록 설정한 모든 윈도우를 표시합니다.

nib-loading 코드에서 객체의 awakeFromNib 메소드를 호출하는 순서는 보장되지 않습니다. OS X에서는 Cocoa는 File's Owner의 awakeFromNib 메소드를 마지막으로 호출하려고 시도하지만 이 동작은 보장되지 않습니다. 만약 로드 시점에 다른 nib 파일을 더 로드할 필요가 있다면 보다 적절한 시점은 nib-loading 호출이 반환된 후입니다. 이때 모든 객체들은 생성, 초기화 및 사용할 준비가 됩니다.


Managing the Lifetimes of Objects from Nib Files

NSBundle이나 NSNib 클래스에 nib 파일을 로드하도록 요청할 때마다 기본 코드는 해당 파일에 있는 객체의 새 복사본을 만들어 사용자에게 반환합니다(nib-loading 코드는 이전에 로드하려고 한 것으로부터 nib 파일 객체를 재활용하지 않습니다). 필요한 만큼 새로운 객체 그래프를 유지하고 사용이 끝났을 때 해제해야 합니다. 일반적으로 그 객체들이 dealloc 되지 않도록 top-level 객체들을 강한 참조(strong reference)하는 것이 필요합니다(부모가 하위 객체를 소유하고 있기 때문에 하위 객체들은 강한 참조를 하지 않아도 됩니다. 그리고 순환 참조 문제(strong reference cycles)가 생기는 위험을 최소화해야 합니다).

 실용적인 관점에서는 iOS와 OS X의 outlet은 선언적 프로퍼티로서 정의 되어야 합니다. Nib 파일에서(또는 iOS의 스토리보드)의 FIle's Owner와 top-level 객체들을 제외하고는 일반적으로 weak 타입이 되어야 합니다. 그러므로 생성한 Outlet은 일반적으로 다음과 같은 이유로 weak 타입이어야 합니다.

  • 예를 들어 ViewController의 View 또는 Windsow 컨트롤러의 window의 하위 View를 생성하기 위한 Outlet들은 소유권을 의미하지 않는 객체 간의 임의적인 참조입니다.
  • Strong outlet은 종종 프레임워크 클래스들로 지정됩니다(예를 들어 UIViewController의 view outlet 또는 NSWindowController의 window outlet).

참고: OS X에서는 모든 클래스들이 약한 참조(weak reference)를 지원하는 것은 아닙니다.(Transitioning to ARC Release Notes를 참조). 따라서 weak를 지정할 수 없는 경우 assign을 사용해야 합니다.

Outlet은 일반적으로 정의한 클래스에 private으로 사용되는 것으로 여겨집니다. (프로퍼티를 public으로 노출해야하는 이유가 없는 경우 프로퍼티 선언을 클래스 확장으로 숨깁니다.) 예를 들어,

 

이러한 패턴은 컨테이너 뷰로부터 이것의 하위뷰까지 참조를 확장한다. 여기서 객체 그래프의 일관성을 고려해야 한다. 예를 들어 테이블 뷰 cell의 경우 특정 하위 뷰를 지정하는 outlet은 일반적으로 weak 타입이어야 한다. 만일 테이블 뷰가 이미지 뷰와 텍스트 뷰를 포함하고 있다면 이 뷰들은 테이블 뷰 셀 자체의 하위 뷰인 한 유효 합니다. 

Outlet은 참조된 객체를 소유하는 것으로 간주될 때 strong 타입으로 변경되어야 합니다.

  • 앞에서 언급한것 처럼, 이것은 Nib파일의 최상위 객체는 File's Owner가 소유하는 것으로 간주됩니다.
  • 몇몇 상황에서 Nib 파일의 객체가 원본 컨테이너의 외부에 존재할 필요가 있습니다. 예를 들어 초기 뷰 계층에서 일시적으로 삭제될 수 있는 뷰를 위해서는 outlet은 독립적으로 유지되어야 합니다.

추상 클래스와 같이 subclass 되어야 하는 클래스들은 outlet을 공개적으로 노출해야 합니다. 그래야 subclass에 의해 적절하게 사용될 수 있습니다(예를 들어 UIViewController의 view oulet). 또한 Outlet은 클래스의 소비자가 그 프로퍼티와 상호작용 하기를 원하는 경우에 노출되어야 합니다. 예를 들어 테이블 뷰 셀은 subview를 노출합니다. 이러한 경우에 적절하게 public outlet은 read-only로 해야 하고 private으로 read-write로 재정의해야 합니다. 아래의 예제를 참조하세요.

 

Top-level Objects in OS X May Need Special Handling

역사적인 이유로 OS X에서는 nib파일의 top-level 객체는 추가적인 reference count를 갖고 생성됩니다. AppKit은 nib 객체가 적절하게 해제 될 수 있도록 돕기 위해 몇 가지 기능을 제공합니다.

  • NSWindow 객체(panel을 포함하여)는 isReleasedWhenClosed 속성을 갖고 있습니다. 이것을 만약 YES로 설정하면 window는 close 될 때 자체적으로 해제(결과적으로 뷰 계층 안의 모든 종속적인 객체)됩니다. 이 옵션은 Xcode inspector의 Attributes pane에서 "Release when closed" 체크 박스를 통해 설정하면 됩니다.
  • nib파일의 File's Owner가 NSWindowController 객체(Document 기반 앱의 Document nib의 기본값인 NSDocument는 NSWindowContoller 인스턴스를 관리한다는 것을 기억하세요.) 또는 NSViewController 객체인 경우, 자동으로 관리되는 window를 폐기한다.

File's Owner가 NSWindowController 또는 NSViewController가 아닌 경우에는 자체적으로 top-level 객체의 reference count를 감소시켜야 합니다. top-level 객체를 Core Founcdation 타입으로 Cast 해야 하며 CFRelease를 사용하세요.(모든 top-level 객체의 outlet을 갖길 원하지 않는다면 nib 파일의 top-level 객체들에 대한 배열을 얻기 위해 NSNib 클래스의 instantiateNibWithOwner:topLevelObjects: 메서드를 사용할 수 있습니다.)

 


Action Methods

대체로 Action method는 일반적으로 nib file의 다른 객체를 실행 시켜주는 메서드입니다. Action method는 타입 한정자 void 리턴 타입 대신 IBAction을 사용하여 Xcode가 인식할 수 있도록 표시합니다.

 

Action method를 클래스의 private으로 간주하기 위해 public @interface에 선언하지 않을 수 있습니다.(Xcode는 구현 파일을 구문 분석하기 때문에 헤더에 선언할 필요가 없습니다.)

 

일반적으로 프로그래밍 방식으로 Action method를 호출해서는 안됩니다. 만약 Action method와 연관된 작업을 수행해야 할 필요가 있을 때, 그 작업을 Action method에서 호출되게 하는 방법으로 구현해야 합니다.

 


Built-In Support For Nib Files

AppKit과 UIKit 프레임워크는 앱에서 nib file을 로딩하고 관리하기 위해 일정량의 자동화된 동작을 제공합니다. 두 프레임워크 모두 앱의 main nib 파일을 로딩하기 위한 기반시설을 제공합니다. 게다기 AppKit 프레임워크는 NSDocument나 NSWindowController 클래스를 통해 다른 nib파일을 로드할 수 있도록 지원합니다. 아래 섹션은 nib파일에 대한 기본 지원, 이를 활용하는 방법과 앱에서 해당 지원을 수정하기 위한 방법에 대해 설명합니다.

 

The Application Loads the Main Nib File

앱을 위한 Xcode 프로젝트 템플릿의 대부분은 main nib파일에 미리 구성되어 있습니다. nib 파일에서 이 기본 nib파일을 수정하고 앱을 빌드하기만 하면 됩니다. 실행 시점에 앱의 기본 구성 데이터는 앱 객체에게 이 파일을 로드할 수 있게 nib 파일을 어디에서 찾을 수 있는지 알려줍니다. AppKit 및 UIKit을 기반으로 하는 앱에서 이 구성 데이터는 앱의 Info.plist 파일에 있습니다. 앱이 처음 로드될 때 기본 앱의 실행 코드는 NSMainNibFile key를 Info.plist 파일에서 찾습니다. 찾게 되면 앱 번들에서 그 key 값과 일치하는 이름(filename 확장자가 있든 없든)을 가진 nib 파일을 찾아 로드합니다.

 

Each View Controller Managers its Own Nib File

UIViewController(iOS) 및 NSViewController(OS X) 클래스는 그들과 연관되는 nib 파일을 자동으로 로딩하는 기능을 지원합니다. View Controller를 생성할 때 특정 nib 파일을 지정했다면 그 ViewController의 View에 접근하려고 할 때 nib 파일을 자동으로 로드합니다. ViewController와 nib 파일 객체 사이의 모든 연결도 자동으로 생성됩니다. 그리고 iOS에서는 UIViewController 객체는 뷰가 최종적으로 로드되었을 때와 스크린에 디스플레이되었을 대 추가적인 Notification을 수신합니다. 그리고 메모리를 더 잘 관리하기 위해 UIViewControler 클래스는 low-memory 상태에서 nib 파일의 unloading을 처리합니다.

UIViewController 클래스를 사용하고 구성하기 위한 더 자세한 내용은 View Controller Programming Guide for iOS를 참조하세요.

 

Document and Window Controllers Load Their Associated Nib File

AppKit 프레임워크에서 NSDocument 클래스는 document window를 포함하는 nib 파일을 로드하기 위한 기본 window controller와 함께 동작합니다. NSDocument의 windowNibName 메서드는 일치하는 document window를 포함하는 nib 파일을 지정하는 데 사용할 수 있는 편의 함수입니다. 새로운 document를 생성될 때 document 객체는 지정한 nib file name을 기본 window object에게 전달하고 이 객체는 nib 파일의 내용을 로드하고 관리합니다. Xcode에 의해 제공된 표준 템플릿을 사용한다면 document window의 콘텐츠를 nib 파일에 추가하기만 하면 됩니다.

그리고 NSWindowController 클래스는 nib 파일 로딩을 자동으로 지원합니다. 프로그래밍 방식으로 커스텀 window controller를 생성한다면 NSWindow 객체 또는 Nib 파일의 이름으로 초기화할 수 있습니다. 만약 후자의 옵션을 선택한다면 클라이언트가 처음 window에 접근하려고 할 때 NSWindowController 클래스는 자동으로 지정된 nib 파일을 로드합니다. 그 후에 window controller는 그 window를 메모리에 유지하고 심지어 window에 "Release when cloade" 속성이 설정된 경우에도 nib 파일을 재로드 하지 않습니다. 

중요: NSWindowController 또는 NSDocument를 사용하여 window를 자동으로 로드하는 경우 nib 파일을 정확하게 구성하는 것이 중요합니다. 두 클래스 모두 관리할 window에 연결해야 하는 window outlet을 포함하고 있습니다. 이 outlet을 window 객체에 연결하지 않으면 nib 파일이 로드되지만 document 또는 window controller에 window가 표시되지 않습니다. Cocoa document architecture에 대한 자세한 내용은 Document-Based App Programming Guide for Mac을 참조하세요.


Loading Nib Files Programmatically

OS X와 iOS 모두 앱에서 nib 파일을 로드하기 위한 편의 합수를 제공합니다. AppKit 및 UIKit 프레임워크는 nib 파일을 로딩을 지원하는 NSBundle 클래스에 추가적인 메서드를 정의하고 있습니다. 덧붙여 AppKit 프레임워크는 NSNib 클래스를 제공합니다. 이 클래스는 NSBundle 클래스와 유사한 nib 로딩 기능을 제공하지만 특정 상황에서 유용한 몇 가지 추가적인 이점을 제공합니다.

앱을 작성할 때 수동으로 로드해야 할 모든 nib 파일은 로딩 절차를 간소화하는 방식으로 구성되어있는지 확인해야 합니다. File's Owner에게 적합한 개체를 선택하고 nib 파일을 작게 유지하면 사용 편의성과 메모리 효율성이 매우 향상될 수 있습니다. nib 파일의 구성을 위한 더 많은 팁은 Nib File Design Guidelines을 참조하세요.

Loading Nib Files Using NSBundle

AppKit 및 UIKit 프레임워크는 nib 파일 리소스를 로딩하는 NSBundle 클래스(Objective-C category를 사용)에 추가적인 메서드를 정의하고 있습니다. 메서드를 어떻게 사용해야 하는 것에 대한 의미는 메서드의 문법과 마찬가지로 두 플랫폼 간에 차이가 있습니다. AppKit에서는 일반적으로 bundle에 접근하기 위한 더 많은 옵션이 있으며 그 bundle로부터 nib 파일을 로딩하기 위한 알맞은 더 많은 메서드들이 있습니다. UIKit에서는 앱은 main bundle에서만 nib 파일을 로드할 수 있어 필요한 옵션이 더 적습니다.

Nib 파일을 로딩할 때마다 그 nib 파일의 File's Owner로서 동작해야 하는 객체를 항상 지정하는 것이 좋습니다. File's Owner의 역할은 매우 중요합니다. 이것은 동작하는 코드와 메모리에 생성하려는 새로운 객체 사이에 주요한 인터페이스입니다. nib-loading 메서드들 모두 options dictionary에서 직접 또는 파라미터로 File's Owner를 지정하는 방법을 제공합니다. 

AppKit과 UIKit 프레임워크가 nib 로드를 처리하는 방법 사이의 의미적 차이중 하나는 top-level nib 객체가 앱에 반환되는 방식입니다. AppKit 프레임워크에서는 loadNibFile:externalNameTable:withZone: 메서드 중 하나를 사용해서 명시적인 요청을 해야 합니다. UIKit에서의 loadNibNamed:owner:options: 메서드는 이 객체들의 배열을 직접적으로 반환합니다. 두 케이스 모두 top-level 객체들에 대해서 걱정을 피하기 위한 간단한 방법은 File's Owner 객체의 outlet에 그들을 저장하는 것입니다.(Managing the Lifetimes of Objects from Nib Files을 참조하세요.)

 

Listing 1-1는 AppKit 기반의 앱에서 NSBundle 클래스를 사용하여 nib 파일을 어떻게 로딩하는지에 대한 간단한 예를 보입니다. loadNibNamed:owner:메서드가 리턴하기만 하면 nib 파일 객체가 참조하는 모든 outlet을 사용할 수 있습니다. 다른 말로는 전체 nib-loading 프로세는 단일 호출 범위 내에서 발생합니다. AppKit 프레임워크에서 nib-loading 메서드는 로드 작업이 성공인지를 나타내는 Boolean 값을 리턴합니다.

 

Listing 1-1 Loading a nib file from the current bundle

Listing 1-2 UIKit 기반의 앱에서 nib file을 어떻게 로드하는지 보여줍니다. 이 경우 이 방법은 반환된 배열을 확인하여 nib 객체가 성공적으로 로드되었는지를 확인합니다.(모든 nib 파일은 nib 파일의 콘텐츠를 나타낼 수 있는 적어도 하나의 top-level 객체는 가지고 있어야 합니다.) 이 예제는 nib file에 File's Onwer 이외에 placeholdeer 객체가 없는 간단한 케이스를 보여주고 있습니다. 추가적인 placeholder 객체를 어떻게 지정하는지에 대해서는 Replacing Proxy Objects at Load Time를 참조하세요.

 

Listing 1-2 Loading a nib in an iPhone application

참고: 만약 유니버설 iOS 앱을 개발하고 있다면 기기에 따라 자동적으로 알맞은 nib 파일을 로드할 수 있는 device-specific naming convention을 사용할 수 있습니다. nib file의 이름을 어떻게 지정하는지에 대한 더 자세한 내용은 iOS Supports Device-Specific Resources를 참조하세요.

 

Getting a Nib File's Top-Level Objects

nib file의 top-level 객체를 얻기 위한 가장 쉬운 방법은 그 객체에 접근하기 위한 setter 메서드(더 좋은 것은 프로퍼티)와 함께 File's Owner에 outlet을 정의하면 됩니다. 이 방식은 top-level 객체가 유지되고 항상 이 객체에 대한 참조가 존재하는 것을 보장합니다.

Listing 1-3은 nib 파일의 유일한 top-level 객체를 유지하기 위해 outlet을 사용하는 단순화된 Cocoa 클래스의 인터페이스와 구현을 보여줍니다. Cocoa에서 top-level 객체는 초기 retain count가 1이기 때문에 추가 적인 release 메시지가 포함됩니다. 이 것은 release가 호출될 때 프로퍼티가 이미 window를 보유하고 있기 때문에 문제없습니다. iPhone 앱에서는 이 방법으로 top-level object를 release 하는 것은 원하지 않습니다.

 

만약 nib file의 참조를 저장하기 위해 outlet을 사용하길 원하지 않는다면 반드시 코드에서 그 객체들을 수동으로 가져와야 합니다. top-level 객체를 획득하기 위한 방법 플랫폼에 따라 다릅니다. OS X에서는 명시적으로 객체를 요청해야 하지만 iOS에서는 자동적으로 반환됩니다.

Listing 1-4는 OS X에서 top-level 객체를 얻기 위한 방법을 보여줍니다. 여기서는 nameTable이라는 dictionary에 가변 배열을 NSNibTopLevelObjects라는 키와 연관시켜 추가합니다. nib-loading 코드는 이 배열 객체를 찾고 만약 존재하면 이 배열에 top-level 객체를 추가합니다. 이 객체들은 배열에 추가되기 전에 retain count가 1로 시작하기 때문에 단순하게 이 배열만 release 하는 것으로는 이 객체들을 해제할 수 없습니다. 따라서 이 방법에서는 각 객체들에 release 메시지를 보내서 이 배열이 그 객체들을 참조하는 유일한 엔티티라는 것을 보장하게 합니다.

 

Listing 1-4 Getting the top-level objects from a nib file at runtime

iPhone에서 top-level 객체를 획득하는 것은 더 간단하며 Listing 1-2에서 보이고 있습니다. UIKit 프레임워크에서 NSBuldle의 loadNibNamed:owner:options:메서드는 자동적으로 top-level 객체의 배열을 반환합니다. 게다가 배열이 반환될 때 그 객체들에게 추가적인 release 메시지를 보낼 필요가 없도록 retain count가 조정됩니다. 반환된 배열은 그 객체들의 유일한 소유자입니다.

 

Loading Nib Files Using UINib and NSNib

UINib(iOS)와 NSNib(OS X) 클래스는 nib file의 콘테츠의 여러 복사본을 생성해야 할 때 더 나은 성능을 제공합니다. 보통의 nib-loading 절차는 nib file을 디스크에서 읽고 포함하는 객체들을 인스턴스 화하는 과정을 포함합니다. 그러나 UINib와 NSNib 클래스를 사용하면 nib file을 디스크로부터 한 번만 읽어 들이고 콘텐츠들은 메모리에 저장합니다. 메모리에 있기 때문에 연속된 객체 집합을 생성하면 디스크 접근이 불필요하기 때문에 시간이 덜 걸립니다.

UINib나 NSNib 클래스의 사용은 두 단계로 이루어져 있습니다. 첫 번째, 이 클래스의 인스턴스를 생성하고 nib file의 위치 정보를 가지고 초기화합니다. 두 번째로 nib file의 내용을 인스턴스 화하여 객체를 메모리에 로드할 수 있습니다. nib file을 인스턴스화 할 때마다 다른 File's Owner 객체를 지정하고 top-level 객체의 새로운 집합을 얻습니다.

Listing 1-5는 OS X에서 NSNib 클래스를 사용하여 nib file의 콘텐츠를 로드하는 한 가지 방법을 보여줍니다. instantiateNibWithOwner:topLevelObjects: 메서드를 사용하여 얻은 배열은 이미 autoreleased 되어있습니다. 특정 시간 동안 이 배열을 사용하려고 한다면 이것을 복사해야 합니다.

 

Listing 1-5 Loading a nib file using NSNib

Replacing Proxy Objects at Load Time

iOS에서는 File's Owner 외에 placeholder 객체를 포함하여 nib 파일을 생성하는 것이 가능합니다. Proxy 객체는 nib file 밖에서 생성되었지만 nib 파일의 콘텐츠와 어떤 연결을 가지고 있는 객체를 나타냅니다. Proxy들은 일반적으로 iPhone 앱의 내비게이션 컨트롤러를 지원합니다. 네비게이션 컨트롤러를 사용할 때 일반적으로 File's Owner 객체를 앱의 delegate와 같은 몇몇 공통 객체와 연결합니다. 따라서 Proxy 객체는 프로그래밍 방법으로 생성되거나 다른 nib 파일로부터 로드되었기 때문에 이미 메모리에 로드된 내비게이션 컨트롤러 객체 계층의 일부분을 나타냅니다. 

 

참고: 커스텀 Placeholder 객체(File's Owner 이외의)는 OS X nib 파일에서 지원되지 않습니다.

 

Nib 파일에 추가한 Placeholder 객체 각각은 unique 한 이름을 가져야 합니다. 객체에 이름을 할당하기 위해 Xcode에서 객체를 선택하고 inspector 창을 열어야 합니다. Inspector의 Attributes pane은 placeholder 객체의 이름을 지정할 수 있는 Name 필드를 포함하고 있습니다. 할당한 이름은 원하는 대로 지정할 수 있으나 객체의 행동이나 타입을 설명하는 것이 좋습니다. 

Placeholder 객체를 포함하는 Nib 파일을 로드할 준비가 되면 loadNibNamed:owner:options: 메서드를 호출할 때 proxy를 위한 대체 객체를 반드시 지정해야 합니다. 이 메서드의 options 파라미터는 추가적인 정보의 Dictionary를 받아들입니다. 이 Dictionary를 사용하여 placeholder 객체에 대한 정보를 전달할 수 있습니다. 이 Dictionary는 Placeholder 대체를 위한 객체와 이름을 포함하는 다른 Dictionary를 UINibExternalOjbects 키를 사용하여 반드시 포함하여야 합니다.

Listing 1-6은 수동으로 앱의 main nib 파일을 로드하는 applicationDidFinishLaunching: 함수의 샘플 버전을 보여주고 있습니다. 앱의 Delegate 객체는 UIApplcationMain 함수를 통해 생성되기 때문에 이 메서드는 nib 파일에서 이 객체를 나타내기 위한 placeholder("AppDelegate"라는 이름의)를 사용합니다. proxies Dictionary는 paceholder 객체 정보를 저장하고 options Dictionary는 이 proxies Dictionary를 포함하고 있습니다.

 

Listing 1-6 Replacing placeholder objects in a nib file

이 loadNibNamed:owner:options:메서드의 option Dictionary에 대한 더 자세한 내용은 NSBundle UIKit Addtions Reference를 참조하세요.

Accessting the Contents of a Nib file

Nib 파일을 성공적으로 로드하면 해당 내용을 즉시 사용할 수 있습니다. NIb 파일 객체를 지정하는 File's Owner에 outlet을 구성한 경우 이 outlet을 바로 사용할 수 있습니다. 만약 어떤 객체도 File's Owner를 구성하지 않았다면 몇 가지 방법을 사용하여 top-level 객체의 참조를 획득해야 하고 나중에 그것을 release 할 수 있습니다.

Nib 파일이 로드되면 outlet은 실제 객체로 존재하기 때문에 그 후에 프로그래밍 방식으로 생성된 다른 모든 객체와 마찬가지로 outlet을 사용할 수 있습니다. 예를 들어 window를 지정하는 outlet을 갖고 있는 경우 사용자의 스크린에 보이기 위해 이 window에 makeKeyAndOrderFront:메시지를 보낼 수 있습니다. Nib 파일의 객체들의 사용이 완료되면 다른 모든 객체들처럼 반드시 release 해야 합니다.

중요: 모든 Nib 파일로부터 Top-level 객체를 로드하고 사용이 종료되면 release 해야 하는 책임이 있습니다. 많은 앱에서 Release를 수행하지 않아 메모리 누수가 발생을 야기합니다. Top-Level 객체를 release 한 후에 Nib 파일의 객체를 가리키는 모든 outlet에 nil을 세팅하여 지우는 것이 좋습니다. Top-Level 객체뿐만 아니라 Nib 파일의 객체와 연관된 outlet 들을 release 해야 합니다.

 

 


Connecting Menu Items Across Nib Files

OS X 앱 메뉴 바의 아이템은 종종 앱의 Documents와 window를 포함하여 많은 다른 객체들과 상호작용해야 합니다. 문제는 이러한 객체들 대다수가 main nib 파일로부터 직접적으로 접근할 수 있다는 것입니다. Main nib 파일의 File's Owner는 항상 NSApplication 클래스의 인스턴스로 지정되어 있습니다. 그리고 main nib 파일의 많은 커스텀 객체를 인스턴스화 할 수 있겠지만 그렇게 하는 것은 실용적이지도 필요하지도 않습니다. Document 객체의 경우, 특정 document 객체에 직접적으로 연결하는 것은 Document 객체의 개수가 동적으로 바뀔 수 있으며 심지어 0이 될 수도 있기 때문에 가능하지 않습니다. 

대부분의 메뉴 아이템은 다음 중 하나로 액션 메시지를 보냅니다.

  • 고정된 객체(항상 명령을 수행하는)
  • 동적 객체(document 또는 window와 같은)

고정된 객체에 메시지를 보내는 것은 일반적으로 application delegate를 통해 가장 잘 처리되는 비교적 간단한 프로세스입니다. 이 application delegate 객체는 NSApplication가 앱을 실행하는데 도움이 되며 main nib 파일에 올바르게 속하는 몇 안 되는 개체 중 하나입니다. 만약 메뉴 아이템이 application-level 명령을 참조하고 있다면 이 명령을 application delegate에서 직접적으로 구현하거나 다른 위치에 있는 적절한 객체로 메시지를 전달하도록 할 수 있습니다.

가장 앞에 있는 window의 콘텐츠에서 동작하는 메뉴 아이템이 있다면 메뉴 아이템을 First Responder placeholder 객체로 연결시켜야 합니다. 메뉴 아이템과 연관된 action 메서드가 다른 객체들(Cocoa에 의해 정의되지 않은) 중 하나를 지정하고 있다면 반드시 연결을 생성하기 전에 First Responder에 이 action을 추가해야 합니다.

연결을 생성한 후에는 커스텀 클래스에서 action 메서드를 구현해야 합니다. 또한 이 객체는 적절할 시점에 메뉴 아이템이 활성화되도록 validateMenuItem:메서드를 구현해야 합니다. Responder Chain이 어떻게 명령을 처리하는지에 대한 자세한 내용은 Cocoa Event Handling Guide를 참조하세요.

 

 


 참고 문서

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW8