xcode3 은  한 프로젝트당 한개의 창이 열리도록 되어 있습니다. 하지만, 프로젝트를 하다보면 동시에 두개의 프로젝트를 진행하기도 하고 동일한 앱이지만 아이폰/아이패드 용으로 개발할때도 있습니다. 전자의 경우 어떨지 모르겠지만 동일한 앱이지만 유니버셜앱이 아니라 아이폰/아이패드 용으로 따로 앱을 만들겠다 결정이 되면 프로젝트 관리가 참 머리아프게 됩니다.

똑같은 기능과 목적을 가진 앱이기 때문에 개발시 사용했던 모델이 동일하게 사용될 확률이 90%가 넘고, 앱이하는 역할이 동일하기 때문에 동일한 로직을 가지고 있는 컨트롤러가 있을 경우 또한 90%가 넘습니다. 결국, 기능의 확장이 약간 있고 사용자에게 보여지는 뷰가 조금씩 다를 뿐 내부는 똑같다는 말이지요.

이런 상황에서 프로젝트를 두개 만들고 동일한 코드는 복사해서 쓰게 되면 두 프로젝트에서 사용하는 공통 코드에서 수정 사항이나 추가 사항이 발생했을시에 작업을 두군데 모두 해줘야 하는 상황이 발생합니다. 상당히 짜증납니다. 

이를 극복하기 위해서 제일 처음에 한 짓이 공통 코드는 코드의 복사본을 만들지 않고, 코드는 한군데에 두고 각 프로젝트는 코드의 레퍼런스만을 유지한채 개발을 하는 것이었습니다. 하지만, 이 방법 또한 만족스럽지 않습니다. 왜냐하면 프로젝트 창이 두개가 열리기 때문이지요. (이럴때는 workspace 개념이 참 간절합니다.) 게다가 공통 코드가 추가 될때마다 각 프로젝트에서 코드를 추가해야 하는 불편함이 생깁니다. 

그래서 다음으로 적용해 본것이 오늘 설명할 Target 을 이용한 프로젝트 관리 입니다. xcode4 에서는 workspace 개념이 생겨서 이번 포스팅의 관리팁이 별로 소용이 없을 수도 있겠습니다. 하지만, 적어도 xocde3 를 계속 사용하시는 분들과, target 개념을 익히기 위한 분들에게는 도움이 될 것 같습니다. 

첨부하는 샘플 코드는 자동차와 관련된 작업을 하는 앱입니다. 말은 거창한데 그야말로 샘플입니다. :) 상황은 이렇습니다. 앱을 개발해야 하는 디바이스는 아이폰/아이패드 입니다. 아이폰에서는 차를 관리해 주는 앱을 만들고, 아이패드에서는 차를 판매해야 하는 앱을 만들어야 합니다. 이때 Car 라는 클래스는 공통으로 쓰입니다. 

샘플 코드의 구조를 보면 common 그룹과, iPhone 그룹, iPad 그룹 으로 코드를 구분했습니다. 이름에서 딱 감이 오시는 분들도 있으실 겁니다. ^^


그리고 처음에는 한개 밖에 없지만, 새로 Target 을 추가하고 다음과 같이 타겟을 구성 합니다. 


처음에 target 을 만들고 나면 새로 만들어진 target 에는 아무것도 추가되어 있지 않습니다. 타겟이 필요한 파일들을 추가하는 방법은 필요한 파일을 선택해서 Drag & Drop 하는게 제일 쉽습니다. 참고로 타겟내의 Compile Sources 그룹에 .h 헤더 파일이 들어가면 컴파일시 경고를 뿜어 내니까 헤더는 추가 하지 않도록 합니다. 

이렇게 구성이 되었다면, 각 타겟의 빌드 설정을 손봐 줍니다. 각 타겟을 선택을 하고 상단의 Info 버튼을 누르거나 cmd + i 단축키를 통해서 설정창을 불러내서 build 탭으로 갑니다. 그곳에 보면 

어떤 디바이스에서 실행이 되게 할것인지 설정하는 부분이 있고,

컴파일시 적용하는 flag 를 세팅해 주는 부분이 있습니다. 저 부분은 프로그래머가 임의로 정해주면 됩니다. 저 같은 경우는 iPad 에서 돌아가는 앱을 컴파일 할때와, iPhone 에서 돌아가는 앱을 컴파일 할때를 구분해 주기 위해서 IPAD라는 flag 를 만든 경우 입니다. 해당 플래그는 앱 델리게이트에서 사용하고 있습니다. 


그리고, 추가적으로 Info 에서는 프로젝트에서 사용될 info.plist 파일 이름을 지정 할 수 있고, 빌드 되고 나서의 product name 도 설정 할 수 있습니다. :)


이렇게 하고 나서 xcode 상단의 Overview 선택 박스를 열어보면 다음과 같이 되어 있습니다. 

이제 Active Target 을 필요에 따라서 선택하고 컴파일 하시면 성공~!

target 은 동일한 소스로 다른 설정을 적용하여 다른 결과물을 만들어 내는데 응용하면 유용하게 쓰일것 같습니다. 저도 target 을 이용한 프로젝트 세팅은 전부 다 알고 있는게 아니라 좀더 공부하면 더욱 좋은 쓰임새를 찾을 수 있을것 같기도 합니다. 더 좋은것을 알고 계시는 분이 있으면 댓글 달아 주시면 감사하겠습니다. :)


 << 주의점 >>

- 위와 같이 프로젝트를 구성했을경우 디자이너와의 협업을 위해서 두 프로젝트에서 사용하는 이미지 이름이 같아지면 IB 에서 혼동해서 이미지를 불러오는 경우가 생깁니다. 하지만, 실제 컴파일되는 이미지는 제대로된 이미지이니 실제로 컴파일해서 돌려보면 정상적인 이미지가 나옵니다. 





블로그 이미지

하루하루

하루하루를 열심히 살아가는 그런 사람이 되고자 합니다. 오늘을 후회없이 말이지요.

,

아마도 아이폰 개발을 하면서 제일 먼저 하게 되는것은 아마도 UIViewController 의 life cycle 을 이해하는 것일 것입니다. UIViewController 을 만들어 낼때는 두가지 방법이 있습니다. IB 를 이용해서 xib 를 만들어서 하는 방법과, 그냥 IB 를 이용하지 않고 (xib 없이) 오로지 코딩을 통해서만 하는 방법입니다. 이러한 두가지 방법중 어떤 방법을 사용하더라도 UIViewController 의 life cycle 을 이해 하는것은 굉장히 중요합니다. 언제 어느 시점에 적절한 뷰 생성과, 배치 , 해제를 해줘야 할지 알아야 하기 때문이지요. 

UIViewController 의 life cycle 은 나름대로 이해하기 굉장히 쉽습니다. 컨트롤러가 가지고 있는 메소드의 이름들은 그저 이름만 봐도 이해를 할 수 있게 쉽고 동작에 맞는 이름들로 되어 있고, 그 구조 또한 간단하기 때문입니다. 

하지만, 딱 하나의 메소드인 viewDidUnload 만은 이해하기 참 힘들었습니다. 굉장히 간단하고, 평범한 메소드 입니다. 그 뜻은 컨트롤러의 뷰가 내려갈때 불리는 함수 입니다. 근데 이것이 참 애매하기 짝이 없습니다. 

뷰가 내려갈때란 어떤때를 말하는 것일까요? Controller 가 dealloc 될때 일까요? 저도 처음엔 컨트롤러가 해제될때 dealloc 되면서 뷰가 내려갈테니까 viewDidUnload 가 불리는줄 알았습니다. 하지만, 절대로 불리지 않더군요. 그저 dealloc 만 로그창에 찍고 있을 뿐이었습니다. 그래서 좀더 세심하게 수많은 문서를 보고 구글링을 해보았지만, 돌아오는 답변은, 거의 두가지로 압축이 되더군요. 

첫째. viewController 의 view 가 nil 로 세팅될때 불리는 함수이다. 
둘째. 메모리 워닝이 통지 될때 (시스템의 메모리 워닝은 UIViewController 에게 Notification을 줍니다.) 가려진 뷰를 가지고 있는 컨틀롤러의 viewDidUnload 가 불린다.

하지만, 여전히 불리는 시점이 명확하지 않았고, 사용되지 않는다는것이 굉장히 애매했습니다. 

그래서 샘플 코드를 작성해 보았습니다.


  1. View Base 로 프로젝트를 하나 만듭니다.
  2. UIViewController 2개를 만듭니다.
    1. RedViewController , BlueViewController
    2. 각 컨트롤러의 뷰의 색깔을 빨강과 파랑으로 만들어 줍니다.
  3. 새로 만든 두개의 컨트롤러의 뷰를 기본 base view 에 차례대로 붙입니다.
  4. base view 하단 에는 red button, blue button 을 만들어서 각 버튼을 눌렀을때 각 버튼에 맞는 뷰가 보이도록 코딩 합니다.
    1. 이때 사용할 방법이 두가지가 있습니다. 첫째는 각 뷰의 hidden 프로퍼티를 사용하여 보여질 뷰에만 NO 를 세팅하고, 감춰질 뷰는 YES 를 세팅하는 방법.
    2. 두번째가 감춰질 뷰는 removeFromSuperview 메세지를 보내 base view 에서 떼어냈다가 보여질 시점에 다시 addSubview or insertview 하는 방법.
  5. 그리고, 시뮬레이터에서 가상으로 memory warning 을 발생시키도록 합니다.


위 순서중에 4번을 눈여겨 보시기 바랍니다. 4번의 방법중에 , 첫번째 방법으로 구현하고 5번을 실행할때와, 두번째 방법으로 구현하고 5번을 실행할때 중 어떤 방법으로 구현했을때 viewDidUnload 가 발생하게 될까요?

네. 결론은 두번째 방법으로 구현했을 경우 입니다. 즉, 현재 뷰에서 보여주지 않을 뷰는 (필요하지 않은 뷰는) 구지 super view 에 붙여 놓고 있을 필요가 없습니다. removeFromSuperview 메세지를 보내서 떼어 놔야 iOS 에서 "아~ 이 view 는 사용하지 않는 view구나" 라고 인식합니다. 따라서 iOS 에서 통지하는 Memory Warning 메세지를 View Controller 가 받았을때 자신이 가지고 있는 view 를 사용하지 않고 있다면 view 에 nil 을 세팅하게 되고, 그때, viewDidUnload 가 불리게 됩니다.

디바이스에서는 메모리가 항상 부족하므로, 필요없는 뷰는 그때그때 떼어내고 필요할때 붙여서 쓰면 되고, memory warning 이 발생했을시에는 viewDidUnload 에서 필요없는 객체들 또는 뷰들을 해제시켜 메모리를 확보하면 메모리 크래쉬로 앱이 죽는 일은 쉽게 일어나지 않을것입니다.

코드는 한줄 없이 장황하게 글로만 써서 죄송합니다. 샘플코드 첨부합니다. :) 주석은 없습니다. :-p


블로그 이미지

하루하루

하루하루를 열심히 살아가는 그런 사람이 되고자 합니다. 오늘을 후회없이 말이지요.

,