iOS Project에서 Bundle Resource의 Patfh를 가져 오는 방법은 보통 아래와 같아.

NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];


그런데, OCUnit의 Logic Test에서는 위와 같은 방식으로는 접근이 안된다.

아직 정확한 매커니즘은 파악하지 못했지만. 아래와 같은 방식으로 사용할 수 있다고 한다.

NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:name ofType:@"png"];


혹시 왜 그런지 아시는 분 답변좀.. 
나중에 찾아봐야지 하고 있지만, 귀차니즘으로 ㅎ 
Android Manifest.xml은 Android 앱의 "시작과 끝"이다.
Android Manifest를 잘 들여 다 보면, 이 앱이 어떤 하드웨어를 사용하고, 어떤 기능등을 제공하는지 대략적으로 알 수 있다.

AndroidManifest.xml을 볼때 가장 먼저 보는것이 android-permission 부분이다.

DeskClock에서 사용하고 있는 Permission List는 아래와 같다 

    <uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <uses-permissionandroid:name="android.permission.WAKE_LOCK"/>

    <uses-permissionandroid:name="android.permission.VIBRATE"/>

    <uses-permissionandroid:name="android.permission.WRITE_SETTINGS"/>

    <uses-permissionandroid:name="android.permission.DISABLE_KEYGUARD"/>

    <uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>

    <uses-permissionandroid:name="android.permission.DEVICE_POWER"/>


해당 설정들은 http://developer.android.com/reference/android/Manifest.permission.html 에서 상세 정보를 볼수 있따.

특히한것만 한번 살펴 보면,

WRITE_SETTING - Android Device의 System Setting을 Read/Write할 수 있는 권한  
DISABLE_KEYGUARD - KEYGUARD를 Disable시키는 건데, KeyGuard란 화면 Lock됐을때의 화면이다.
READ_PHONE_STATE - Phone 번호 정보등 폰에 관련된 정보를 얻기 위한 권한 

Activity 들은 UI 단의 Activity 이기 때문에, 제처두고, 

등록되어 있는 Service, Provider, Receiver쪽을 잠깐 살펴보자..

<provider android:name="AlarmProvider" android:authorities="com.android.deskclock" />


이부분은 해당 App.을 "Content Provider"로 사용하겠다는 선언이다.
android:authorities는 Content Provider의 identification 이라고 생각하면 된다.
자세한 정보는 에서 http://developer.android.com/guide/topics/manifest/provider-element.html 확인할 수 있다.


 <receiver android:name="AlarmReceiver">

            <intent-filter>

               <action android:name="com.android.deskclock.ALARM_ALERT" />

               <action android:name="alarm_killed" />

               <action android:name="cancel_snooze" />

            </intent-filter>

 </receiver>

 <service android:name="AlarmKlaxon">

            <intent-filter>

                <action android:name="com.android.deskclock.ALARM_ALERT" />

            </intent-filter>

 </service>


위와 같이 service/receiver 선언 부분이 몇가지 있는데, 
service 는 Android에서 얘기하는 "BroadCast" 를 발생시키는 녀석이고,
receiver는 "BroadCast"를 받아서 처리 하는 녀석이다.

위의 코드를 보면, DeskClock에서는 Alram을 발생시키고, 그리고 자기가 직접 처리 할수 있게 설정되어 있다.
그외에도 몇가지 Receiver들이 선언 되어 있는데,
BOOTING/TIME_SET/TIMEZONE_CHANGED/등등의 BROADCAST를 처리 할 수 있게 
Receiver들이 선언 되어 있다. 

단순한 것이지만, service를 등록하여 alram을 broadcast하고, receiver를 등록하여 alram을 사용자에게 알려주는 기능을 한다는 것을 
AndroidManifest.XML을 보면서 파악할 수 있었다.

이 Manifest 설정을 보면서 소스 코드를 파악해보도록 하자.


DeskClock ? Google사에서 오픈 소스로 여는 시계 어플리케이션이다.


Android 공부도 할겸해서, 분석해보기로 한 어플리케이션! 

Project URL  : http://android.git.kernel.org/?p=platform/packages/apps/DeskClock.git 

이 소스 분석을 통해 기대 하고 있는 부분은, Alaram Service를 위한 Service Provider 구현.
Alaram을 울리기 위한, 진동/사운드 등등 이다.

Project Page 에서 볼 수 있듯이, 소스 코드는 git 를 통해서 다운로드 받는다.

> git clone git://android.git.kernel.org/platform/packages/apps/DeskClock.git



위와 같이 다운로드 받아 진다.

Google에서 공개 하는 Source Code 는 Android Sample역시, Eclipse 에서 import 하도록 되어 있지 않고,
Eclipse의 "Create Project from exising Source"를 통해서 Eclipse에서 Project를 열도록 되어 있다.


적당한 이름을 써주고, Buid Target을 각자의 환경에 맞게 설정하고, 

Run as "Android Applicaton" 으로 실행하면,

프로젝트 실행이 완료 된것이다.


Bot으로 메일 보내는 기능이 필요 해서, SMTP 서버를 돌릴 필요가 생겼습니다.

http://www.phase2technology.com/node/667/


위의 원문 블로그가 있으니, 원문 보실분은 참조 하시길 바랍니다 ㅎ

의레 그러하듯이 OSX의 LaunchDaemon에 SMTP 를 등록해야 합니다.

사실 SMTP는 이미 등록은 되어 있는데, Service로 등록되어 있아, 수정만 해주면 됩니다.

/System/Library/LaunchDaemons/org.postfix.master.plist


를 열어서, </dict> 이 닫히기 전에 

<key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/>

아래 코드를 추가합니다.

Terminal 을 통해서 launchctl cli program을 돌려서 수동으로 서비스를 실행시키거나,
재부팅을 하면 smtp 가 돌아갑니다.

$ sudo launchctl 

launchd% start org.postfix.master


SMTP server자체는 OSX에 이미 들어 가 있기 떄문에,
별로 어려운일은 아닙니다 ㅎㅎ

Tiger나 Leopard에서는 다를 수도 있지만, Snow Leopard에서 sshd를 Enable시키는건 매우 간단합니다.

System Preference -> Sharing -> Remote Login 을 활성화 시켜주면 땡!!!


GUI를 통해 쉽게 할수 있으니 편하다고나 할까,

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 선언부에서 선언하는것이다.

흐음.. 왜 이렇게 문법적으로 막혀있는지는 아직 잘 모르겠다 -_ - 

+ Recent posts