1. http://sourceforge.net/projects/traconwindows/ 에서 최신 버전 다운로드 받기


2. TOW_HOME설정하기

기본적인으로 설치 경로가 C:\TOW로 잡혀서, 다른 폴더에 설치 할경우 에러가 난다.
set-tow.bat을 열어서 경로를 변경해주면 된다.

3. httpd.conf 에러

이러면서 에러 발생 ㅜ_ㅜ line 42에 에러님이 계시다네요
그래서 스윽 훓어 보고, 스윽 수정하고 싶었는데,

httpd.conf안에 c:\TOW이런식으로 다 패스가 잡혀 있다 ㅜㅜ

다 수정해주니..

잘 동작한다.

이제 잘 사용하는 일만 남았네요 ㅎㅎ
TouchXML을 프로젝트에 붙혀서 사용하려고 하는데, 
내 소스도 아닌 것이 내 프로젝트에 있는것이 영 맘에 들지 않아서, Static Library 로 빌드 해서 사용하려고 시도 중입니다.

iOS 용 Static Library를 만들면, Device 용 iOS Simulator용 두가지 버전을 만들어서 
lipo Tool을 이용해서 universal library로 만들어야하는데, 이게 좀 에러다.. 

Target's Build Configuration의 Architecture를 아무리 Setting해도 Xcode 3.2.5 에서는 이게 무시된다.
Xcode 3.2.4 에서는 Base SDK 가 iOS Device와 iOS Simulator이렇게 나뉘어져 있었는데,
Xcode 3.2.5 에서는 Xcode Setting으로만 존재 하고, build configuration에서는 사라졌다.

결국 Xcode 내에서는 iOS Device용과 iOS Simulator용을 한번에 빌드 하는것이 불가능하다는 얘기다.

Script만 돌리는 Target을 만들어서, 
Xcode가 아닌 xcodebuild 를 이용해서 build 하고 merge 하게 해야 할것 같은데,,, 이건 영 맘에 들지 않는 솔루션이다.

Build Dependancy가, Xcode IDE상에 명확히 나타나지 않기 때문이다.

여튼 현재 내가 아는선에서의 방법은

Build Phase Target을 프로젝트에 추가하고 

1. xcodebuild로 device용 library 빌드
2. xcodebuild로 simulator 용 library 빌드
3. lipo로 각각의 library 를 universal 로 merge

하는 식이다.

혹시 누구 좋은 방법 없나요.. 이건 전혀 Cool 하지 못해요 ;ㅂ; 

최근에 받은 프로젝트 중에, 
KVO(Key-Value Observing)을 전투적으로 사용하는 프로젝트가 있습니다.

KVO 를 사용하면 안좋다고 생각하는 게 소프트웨어 가독성이 너무나 너무나 떨어집니다.
( 성능등 이런저런 이슈들도 있지만.. 제일 큰 문제가 이녀석이라고 생각합니다 )

현재 Target이 되는 Object를 어떤 녀석들이 Observe하고 있는지를 알기가 참 어려운점이지요.
얼마나 유연하게, 소프트웨어가 관리 되는가가 keyword인 요즘에 너무나 동떨어 지는 일이죠.

그래서 KVO를 걷어내는 일에 착수 했습니다.
그런데... 앞서 말한것처럼 누가누가 Observe하고 있냐를 알아 봐야 하는데, 이게 Code만 봐서는 알기가 매우 어렵습니다.
해당 Source Code를 Grep 하는 일 따위는 제일 나중에 하고 싶군요.

그래서 생각해본결과 

Target Object의 구현체에서 observer를 추가 하는 코드를 Override 해서 중간에 찾아 내는 코드를 찍는것입니다.

#pragma mark KVO걷어 내기 위해서가로 채서, Caller Log 찍기 위해 Override


- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {

NSLog(@"===================");

NSLog(@"[KVO Register] caller Class = %@", [observer class]);

NSLog(@"===================");

[super addObserver:observer forKeyPath:keyPath options:options context:context];

}


위의 코드를 Target Object의 구현체에 넣어주면, observer가 등록 될때마다, observing하는 Class이름을 로그에 찍어 주니,
추가하고, 소프트웨어를 돌려서 로그창을 긁으면 좀 더 쉽게 observing class를 찾아 낼 수 있습니다.

이 방법에 한가지 문제가 있는데, 그건 바로 "내가 소스코드를 가지고 있는게 아니라, 라이브러리나 Framework의 Object 를 Observing하고 있는 경우 어떻게 하느냐?" 입니다.

그건  Objective-C Runtime특성을 이용하면 됩니다.
이미 구현되어 있는 Method Implementation을 Runtime에 추가 할수 있고, 그리고 바꿔 끼어 넣는것도 가능합니다.
그건 블로그에 쓰기 귀찮으니, Googling해보시기 바랍니다 ㅎㅎ




Objective-C에서 Method를 Deprecated 마킹하는 방법에는 두가지가 있다.

-(id) initWithImageName:(NSString *) imageName __attribute__((deprecated));

-(id) initWithImageName:(NSString *) imageName withEditing:(BOOL) aEditing DEPRECATED_ATTRIBUTE;


사실 2번째 방법은 첫번째 방법을 Macro로 감싼것에 불과하다고 볼 수 있지만, 조금더 깔끔하다.

Deprecated Marking을 하고 나면, 아래와 같이, 해당 메소드를 사용한곳마다, Deprecated되었다고, 워닝을 때려준다.


API를 제공하는 단에서, 새로운 릴리즈때 API를 삭제 할 수도 있지만,
보통 Deprecated를 마킹 하고, 그다음에 삭제 하는게 API를 사용하는 사람을 배려 하는 것입니다.

다들 제발 그냥 지워버리지 말고, Deprecated해서 잠시동안만이라도 호환성을 맞춰주세요 ㅜ_ㅜ




Objective-C Class Category의 구현체에서는 @synthesize keyword를 사용하면, 
아래와 같이 에러가 뜬다. 
카테고리에서 @property를 선언하는것은 되는데, @synthesize하는것은 안된다. 

일단 이게 왜 필요 한가 ? 
Objective-C에서는  Private개념이 따로 없고, 저런식으로 Category를 이용해서 Private Interface를 사용하는데, 
내부에서만 사용하고 싶은 변수가 있을때, @property를 사용할 수 없으면, setter/getter를 손코딩 해야 하는데, 그건 참 귀찮은 일이다.

이에 대한 해결책은 의외로 간단하다.

@property는 Private Category 선언부에서 선언하고, 
@synthesize는 Main 선언부에서 선언하는것이다.

흐음.. 왜 이렇게 문법적으로 막혀있는지는 아직 잘 모르겠다 -_ - 
iPad가 Release되면서 새로추가된 Class로 사용자 TouchGesture를 인식하는 Class다.
오늘 ( 2010.11.17 )을 기준으로는 iPad(iOS 3.2) 에서 밖에 사용할 수 없는데,
iOS 4.2 에도 포함되었기에, 곧 iPhone 용 Project에서도 사용가능하다.


Detect 할 수 있는, Gesture의 종류는 위와 같다. 
CustomGestureRecognizer도 구현이 가능한데, 그것은 필요 할때 되서 스터디 할 생각이다.

일단은 UIPinchGestureRecognizer를 해볼생각인다.

UIGestureRecognizer의 Event 처리방식은 Delegate Pattern이 아닌, Target Action을 따른다.
Gesture라는게 여러 상황이 발쌩할 수도 있는데, Target Action Pattern을 이용한다는 뜻은, 
결국 한종류의 Event만을 사용하게는 뜻으로 보인다.

여러 종류의 Event를 처리 하고 싶으면, 여러 종류의 UIGestureRecognizer를 붙여야 할테다. 

UITouch 와 달리 Gesture는 UIResponder 가 아니라, UIView에서만 동작한다.

UIKIT_CLASS_AVAILABLE(2_0) @interface UIView : UIResponder<NSCoding> {

  @package

    CALayer        *_layer;

    id              _tapInfo;

    id              _gestureInfo;

    NSMutableArray *_gestureRecognizers;


Class 선언에서 보는것과 같이 View에 UIRecognizer를 add 시키면, 내부적으로 Array로 관리한다.
UIView에 UIGestureRecognizer 를 add 하고 remove하는 method들은 아래와 같다.


@interface UIView (UIViewGestureRecognizers)


@property(nonatomic,copy) NSArray *gestureRecognizers __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);


- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);

- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);


@end


UIGestureRecognizer Object를 생성하고,  특정 View에 그 Object를 Add 해주면 자동적으로
Target-Action Mechanism 으로 특정 Selector가 불러진다.
아래는 UIPinchGestureRecognizer 를 생성하고 추가 하는 예제 소스 코드이다.


- (void)viewDidLoad {

    [super viewDidLoad];

UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc]

initWithTarget:self action:@selector(handlePinchGesture:)];

    [self.view addGestureRecognizer:pinchGesture];

    [pinchGesture release];

}


이 것만 해주면 Pinch Gesture가 해당 View에서 일어나면, @selector(handlePinchGesture:) 가 invoke 된다.
아래 소스는 selector의 구현 예제다.


- (void)handlePinchGesture:(UIGestureRecognizer *)sender {

UIPinchGestureRecognizer *pinchGesture = (UIPinchGestureRecognizer *) sender;

NSLog(@"scale = %f", [pinchGesture scale]);

NSLog(@"velocity = %f", [pinchGesture velocity]);

if([pinchGesture velocity] > 0.0f) {

label.text = @"Outward Pinch";

}else {

label.text = @"Inward Pinch";

}

}


argument 로 UIGestureRecognizer 의 object가 던져 지는데,
UIPinchGestureRecognizer의 경우 scale ( 절대 값) 과 velocity ( 상대값 ) 이 들어 온다.
velocity가 + 값이면 확대 Gesture , - 값이면 축소하는 Gesture라고 보변 된다.

UIGestureRecognizer가 없을때는 UIResponder의 Touches값을 이용해서 이래저래 번거로이 구현했는데,
UIGestureRecognizer를 이용하면 쉽고, 빠르게 구현할 수 있다.

덤으로, "시작하세요! 아이폰 프로그래밍" 의 PinchMe 예제를 UIGestureRecognizer를 이용해서 재구현한 프로젝트를 
첨부하니, 참고용으로 사용하시기 바랍니다.

주의) 최신버전의 iOS SDK(3.2.4)에서는 동작하지 않고, 현재에는 Xcode 3.2.5 GM을 이용해서 해야 합니다.


iOS 에서 사용될 라이브러리를 만들때, 

Simulator 와 Device Arch Type이 달라서, 2가지 library 파일을 생성해야한다.

최근에 어떤 프로젝트 소스 코드를 본적이 있는데,

libxxxx-iphoneos.a libxxx-iphonesimulator.a 를 생성해두고,

Other Linker Flag 에서 -lxxx-$(EFFECTIVE_PLATFORM_NAME) 이런식으로
Platform 에 dependency하게 명명해두고, 사용하는것을 봤다.

예전에, universal 한 static library 를 본적이 있는것 같아서, 언제나 처러 search 고고싱 ~!

lipo 라는 cli tool로 이런 multi arch type library를 merge 할 수 있는데, 

커맨드는 아래와 같다. 

$ lipo -create -output myLib.a myLib-arm6.a myLib-arm7.a myLib-i386.a


정상적으로 생성되었는지 확인하려면 'file'이라는 cli command로 확인가능하다.

$ file myLib.a myLib.a: Mach-O universal binary with 3 architectures

myLib.a (for architecture armv6): current ar archive random library 

myLib.a (for architecture armv7): current ar archive random library 

myLib.a (for architecture i386): current ar archive random library


자 이제 쓸데 없이 거추장 스러웠던 환경변수는 집어 던지고

하나의 library를 그냥 링크하면된다 



처음 가본 속초는 참 아름다운 곳이더이다.

taehoon.koo 2010.11

최근 아이폰 프로젝트를 아이폰/아이패드 Project로 Migration하는것을 스터디 하고 있는데,

제대로 마이그레이션 할려면, 웹개발자 들이 브라우저 별로 처리를 하듯이,
현재 Device체크를 해서 따로 둬야겠지만, 좀 더 고해상도로만 보여지더라도, iPad용으로 Release를 하는것은 꽤나 간단하게 할 수 있습니다.

첫번째로, Target설정을 Universal 로 바꿔줘야합니다.

Build Configuration 에서 Target Device Family 를 iPhone/iPad로 변경합니다.


그러면 iPad에서 실행 시켰을대 Application이 아이폰 App을 실행시켰을때와 달리, 큰 해상도로 나타나게 됩니다.
그리고 iPhone으로만 설정되어 있을때 와 달리, Executable에 iPad Simulator가 추가 되어서 노출됩니다.


이것으로 끝나게 되면 정말 좋을텐데, 문제가 하나 터집니다.
이걸 알아 내는라 몇시간 삽질했습니다. ;ㅂ; 

예를 들어서 NavigationBar가 존재 하는 어플리 케이션의 경우 우측 버튼이 동작하지 않는 경우가 발생합니다.

우측 상단에 있는 저 버튼이 아무리 해도 터치가 되지 않는데, 사실 저부분뿐 아니라 일정 영역이상에서는 모두 터치가 안됩니다.
그 이유는 UIWindow의 사이즈 에 있습니다.

iPhone으로 개발 된 프로젝트는 UIWindow사이즈가 iPhone에 맞춰서 설정 되어 있습니다.
특히 MainWindow.xib를 그대로 사용한 경우에도 마찬가지인데요.

그럼 MainWindow.xib를 따로 설정해주어야 하는건가 라는 생각을 할 수 있지만,
그냥 Window사이즈만 적절히 설정해주는것만으로도 완벽하게 동작합니다.

    CGRect  rect = [[UIScreen mainScreen] bounds];

    [window setFrame:rect];


해당 소스를 ApplicationDidFinishLaunching쪽에다가 넣우주시면

UIScreen에 맞게 윈도우 사이즈를 조절하게 됩니다.

사실 제대로 Universal을 만들려면, xib파일 분리가 불가피 하겠지만,
간단히 작업해서 우선 Release만 하는 선택을 할때 요긴하지 않을까 생각합니다.


XCode Project 용 CI 구축작업을 하고 있는데,
구축을 완료 하고, Tomcat을 자동 실행 시키려고 하니 이것 또한 참 귀찮다 ㅎㅎ

Mac OS X 에 Launch Daemon 에 등록시켜 주면 되는데,  방법은 아래의 plist파일을 
/Library/LaunchDaemons/ 에 작성하는것이다. (예 : /Library/LaunchDaemons/com.tomcat.run.plist )

<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"

        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

        <key>Label</key>

        <string>com.tomcat-5.5.launched</string>

        <key>ProgramArguments</key>

        <array>

                <string>/Users/taehoonkoo/WorkSpace/severs/apache-tomcat-5.5.31/bin/catalina.sh</string>

   <string>run</string>

        </array>

        <key>RunAtLoad</key>

        <true/>

</dict>

</plist>


나 같은 경우에는 tomcat 5.5 version을 써서 catalina.sh 를 실행시켜줬지만,
apache wiki에 따르면 tomcat 6.x version에서는 startup.sh를 실행시켜야한다.

여기서 hudson webapp을 붙혀서 실행시키는데 여기서 또 말썽이다.
최초 한번은 잘되던데 2번째에는

"AWT is not properly configured on this server. Perhaps you need to run your container with "-Djava.awt.headless=true"?"


라는 에러를 내뱉으며 hudson이 동작하지 않는다. -_ㅜ.

이때는 catalina property를 수정해줘야한다.
 TOMCAT_HOME/conf/catalina.properties 파일을 열어서 마지막 즈음에 #String Cache Configuration 을 지정하는 쪽에다가

java.awt.headless=true


를 추가 해두고 재시작하면 정상 동작하는 것을 볼 수 있다.

주의해야 할점은 root 계정으로 실행시키기 때문에 .huson 폴더를 root의 home ( /var/root ) 에 생성 시킴으로, 
따로 관리를 하려면, root 계정을 활성화해서 관리 해야 한다.


+ Recent posts