Posted by 장현준

Posted by 장현준
+ (SingletonClass *)sharedSingletonClass
{
static NSMutableDictionary *dictionary = nil;
id sharedObject = nil;
NSString *className;
// for DCL
if(dictionary == nil)
{
@synchronized(self)
{
if(dictionary == nil)
{
dictionary = [[NSMutableDictionary alloc] init];
}
}
}
className = NSStringFromClass([self class]);
sharedObject = [dictionary objectForKey:className];
// for DCL
if(sharedObject == nil)
{
@synchronized(self)
{
sharedObject = [dictionary objectForKey:className];
if(sharedObject == nil)
{
sharedObject = [[[self class] alloc] init];
[dictionary setObject:sharedObject forKey:className];
}
}
}
return sharedObject;
}Posted by 장현준
{
Identifier = c.block.if;
BasedOn = c.block;
Name = "If Block";
IsMenuItem = YES;
OnlyAtBOL = YES;
Command = "if";
Expressions = "<#condition#>";
CompletionPrefix = if;
CycleList = (
c.block.if,
c.block.ifelse,
);
},
Posted by 장현준
#define __IPHONE_2_0 20000#define __IPHONE_2_1 20100#define __IPHONE_2_2 20200#define __IPHONE_3_0 30000
#ifdef __IPHONE_3_0
#if defined(__IPHONE_3_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_0)
Posted by 장현준
~/Library/Application Support/iPhone Simulator/User/Media/DCIM/100APPLE
Posted by 장현준
Posted by 장현준

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// 여기서 항목 삭제
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}Posted by 장현준
Xcode에서 객체를 새로 생성하면 다음과 같이 자동으로 code가 입력되어 있는 것을 보실 수 있습니다.
#import "TestClass.h"
@implementation TestClass
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[super dealloc];
}
@end
개인적으로 괄호 여는 스타일, 들여쓰기가 default로 공백이라는 점 등등 마음에 안드는 점이 한두개가 아닌데요, 아래의 경로에서 이러한 자동 생성되는 코드를 수정할 수 있습니다.
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates

위의 그림에서와 같이 iPhone OS의 Cocoa Touch Class, Code Signing등의 디렉토리를 보실 수 있습니다.
여기서는 Cocoa Touch Class의 Objective-C class에 있는 UIView 클래스의 기본 코드를 수정 하도록 하겠습니다.
우선
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch Class/Objective-C class/UIView subclass.pbfiletemplate
//
// «FILENAME»
// «PROJECTNAME»
//
// Created by «FULLUSERNAME» on «DATE».
// Copyright «YEAR» «ORGANIZATIONNAME». All rights reserved.
//
«OPTIONALHEADERIMPORTLINE»
@implementation «FILEBASENAMEASIDENTIFIER»
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[super dealloc];
}
@end
다음과 같이 변경하였습니다
//
// «FILENAME»
// «PROJECTNAME»
//
// Created by «FULLUSERNAME» on «DATE».
// Copyright «YEAR» «ORGANIZATIONNAME». All rights reserved.
//
«OPTIONALHEADERIMPORTLINE»
@implementation «FILEBASENAMEASIDENTIFIER»
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
- (void)dealloc
{
[super dealloc];
}
@endPosted by 장현준
// // TestProject.h // TestProject // // Created by Hyun Jun Jang on 09. 08. 10. // Copyright 2009 CompanyName. All rights reserved. //
defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions '{ORGANIZATIONNAME="CompanyName";}'
Posted by 장현준
Objective-C를 처음 접했을 때 클래스 메서드를 호출하였을 때 C의 함수 호출과 다른점이 무엇일까? 라는 생각이 들었는데.. 그냥 wrapping 한건지 아니면 뭐 또 다른게 있을지 말이죠.
그 동안 귀찮아서 미루다가 오늘 disassemble 해 보았습니다.
테스트 환경은 iPhone Simulator입니다. 따라서 레지스터 이름들이 익숙한(?) eax, ebx가 나오는데요..
실제 iPhone은 arm이라 어떻게 disassemble 될 지는 잘 모르겠습니다;;
(제가 disasm 초보라 양해 부탁드립니다~)
* 테스트에 사용 될 클래스 및 함수
@interface MyClass : NSObject
{
int _member;
}
- (void)printTest;
- (int)get;
- (void)setMember:(int)member;
@end
@implementation MyClass
- (void)printTest
{
printf("- printTest\n");
}
- (int)get
{
return 30;
}
- (void)setMember:(int)member
{
_member = member;
}
- (int)member
{
return _member;
}
@end
void PrintTest()
{
printf("PrintTest()\n");
}
int Get()
{
return 30;
}
* "-[MyClass printTest]" vs "PrintTest()" (void 테스트)
Dump of assembler code for function -[MyClass printTest]: 0x00001d7a <-[MyClass printTest]+0>: nop 0x00001d7b <-[MyClass printTest]+1>: nop 0x00001d7c <-[MyClass printTest]+2>: nop 0x00001d7d <-[MyClass printTest]+3>: nop 0x00001d7e <-[MyClass printTest]+4>: nop 0x00001d7f <-[MyClass printTest]+5>: nop 0x00001d80 <-[MyClass printTest]+6>: push ebp 0x00001d81 <-[MyClass printTest]+7>: mov ebp,esp 0x00001d83 <-[MyClass printTest]+9>: push ebx 0x00001d84 <-[MyClass printTest]+10>: sub esp,0x14 0x00001d87 <-[MyClass printTest]+13>: call 0x1d8c <-[MyClass printTest]+18> 0x00001d8c <-[MyClass printTest]+18>: pop ebx 0x00001d8d <-[MyClass printTest]+19>: lea eax,[ebx+0x1dc] 0x00001d93 <-[MyClass printTest]+25>: mov DWORD PTR [esp],eax 0x00001d96 <-[MyClass printTest]+28>: call 0x400a0x00001d9b <-[MyClass printTest]+33>: add esp,0x14 0x00001d9e <-[MyClass printTest]+36>: pop ebx 0x00001d9f <-[MyClass printTest]+37>: leave 0x00001da0 <-[MyClass printTest]+38>: ret
Dump of assembler code for function PrintTest: 0x00001ebd: nop 0x00001ebe : nop 0x00001ebf : nop 0x00001ec0 : nop 0x00001ec1 : nop 0x00001ec2 : nop 0x00001ec3 : push ebp 0x00001ec4 : mov ebp,esp 0x00001ec6 : push ebx 0x00001ec7 : sub esp,0x14 0x00001eca : call 0x1ecf 0x00001ecf : pop ebx 0x00001ed0 : lea eax,[ebx+0xa5] 0x00001ed6 : mov DWORD PTR [esp],eax 0x00001ed9 : call 0x400a 0x00001ede : add esp,0x14 0x00001ee1 : pop ebx 0x00001ee2 : leave 0x00001ee3 : ret
Dump of assembler code for function -[MyClass get]: 0x00001da1 <-[MyClass get]+0>: nop 0x00001da2 <-[MyClass get]+1>: nop 0x00001da3 <-[MyClass get]+2>: nop 0x00001da4 <-[MyClass get]+3>: nop 0x00001da5 <-[MyClass get]+4>: nop 0x00001da6 <-[MyClass get]+5>: nop 0x00001da7 <-[MyClass get]+6>: push ebp 0x00001da8 <-[MyClass get]+7>: mov ebp,esp 0x00001daa <-[MyClass get]+9>: sub esp,0x8 0x00001dad <-[MyClass get]+12>: mov eax,0x1e 0x00001db2 <-[MyClass get]+17>: leave 0x00001db3 <-[MyClass get]+18>: ret
Dump of assembler code for function Get: 0x00001ee4: nop 0x00001ee5 : nop 0x00001ee6 : nop 0x00001ee7 : nop 0x00001ee8 : nop 0x00001ee9 : nop 0x00001eea : push ebp 0x00001eeb : mov ebp,esp 0x00001eed : sub esp,0x8 0x00001ef0 : mov eax,0x1e 0x00001ef5 : leave 0x00001ef6 : ret
이것 역시 완전히 동일합니다. mac 개발에서도 eax로 리턴값을 주는군요.
이번에는 메서드를 호출하였을 경우 어떻게 코드가 실행되는지 분석 해 보겠습니다.
1. [myClass setMember:10] 호출
0x00001e67: mov edx,DWORD PTR [ebp-0xc] // myClass 주소 ($ebp-0x0c)를 edx에 저장 0x00001e6a : lea eax,[ebx+0x121b] // selector(setMember:)주소를 가리키고 있는 변수 0x00001e70 : mov eax,DWORD PTR [eax] // eax가 가리키는 곳의 값을 eax에 넣음 0x00001e72 : mov DWORD PTR [esp+0x8],0xa // 10을 esp+0x08에 넣음 (3번째 인자) 0x00001e7a : mov DWORD PTR [esp+0x4],eax // selector 값을 넣음 (2번째 인자) 0x00001e7e : mov DWORD PTR [esp],edx // 스택에 myClass 주소를 넣음 (1번째 인자) 0x00001e81 : call 0x4005 // dyld_stub_objc_msgSend 호출
objc_msgSend는 많이 들어 보셨을 껍니다. 잘못된 receiver에 메시지를 날릴려고 하면 항상 나오는 EXC_BAD_ACCESS와 같은 오류 상황에서 backtrace (call stack) 해보면 objc_msgSend()가 찍히죠.
여기서 보시면 receiver가 edx에 저장이 되고 eax에는 selector가 저장됩니다.[2] 이러한 값들을 c 코드로 보면 다음과 같이 되겠죠.
dyld_stub_objc_msgSend(myClass, selectorOfSetMember, 10);
0x00004005 <dyld_stub_objc_msgSend+0>: jmp 0x95da0670 <objc_msgSend>
objc_msgSend() 보기
뭐가 뭔지 잘 모르겠지만 한번 보도록 하죠.
청므에 ecx에 esp+0x08, eax에 esp+0x04를 넣습니다. 각각 selector와 myClass가 되겠죠.
이렇게 넣고 0xfffeb010과 비교를 하는데 이걸 찾아보니 ignore selector라는 군요.[4] 참고 자료를 보시면
-#if WITH_OBJC
-static inline bool
-is_ignored_selector(SEL sel)
-{
-#if defined(__ppc__)
- return sel == (SEL)0xfffef000;
-#elif defined(__i386__)
- return sel == (SEL)0xfffeb010;
-#else
-# error Unsupported arch
-#endif
-}
-#endif
-_class_lookupMethodAndLoadCache 보기
일반 C++ 메서드 호출과는 비교할 수 없을 정도로 징하게 많이 호출하는군요;
보시면 _class_lookupMethodAndLoadCache, _class_getFreedObjectClass, _class_getNonexistentObjectClass, _class_isInitialized, class_initialize과 같은 메서드 들을 호출합니다. 정확히 분석해 보진 않았지만 receiver가 nil 일 경우 여기서 걸러지겠죠?? 여기서 상위 클래스의 메서드 호출 등을 수행하고 메서드를 cache 하는 동작을 한다고 합니다.[3] 머.. 여기선 특별한게 없는 듯 합니다.
다시 objc_msgSend()로 가서 보면 <objc_msgSend+110>에서 jmp eax 합니다. 여기서 eax는 selector 주소가 계산되어 실제 코드 영역으로 진입하게 됩니다. 이 부분을 통해 setMember:가 호출 되는 것이죠.
4. setMember 안에서..
Dump of assembler code for function -[MyClass setMember:]: 0x00001db4 <-[MyClass setMember:]+0>: nop 0x00001db5 <-[MyClass setMember:]+1>: nop 0x00001db6 <-[MyClass setMember:]+2>: nop 0x00001db7 <-[MyClass setMember:]+3>: nop 0x00001db8 <-[MyClass setMember:]+4>: nop 0x00001db9 <-[MyClass setMember:]+5>: nop 0x00001dba <-[MyClass setMember:]+6>: push ebp 0x00001dbb <-[MyClass setMember:]+7>: mov ebp,esp 0x00001dbd <-[MyClass setMember:]+9>: sub esp,0x8 0x00001dc0 <-[MyClass setMember:]+12>: mov edx,DWORD PTR [ebp+0x8] // edx에 self주소(ebp + 0x08) 저장 0x00001dc3 <-[MyClass setMember:]+15>: mov eax,DWORD PTR [ebp+0x10] // eax에 첫번째 인자 값(ebp + 0x10) 저장 0x00001dc6 <-[MyClass setMember:]+18>: mov DWORD PTR [edx+0x4],eax // _member(edx + 0x04)에 eax값 저장 0x00001dc9 <-[MyClass setMember:]+21>: leave 0x00001dca <-[MyClass setMember:]+22>: ret
아까부터 느끼는거지만... mac에서는 앞에 nop이 꼭 붙는군요; 저 영역이 어딘가에 사용되서 이겠죠?
특별한건 없습니다. 값 들을 보면 objc_msgSend에서 esp에 myClass 포인터를 넣어주는 것 같고, 2번째 인자로 10을 넣어주는 것 같네요.
전체적으로 보자면..... 한개의 메시지가 전달되기 위해 엄청 많은 명령어를 실행하고 있는데 좀 비효율 적인게 아닌가 생각이 듭니다.
나중에 selector 호출 속도와 일반 함수, C++ 클래스 메서드 호출 속도를 profiling 해봐야 겠네요.
어셈을 잘 몰라 하면서 분석하느라 부족한게 많은 글이네요. 나중에 한번 다시 도전 해봐야겠습니다-_-;
* 참고
[1] So you crashed in objc_msgSend()
http://www.sealiesoftware.com/blog/archive/2008/09/22/objc_explain_So_you_crashed_in_objc_msgSend.html
[2] obj_msgSend()
http://ridiculousfish.com/blog/archives/2005/08/01/objc_msgsend/
[3] The faster objc_msgSend
http://www.mulle-kybernetik.com/artikel/Optimization/opti-9.html
[4] [macruby-changes] [391] MacRuby/branches/lrz_unstable
http://lists.macosforge.org/pipermail/macruby-changes/2008-August/000280.html
Posted by 장현준
빗소리를 먹는 사람.
- 장현준
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 |