Xcode 를 실행하여 Window-based Application 템플릿으로 새 프로젝트를 생성한다.
프로젝트 명은 HelloiPhone 으로, Core Data 사용여부 체크는 해제한다.
HelloiPhoneAppDelegate.h : 헤더, 클래스 정의
HelloiPhoneAppDelegate.m : 모듈, 클래스 구현
MainWindow.xib : 인터페이스 정의
이렇게 세 파일이 만들어졌다.
HelloiPhoneAppDelegate.h 파일에서 아래와 같이 코딩.
#import <UIKit/UIKit.h>
@interface HelloiPhoneAppDelegate : NSObject <UIApplicationDelegate> {
UILabel *myText;
BOOL txtState;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UILabel *myText;
@property BOOL txtState;
- (IBAction)changeText:(id)sender;
@end
HelloiPhoneAppDelegate.m 파일에서 아래와 같이 코딩.
#import "HelloiPhoneAppDelegate.h"
@implementation HelloiPhoneAppDelegate
@synthesize window=_window;
@synthesize myText, txtState;
- (IBAction)changeText:(id)sender{
if(txtState){
myText.text = @"Hello, World";
txtState = NO;
} else {
myText.text = @"Hello, iPhone";
txtState = YES;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
… 이후 생략
MainWindow.xib 에서 그림과 같이 객체 생성
Hello, World 는 UILabel 을, Change Text 는 UIButton 객체이다.
Connections Inspector 에서 HelloiPhone App Delegate 를 선택, Outlet 과 Action 을 연결한다.
myText 와 Label 을, changeText 와 Button 의 Touch Up Inside 를 연결.
모두 저장하고 Build And Run.
change Text 버튼을 누를 때 마다 Label 의 글이 변하는 것을 확인할 수 있다.
아이폰 프로그래밍 (객체 지향 프로그래밍) 을 위해서는 MVC 패턴에 대해 이해할 필요가 있다.
MVC 패턴?
MVC (Model-View-Controller) 패턴을 사용하면 사용자 인터페이스로부터 비즈니스 로직을 분리하여 애플리케이션의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향없이 쉽게 고칠 수 있는 애플리케이션을 만들 수 있다 (@위키백과;모델-뷰-컨트롤러).
MVC 패턴의 예
예를 들자면 이런 상황에서
- 더러워진 옷이 있다.
(세탁을 하기로 한) 더러워진 옷 = 빨랫감
세탁기에게 빨래를 시키기 위해서 세탁기의 기능 버튼을 이용한다 (물 높낮이, 강약, 탈수 등).
세탁기는 주어진 빨래와 주어진 기능을 토대로 빨래를 하고 끝나면 세탁된 옷을 돌려준다.
더러워진 옷은 그 종류가 다양하며 그 자체로는 (더러워졌으므로) 사용할 수 없다. 고로, 더러워진 옷은 세탁이라는 행위를 통해서 사용가능한 의류가 된다. 프로그래밍의 관점에서 더러워진 옷은 일종의 데이터 ( = Model) 라고 볼 수 있다.
기능 버튼은 사람과 세탁기의 중간 역할을 한다. 세탁기 스스로는 어떤 행위도 할 수 없고 사람 또한 임의로 전기신호나 다른 물리력을 통해 세탁기를 움직이게 할 수 없다. 기능 버튼은 사용자 인터페이스 ( = View) 라고 할 수 있다.
세탁기는 어떤 종류의 더러워진 옷이 주어져도 빨래를 해야한다. 고로, 옷에 독립적이어야 한다. 세탁기는 일을 수행하는 입장에서 비즈니스 로직 ( = Controller = Class) 이라고 볼 수 있다.
MVC 패턴의 이점
M, V, C 가 각각 어떤 것들인지 감을 잡았으니 이런 생각을 해보자.
더러워진 옷은 세탁이라는 행위 말고도 그냥 버린다던지 걸레로 만든다던지 하는 다른 차원의 일을 할 수 있다. = 프로그램 상에서 가공하고자 하는 데이터는 프로그램이 참조/변환할 수 있지만 프로그램 자체에 종속적이지는 않다.
기능 부분의 남은 세탁 시간을 표시하는 부분의 패널은 경우에 따라 냉장고의 온도를 표시하거나 저울의 중량을 표시하는 데에 사용될 수도 있다. = 뷰는 사용자에게 가장 밀접하며 MVC 패턴 중 가장 상호 의존도가 높은 편이며 재사용의 범위가 다른 것들에 비해 좁다. 따라서 새로운 프로그램을 만들 때 가장 높은 빈도로 신규 생성되는 패턴이다.
세탁기를 만드는 공장에서 매년 새 제품을 개발해 판매할 때 모든 제품의 기능 버튼을 동일하게 만들 수는 없다. 하지만 궁극적으로 세탁은 ‘물을 넣고-통을 돌리고-탈수 한다’ 라는 공통의 행위를 만족시켜야 한다. 세탁기 제조 회사에서는 이러한 공통행위를 위한 부품을 미리 만들어 놓고 새로운 제품을 개발할 때 마다 기능 버튼만 바꿔서 세탁기를 만드는 것이 훨씬 효율적일 것이다. = 특정 기능을 하는 클래스를 만들어 둔다면 그 기능을 사용하는 모든 프로그램은 해당 클래스를 그냥 가져다 씀으로써 개발 비용을 절감할 수 있다.
MVC 의 측면에서 본 우리가 만든 앱
Hello, World – Hello, iPhone 프로그램은 MVC 패턴을 제대로 적용하기에는 그 단위가 너무 작지만 (굳이 지정해야 한다면) 모델은 Hello, World / Hello, iPhone 이라는 문자열, 뷰는 xib 파일, 컨트롤러는 클래스 파일로 볼 수 있다.
IBOutlet, IBAction – Connection 에 관하여
인터페이스 빌더에서 생성한 Label 객체에 IBOutlet 으로 선언해 둔 myText 를, Button 객체에 changeText 메소드를 연결하는 행위는 뷰와 컨트롤러의 접점을 만들어 줌 으로써 각각의 패턴에서 서로를 인식할 수 있도록 하기 위함이다. 이렇게 함으로써 클래스 파일 (=컨트롤러)은 다른 모양의 xib 파일에 적용해도 IBOutlet 과 IBAction 을 적절히 연결하기만 하면 얼마든지 재사용 가능하다.
IBOutlet 은 인터페이스 상의 특정 객체와 클래스 내 객체변수의 연결 고리가 된다. 클래스 내에서 인터페이스 상의 객체를 가르키기 위해서는 인테페이스 상의 객체를 식별할 수 있는 객체변수가 있어야 하는데 이 객체변수를 IBOutlet 이라는 키워드로 정의해 두는 것이다.
IBAction 은 IBOutlet 과 비슷한 개념으로 인터페이스 상의 특정 객체의 상태/동작에 반응하는 메소드를 선언하는 키워드 이다.
@property 와 @synthesize
클래스 내에서 사용되는 객체 변수는 getter/setter 라는 방식을 통해 그 값을 참조/할당 한다. Objective-C 2.0 이전까지는 이러한 접근자 메소드를 직접 만들어 주었어야 했는데 2.0 부터는 @property 키워드를 통해 해당 객체 변수가 접근자 메소드를 이용해 값의 참조/할당이 이루어진다고 지정할 수 있다. @synthesize 키워드는 이러한 접근자 메소드가 모듈 내에서 자동적으로 생성됨을 알리는 역할을 한다. 즉, 두 키워드를 이용하고 컴파일을 하면 바이너리 단 에서는 프로그래머가 일일이 입력하지 않은 접근자 메소드들이 자동으로 프로그램에 합성된다.
@property 의 용법
@property 키워드를 보면 nonatomic, retain 과 같은 속성들과 함께 사용됨을 알 수 있는데 그 속성의 종류는 아래와 같다 (@Outsider's Dev Story).
- getter=getterName - getter의 이름을 getterName로 지정
- setter=setterName - setter의 이름을 setterName로 지정
- readwrite - 기본동작으로 getter와 setter를 모두 생성
- readonly - getter만 생성. 값을 할당하려고 하면 컴파일 오류 발생
- assign - 기본동작이며 setter가 간단한 할당을 사용. (예 location = where;) 객체를 소유할 필요가 없을때 사용.
- retain - assign과 비슷하지만 레퍼런스 카운트를 증가시킴. 포인터객체를 할당할 경우에 외부에서 객체가 릴리즈되어 파괴된 객체를 참조하는 문제를 막기 위해 클래스가 멤버객체를 소유하도록 레퍼런스 카운트를 증가시킨다.(이전 값은 release)
- copy - 할당하는데 객체의 복사본을 사용. 포인터 객체의 경우 레퍼런스의 값이 바뀌어 프로퍼티의 값이 바뀌는 걸 막기 위해 setter에서 복사본을 만들어서 할당
- nonatomic - atomic이 기본동작. 멀티 스레드 환경에서 동시에 여러 스레드에서 하나의 프로퍼티에 접근하려면 문제가 생기는데 이를 보호하기 위해 atomic 을 사용. 단일 스레드 환경에서는 nonatomic 을 사용