그림 1.



Corner에 라운드가 처리된 ImageView가 필요해서, 만들게 되어 이러게 공유 합니다 ^^

구글링 해보면, 이런저런 기법들이 많이 나오는데요, 복잡한 해법들이 많은데,
저는 UIBeizerPath라는 녀석을 이용해서, 간단히 구현 하는 방법을 소개 해볼까 합니다.

아직 UIImageView를 이용하면 가장 좋을것 같은데, 방법을 따로 못찾아서, UIView를 상속 받아서, 구현하였습니다.
혹시 아시는 분 있으면 공유! 부탁드립니다 !

RoundRectedImageView.h

#import <Foundation/Foundation.h>

@interface RoundRectedImageView : UIView {

}

@property (nonatomic,retain) UIImage *image;

-(id) initWithImage:(UIImage *) aImage;

@end


RoundRectedImageView.m

#import "RoundRectedImageView.h"


@implementation RoundRectedImageView

@synthesize image = mImage;

-(id) initWithImage:(UIImage *) aImage {

    

    self = [super initWithFrame:CGRectMake(0, 0, aImage.size.width, aImage.size.height)];

    self.image = aImage;

    self.backgroundColor = [UIColor clearColor];

    return self;

}


-(void) drawRect:(CGRect)rect {

    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(16.0, 16.0f)];

    [path addClip];

    [self.image drawInRect:rect];

}

-(void) dealloc {

    self.image = nil;

    [super dealloc];

@end


코드양이 별로 없어서, 설명도 간략하겠군요 :)
drawRect: Method만 살펴 보시면 됩니다.

1. Round가 있는 사각형 UIBezierPath Object를 만든다.
2. addClip 을 호출하여, 앞으로 그려질때, clipping 되도록한다.
3. UIImage를 그려준다.



이렇게 해주면 그림.1 의 우측 그림과 같이 RoundRect 된 ImageView가 됩니다. ^^



 

개인적으로 읽을 책들을 정리하고, 
왜 읽는가를 생각하면서 읽기 위해 정리해봅니다.

그외에 책을 추천해주시면 너무나들 감사하겠습니다 :)

1. 디자인과 인간심리



소프트웨어 개발자가 디자인에 대한 이해가 필요 할까요?

그럼요 ! 소프트웨어는 혼자 만드는 게 아니니까요.
그래픽 디자이너, UX 설계자 등과의 원만한 커뮤니케이션을 위해 그들의 일에 대한 이해가 필요 하다고 생각되어 읽기 시작했습니다.

아이러니 하게도 

" 이책은 한번에 3장 이상 읽기가 힘드니, 책 디자인에서 인간심리를 전혀 고려하지 않은 셈이죠 ;ㅂ; "
 
2. 실전 코드로 배우는 실용주의 디자인 패턴


읽어본 디자인 패턴책이, Head First , GOF 두권 정도 인데,
디자인 패턴에 대한 깊은 성찰을 위해,
여러 사람이 쓴 책을 읽어 보고 싶다는 생각이 들어, 집어 들었습니다.


아자 아자 아자 부디, 책을 읽는 것을 넘어, 서평을 쓸 정도의 부지런함이 생기길 바라며..
시작합니다 :)
 
 

다들 아시다 시피,  Objective-C 는 Abstract Class가 없습니다.

OO를 Design 할때, Abstract Class를 사용해야 할 일이 있습니다. ( 예 : Factory Method Pattern )

문법단에 없는 녀석이기에, Compiler Level에서는 Error를 발생시킬순 없지만,
RunTime에 강제하도록 하는 방법을 한번 살펴보도록 하겠습니다.
 
아래에 원문이 있으니, 한번씩 읽어보세요 :)

 http://stackoverflow.com/questions/1034373/creating-an-abstract-class-in-objective-c

원리는 간단합니다.

init 과 override를 강제 시키고 싶은 곳에서 Exception을 날린다.

 

-(id) init {

    @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] userInfo:nil];

    

    return nil;

}


 위의 예제 코드 와 같이 말이죠

해당 Class를 그냥 사용하면, 아래와 같이 RunTime Error가 발생됩니다.

 
장문의 Exception Throw를  그냥 매번 쓰고 있자니 답답하군요

prefix header에 아래와 같이 정의 합니다.

#import <Availability.h>


#ifndef __IPHONE_3_0

#warning "This project uses features only available in iPhone SDK 3.0 and later."

#endif


#ifdef __OBJC__

    #import <UIKit/UIKit.h>

    #import <Foundation/Foundation.h>

#endif


#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] userInfo:nil]

 
그리고 -(id) init은 아래와 같이 변경

-(id) init {

    mustOverride();

    return nil;

}

 
자 이러면 쉽게 되는군요.

compile 단에서 이루어지는게 아니라, 아쉽지만,
그렇다고 아무런 안전장치 없이 또 사용하는것보단 나은것 같습니다 ^^

사족으로 Java의 Interface 대신 Protocol 을 사용하고 있긴 하지만, Interface와 abstract class는 문법상으로 좀 제공해줬으면 합니다.
그럼 좀 더 great할것 같군요 :) 

1. String to java.sql.date

SimpleDateFormat ts= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Long time = ts.parse("2007-05-16 13:12:18").getTime();

java.sql.Date date = new java.sql.Date(time);

 
SimpleDateFormat으로 부터 형식에 맞는 Date String을 parse한후 long 값 생성.
long값을 통해 java.sql.Date 객체를 생성한다.

2. java.sql.date.to String

SimpleDateFormat ts= new SimpleDateFormat("yyyyMMddHHmmss");

return ts.format(date);

 
 SimpleDateFormat Object의 format() method를 통해 String을 쉽게 Generate할 수 있다. 

SimpleDateFormat Class Reference Link

Abandoned Memory ?

흔히 얘기하는 Leaked Memory 는 reference가 존재 하지 않는 잃어버린 Memory를 이야기한다. 

그럼 Abandoned Memory는 무엇일까?

Abandoned Memory는 Reference가 존재하지만 사용하지 않는 Memory 이다.

Abandoned Memory
는 Profiler가 찾을 수 없기 때문에, Debugging하기가 매우 어렵다.
허나, Instrument 의 Heap Shot기능을 이용하면,
비교적 쉽게 Debugging이 가능하다. Abandoned Memory를 찾아 낼수 있다.

Heapshot의 기능? 

Heapshot의 기능은 Heapshot간의 Memory Allocation Diff 정보를 제공한다는것이다.

HeapShot#0 -> Do some task -> HeapShot#1

이런식으로 흘러 갔을때, HeapShot#0와 HeapShot#1의 Memory allocation Diff가 0 bytes가 되는것이 이상적이라 할수 있겠다.

현실에선, Cache등의 이유로 Diff가 0가 되는일은 거의 없겠다.

허나 HeapShot이 Diff를 제공하기에 Task 후에 살아 남은 Memory Block들을 한눈에 볼수 있고,
내가 의도지하지 않은 Memory Block을 비교적 쉽게 찾아 낼 수 있다.

HeapShot 예제


위의 Sample Program은 2 Button이 존재하며, Memory Leak을 유발하는 Button 하나와 아무것도 하지 않느 버튼 하나가 존재한다.

-(IBAction) pressLeak:(id)aSender {

    [[NSArray alloc] initWithObjects:@"abcdef",@"abcdef",@"abcdef",@"abcdef", nil];

}


Leak Button을 Touch 하면 아무의미 없이 NSArray Object하나를 만든다. 


Profile 모드로 앱을 실행시키고, Instrument Template은 "Allocation"을 선택한다.

좌측 메뉴의 MarkHeap을 눌러 Heap shot을 찍을 수 있다.

아래 그림은 Heapshot을 찍은후의 Heapshot List이다.


Heap Growth Column이 Heapshot을 찍었을 당시의 이전 HeapShot과의 Memory Diff라고 보면 된다.
Memory Leak을 보여주는것이 아니라, Memory Allocation Diff를 보여주는것이기때문에,
Heapshot에 기록되는 모든 Memory가 Leak 혹은 Abandoned 되었다고 판별할 수 없다.

필자는 Heapshot4와 Heapshot8을 찍기 직전에 "Leak" button을 Touch 했다.

 
Heapshot8을 펼쳐 보면 예상과의 달리 Object들이 많이 생성되었다.
아마 Touch Handling을 위해 Framework단에서 생성하는 Object들일것이다.

위의 Test Code를 보면 NSArray를 만들었기에,  유일한 NSArray Object의 생성주기를 보자.(Instrument 우측 View에 나타난다.)


우측에 보면, Object가 만들어진 Stack Trace가 노출되고, Framework 코드가 아닌, 내가 작성한 코드는 Bold로 Highlight된다.
해당 부분을 Double Click 하면 코드도 볼수 있다.


생각보다 Smart한 방법은 아니고, 사람에 의존적인 방식이다.
허나 Diff를 제공하여, 살아 남은 Object 를 추적할 수 있기에, 매우 유용하다.

Memory 문제에 자유로운 개발자는 아마 없을터,
Leaked Memory가 아니라, Abandoned Memory 를 찾아 내는데는  꽤나 유용한 Tip이라고 생각한다.
 

A.Java

package a;

public class  A extends B {}

 
B.Java

package b;

 abstract class B {}

 
위와 같은 Code는 아래와 같은 Compile Error가 발생합니다.

B cannot be resolved as a type


A 에서 extneds 하려는 B abstract class를 못찾는것인데,
이유인 즉은 class B 가 "no modifier"로 선언 되어 있어서 그런것이지죠.
앞에 "public" keyword를 추가해주면 error는 말끔히 사라집니다.

default 가 protected 같은 녀석이 되어 있어서, 그러려니 했었습니다만,
혹시나 해서 문서를 찾아 봤습니다.

java 에서 access control 은 아래의 4종류가 있습니다..

* public
* private
* protected
* no modifier


여기서 중요한게 no modifier가 따로 있다는 것입니다.
저는 default behavior가 protected라고 추측했었는데, 그게 아니었던것이지요.

C Language를 오래했던 제가, Java에 대해 공부가 부족했던 오해 인가 봅니다.
Scope등의 자세한 내용은 "http://download.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html" 여기서 참조 하시기 바랍니다.

참고로, protected 는 class 에는 붙힐 수 없고, memeber variable에만 적용됩니다. ㅎ



 
 
http://developers.facebook.com/docs/guides/mobile/#android

FaceBook사에서 Guide를 했는데도 불구하고,

07-29 10:11:50.140: DEBUG/Facebook-authorize(11292): Login failed: invalid_key


 위와 같은 error가 발생하며, Login Page로 가지 않는 문제가 있었다. 
 
이래저래 서치를 해본 결과
Android Hash Key를 생성하는데서 문제가 있었다. 
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore 
| openssl sha1 -binary
| openssl base64

http://developer.android.com/guide/publishing/app-signing.html#debugmode

Android 개발자 사이트를 보면, debugmode에서의 app-sigining에 대한 내용이 있다.

Eclipse에서 applicatoin을 debug로 signing할때, "android"란 password를 통해서 생성sign하기때문에, keytool에서도 동일 password를 써줘야 한다.




요새 CoreData 관련 공부에 몰두 하고 있습니다.
이런저런것들을 보고 있는데, CoreData Version Up과 LightWeight Migration에 관련 챕터를보았는데,

Modeling 을 변경했을때 자동으로 Data Migration을 해준다는것입니다 ! 
 
그래서 한번 해보기로 했습니다.

아래는 WWDC 2010 의 "Mastering CoreData" Session의 Slide 캡처 화면입니다.

 
음 샬라샬라~
기존의 모델을 유지하고, Version  올려 주고, Option만 주면 된다는 간략한 내용이군요

기존의 자료 ( Apple의 Core Data Programming Guide 마저도... ) Xcode3기준으로 작성되어 약간의 Editor 사용법이 다르지만, 어렵지는 않습니다.
진행 순서는 아래와 같습니다.

1. 버전 추가
2. Attribute renaming
3. 소스 변경
4. Model Current Version 변경
5. 옵션 추가

1. 버전 추가

 위와 같이 Model Editor에서 Editor->Add Model Version 클릭 해주시면 Version 생성 다이얼로그가 나오고는데 적당한 이름을 사용하시면 됩니다.

2.  Attribute renaming
attribute의 이름을 변경합니다. 저는 createdDate라는 Attribute를 createdDate2로 변경했습니다.
예제를 rename 으로 한데는 이유가 있겠죠, 

rename의 경우 original attribute name을 core data 쪽에 알려줘야합니다.

entity를 선택한후 Data Model Inspector의 "Renaming ID"에 원래 이름을 적어줍니다 저 같은 경우 createdDate가 되겠군요


3. Class 변경

class 의 변수 이름을 createdDate 에서 createdDate2로 변경해줍니다.
Xcode내의 refactoring 기능을 이용하면 한번에 수정가능합니다.

단, NSSortDescriptor나 NSPredicate에서 문자열로 사용한 경우에는 수동으로 찾아서 변경해주어야합니다.

 

 4. DataModel 현재 버전 변경하기


위에서 보시는 바와 같이, Version을 추가하여, 새로 만들었지만, 현재는 예전의 Model을 사용하도록 되어 있습니다. 
Model 파일의 Root(Locations.xcdatamodelId)를 선택한후 File Inspector를 열어서 현재 버전을 세팅 하실수 있습니다.

 변경후의 모습은 다음과 같겠군요


 5. NSPersistentStoreCoordinator Open Option설정하기

기본적으로는 Migration기능이 비활성화 되어 있기 때문에, lightweight migration기능을 사용하기 위해선, 몇가지 옵션을 설정해주어야 합니다.

NSInferMappingModelAutomaticallyOption 과 
NSMigratePersistentStoresAutomaticallyOption 입니다.

코드는 아래와 같겠군요

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption,
                             nil];
 
    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
    {}


 
이렇게 하고 실행하시면, 수정된 Model Class로 이전의 Data까지 사용가능하게 됩니다.

별로 어려운건 아닙니다만, 이래저래 개념적으로 확인해야 할것들이 많군요

CoreData는 살펴볼수록 괜찮은 녀석인것 같습니다 ^^ 

settings.py 를 template에서 사용하기 위해서는 기본적으로 context할때 값을 넣어 주는 수 밖에 없다.

    context = {'DEBUG': settings.DEBUG}

 
근데 view에서 매번 이것을 세팅해서 넘겨주는건 좀 아니지 않을까? :)

그래서 context_processor를 사용하여 모든 request의 context에 값을 세팅 할 수 있다.

 myapp/context_processors.py를 아래와 같이 작성하자.

def load_settings(request):

return {'DEBUG':settings.DEBUG} 


그리고 settings.py에 아래와 같이 CONTEXT_PROCESSOR를 등록한다.

 86 TEMPLATE_CONTEXT_PROCESSORS = (

 87         'myapp.context_processors.load_settings',

 88         'django.contrib.auth.context_processors.auth',
89 ) 

 
template에서의 사용법은 아래와 같다.

<div>{{DEBUG}}</div>



application으로 들어오는 모든 request를 Catch하기 때문에, 한번 해두면 어디서든 쓸 수 있다.

 

static file을 DJango Framework에서 사용하기 위한 설정

1. django.contrib.staticfiles app 설정

116 INSTALLED_APPS = (

117     'django.contrib.auth',

118     'django.contrib.contenttypes',

119     'django.contrib.sessions',

120     'django.contrib.sites',

121     'django.contrib.messages',

122     'django.contrib.staticfiles',


 ( default로 포함되어 있다.)

2. STATIC_ROOT 설정

 STATIC_ROOT = '/Users/taehoonkoo/workspaces/work_django/static/'

static file이 모이게 되는 folder

3. STATIC_URL 설정

 STATIC_URL = '/static/'

{{PROJECT_URL}}/static/로 접근이 가능하게 된다.

4. STATICFILES_DIRS

71 STATICFILES_DIRS = (

 72     # Put strings here, like "/home/html/static" or "C:/www/django/static".

 73     # Always use forward slashes, even on Windows.

 74     # Don't forget to use absolute paths, not relative paths.

 75     ("js","/Users/taehoonkoo/workspaces/work_django/testproject/static/js/"),

 76     ("css","/Users/taehoonkoo/workspaces/work_django/testproject/static/css/"),

 77     ("images","/Users/taehoonkoo/workspaces/work_django/testproject/static/images/"),

 78     ("pages","/Users/taehoonkoo/workspaces/work_django/testproject/static/pages/"),

 79 )


prefix를 사용하면, /static/js, /static/images/ 등으로 사용하면 된다.

설정이 잘못되어도, server에 올라 갈때 error가 감지 되지 않기 때문에, 아래와 같은 명령어로 확인할 수 있다.

$ python manage.py collectstatic --help
$ python manage.py findstatic my.js



원문 : https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS

 

+ Recent posts