맨날 쓰면서 자꾸만 까먹는, Path -_ㅜ

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];


이렇게 하면 SandBox내의 Documents Directory의 Path를 가져올 수 있다.


iOS SDK 에서 Email 보내기.

그 옛날, 한국에 아이폰도 없던 그 시절 ( iOS 2버전대.. ) iOS SDK 에서 Email을 보내려면 
External URL Protocol을 이용해서 Email프로그램으로 스위치 했어야 했다.
Suspending/Resume구조 도 없던 시절이라, 그야 말로 내 App은 종료되어 버렸다 ;ㅂ;

NSString *url = [NSString stringWithString: @"mailto:foo@example.com?cc=bar@example.com&subject=Greetings%20from%20Cupertino!&body=Wish%20you%20were%20here!"];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]];
이런식으로 프로그래밍 하였었다.

iOS 3.0 이 나오면 MFMailComposerViewController라는 녀석이 생겼는데,
UIImagePickerViewController처럼, Tunkey 방식으로 사용된다.

적절한 값만 세팅하면 내가 제어 할수 없는 ViewController가 Display되고
거기서 이메일 보내고 지지고 볶고 다 한다.

MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller
.mailComposeDelegate = self;
[controller setSubject:@"My Subject"];
[controller setMessageBody:@"Hello there." isHTML:NO];
[self presentModalViewController:controller animated:YES];
[controller release];
대강 이런식으로 하면 사용할 수 있다..

MFMailComposeViewController는 MessageUI Framework에 포함되어 있음으로,
MessageUI Framework을 추가 시키는걸 잊지 말자.
그리고 MFMailComposeViewControllerDelegate도 필요 하면 구현하면 된다!!




Inspector Window에서 Status Bar를 Unspecified로 두면 된다.


XCode 3.2.5에서는 Base SDK 설정이 simulator로 설정이 안돼고, 
Device로만 선택할 수 있따.

왜 없애 버렸는지 알순 없지만... 기본적으로 "Other.."를 통해서 직접 입력할 수가 있다.

Base SDK 를 설정하면 Compiler 옵션으로 SDKROOT=iphoneosx.x 나 SDKROOT=iphonesimulatorx.x이런식으로 들어 간다.
Other를 선택해서 iphoneosx.x 이런식으로 값을 따로 설정 하면 된다.

위와 같이 iphonesimulator라고 써주면, Base SDK를 Simulator로 설정할 수 있다.

iphonesimulator라고만 쓰면 최신의 sdk 이고,
특정버전이 필요 한 경우에는 iphonesimulator4.2 이런식으로 기술 해주면 된다.

iOS 에서 외부 라이브러리를 사용할때,
가끔 가끔 unrecognized selector에러가 날때가 있다.

이건 참 짜증나는 경운데, 보통은 Objective-C Runtime버그로 이러는 경우가 있다.

라이브러리를 구현한쪽에서 Category를 이용해서 Class를 확장해서 사용하는 경우, 
Category에 정의된 selector를 Objective-C Runtime에서 못찾는 게 되는 경우다. 

Objective-C Runtime에서 라이브러리 Loading을 안한 경우인데,
이때는 Compiler , Other Linker Flag에서 -all_load 혹은 -force_load를 해줌으로써  해결할 수 있다.

Compiler/Runtime은 보통 믿고 사용하는 녀석들인데,
98% 만 믿어야지, 100% 믿으면 안된다 ;ㅂ;


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"];


혹시 왜 그런지 아시는 분 답변좀.. 
나중에 찾아봐야지 하고 있지만, 귀차니즘으로 ㅎ 
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