posted by By훈트 2011.01.24 18:39


의외로 쉬운 방법이 존재합니다!

 

 

[Intro]

 

보통 BitmapFactory의 decode 함수들은 메모리 Leak이 존재한다고 알려져 있습니다.

(2.1에서 수정이 되었는지 아직도 그대로인지는 잘 모르겠습니다.)

 

실제로 안그럴지 몰라도,

decode를 하면 할 수록 메모리 Leak의 위험부담은 더 커지기 마련이죠.

제가 처음에 Drawable을 Bitmap으로 바꿀 때 BitmapFactory를 사용 했었습니다.

 

정확히 말하면 Drawable을 Bitmap으로 바꾼 것이 아니라

RawResource를 InputStream으로 얻어와서 BitmapFactory로 decode한 것이었죠.

Bitmap bitmap;
InputStream stream;
stream = context.getResources().openRawResource(resource);
try {
    bitmap = BitmapFactory.decodeStream(stream);

finally {
    try { stream.close(); } 
    catch(IOException e) {}
}

하지만 위의 코드는 계속 BitmapFactory를 호출 하기 때문에

잠재적인 위험을 가지고 있습니다.

 

그렇다면 리소스로 부터 Bitmap을 얻어내고 싶다면 어떻게 해야 할까요?

 

 

[Googling...]

 

일단 포스팅 하기전에 구글링을 좀 해봤습니다.

상위 몇개의 검색 결과를 살펴보니...

 

구글링으로 살펴본 결과들은 대부분 아래와 같이 되어있었습니다.

Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);

1. 빈 Bitmap을 만들고

2. Canvas를 연결 한 뒤

3. Drawable의 draw메소드를 통해 Bitmap과 연결된 Canvas에 Drawable의 내용을 그립니다.

 

물론 이 방법이 틀린 것은 아닙니다.

하지만 만들어 줘야 하는것이,

 

1. Drawable 크기만한 빈 Bitmap

2. Bitmap에 연결할 Canvas

3. 크기를 가지는 WidthHeight 변수

 

Drawable 객체를 제외하고 두개의 객체와 두개의 변수를 더 만들어야 합니다.

중간에 setBounds() 메소드도 호출 해야 겠죠.

 

근데 여기서 한가지 잘 생각해 봐야 할 것이 있습니다.

View안에서는 Drawable을 잘 사용해서 이미지를 표시해 주고 있다는 점입니다.

Layout XML 파일 안에서 ImageView의 이미지를 지정해 줄 때

Drawable을 잘~ 사용해왔다는 것이 하나의 예가 될 수 있겠네요.

 

그렇다면 Drawable이 당연히 Bitmap을 가지고 있어야 하지 않을까요?

 

 

[BitmapDrawable]

 

질문에 대한 답은 바로 BitmapDrawable에 담겨있습니다.

아래의 코드를 보시죠!

BitmapDrawable drawable = 
            (BitmapDrawable) getResources().getDrawable(R.drawable.icon);
Bitmap bitmap = drawable.getBitmap();

읭...? 이게 끝입니다.

BitmapDrawable을 사용하면 Bitmap을 손쉽게 얻어 올 수 있습니다.

위의 길고 긴 코드가 단 두 줄로 줄어 들었습니다.

 

따로 Bitmap을 만들지 않아도 됩니다.

그냥 Drawable안에 있는 Bitmap을 사용하기만 하면 됩니다.

 

 

[주의사항!]

 

BitmapDrawable을 사용하면 손쉽게 Bitmap을 얻을 수는 있지만,

Drawable이 꼭 BitmapDrawable만 존재 하는 것은 아닙니다.

대표적인 예로 ShapeDrawable이 있을 수 있겠네요.

ShapeDrawable을 사용하면 원하는 도형(Shape 객체)을 Drawable로 사용 할 수 있습니다.

 

하지만 getBitmap() 메소드가 없기 때문에

ShapeDrawable로 부터 Bitmap을 얻어 올 수는 없습니다.

굳이 도형을 Bitmap으로 바꾸고 싶다면 위에서 봤던 빈 Bitmap과 Canvas를 만들어서

draw() 메소드를 통해 그리는 방법 밖에는 없습니다.

 

아마도 대부분의 경우 drawable 디렉토리에 있는 이미지들을 Bitmap으로 사용하려고 하지,

Shape을 Bitmap으로 사용하려고 하지는 않을 거라 생각합니다.

네네... 그럴겁니다...

 

 

[BitmapDrawable Bitmap의 특징]

 

BitmapDrawable에서 얻어온 Bitmap 객체는 보통녀석이 아닙니다.

특징을 한번 살펴 봅시다.

 

1. 우선, Bitmap을 얻어 올 때는 final 입니다.

 

레퍼런스에 보면 final로 선언되어 있습니다.

즉, 변경하지 않겠다는 의지를 표현 한 것이죠.

사실 리턴에 final을 붙여봤자 대입되는 변수와는 아무 상관이 없습니다... 네... 넘어가죠.

 

2. Immutable 입니다.

 

좀 더 강력한 녀석이 나왔습니다. Immutable, 즉, 변경 불가입니다.

Canvas canvas = new Canvas(bitmap);

만약 위와 같은 시도를 한다면, 아래와 같은 Exception이 발생 할겁니다.

Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor

3. 절대 recycle() 메소드를 호출 해서는 안됩니다!

 

Bitmap을 얻어와서 그릴거 다 그렸다고 무의식적으로 recycle() 메소드를 호출 했다...

그럼 아래와 같은 메세지를 볼 수 있습니다.

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@43774438

위의 상황은 ImageView 같은데서 사용하고 있던 Drawable의 Bitmap 객체를 얻어와서

그 Bitmap 객체에 recycle() 메소드를 호출 한 상황입니다.

한마디로 Bitmap 객체를 공유해서 사용한다고 볼 수 있겠죠.

 

 

[One more Tip - Bitmap copy]

 

그렇다면 BitmapDrawable로 부터 얻어낸 Bitmap 객체를

마음대로 바꾸고, 쓰고, 버리고 싶다면 어떻게 해야 할까요?

 

그냥 copy() 하면 됩니다.

Bitmap bitmap = drawable.getBitmap().copy(Config.ARGB_8888true);

Mutable로 복사하면 마음대로 변경해서 사용 할 수 있습니다!

 

 

[Outro]

 

이번에는 BitmapDrawable과 Bitmap에 대해서 살짝 살펴보았습니다.

 

앞에서는 BitmapDrawable을 Bitmap으로 바꾸는 이야기만 했었지만,

반대로 Bimap을 Drawable로 바꾸고 싶다면

BitmapDrawable의 생성자를 사용하면 간단히 Drawable로 만들 수 있습니다.

 

물론 구글링해서 찾은 방법이 틀린 방법은 아닙니다.

이미지와 관련이 없는 Drawable을 다루고자 할 때는

번거롭게도 draw() 메소드를 이용 해야 하는것이 맞지만,

이미지와 관련된 Drawable을 다루고자 할 때는 분명 BitmapDrawable을 사용하는 것이 더 편합니다.

 

네... 제가 하고 싶은말은 그겁니다.

이미지 파일 힘들게 바꾸지 맙시다!


[출처] 비즈페이님의 블로그

저작자 표시
신고
posted by By훈트 2011.01.24 18:36

생각보다 숨겨진 TIP이 많아요

 

 

[Intro]

 

Android에서 사용하는 이미지는 Bitmap이라는 클래스에서 다~ 알아서 해줍니다.
그리고 이런 Bitmap Object를 쉽게 만들 수 있도록 도와주는 
BitmapFactory 클래스 라는 것도 있습니다.

 

BitmapFactory는 여러가지 소스로 부터 Bitmap Object를 만들어 주는 일을 하는데,
전부 static이며 decodeXXX 라는 이름을 가진 메소드들로 이루어져 있습니다.

XXX에는 어떤 것으로 부터 decode를 하여 
Bitmap Object를 만들어 낼지에 대한 말들이 들어 가겠죠.

 


[Decoding Methods]

 

BitmapFactory.decodeByteArray() 메소드는 Camera.PictureCallback 으로 부터 받은
Jpeg 사진 데이터를 가지고 Bitmap으로 만들어 줄 때 많이 사용 합니다.
Camera.PictureCallback에서 들어오는 데이터가 byte[] 형식이기 때문에
저 메소드를 사용 해야 하는 것이죠.

 

BitmapFactory.decodeFile() 메소드는 파일을 그대로 읽어 옵니다.
내부적으로는 파일 경로를 가지고 FileInputStream을 만들어서 decodeStream을 합니다.
그냥 파일 경로만 쓰면 다 해주는게 편리 한 것이죠.

 

BitmapFactory.decodeResource() 메소드는 Resource로 부터 Bitmap을 만들어 내며
BitmapFactory.decodeStream() 메소드는 InputStream으로 부터 Bitmap을 만들어 냅니다.
뭐 그냥 이름만 봐도 알 수 있는 것들이지요.

 


[OutOfMemoryError??]

 

보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데, 
그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면

간단하게 처리 할 수 있습니다.

 

그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
아래의 예를 한번 보시죠.

Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

이미지 파일로부터 Bitmap을 만든 다음에

다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
보통이라면 저렇게 하는게 맞습니다.

 

읽어서, 줄인다.

 

그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
지금 Dev Phone에서 카메라로 촬영하면
기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,

과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?

 

실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면

아래와 같은 황당한 메세지를 발견 할 수 있습니다.

java.lang.OutOfMemoryError: bitmap size exceeds VM budget

네... OutOfMemory 입니다.
더 이상 무슨 말이 필요 하겠습니까...
메모리가 딸려서 처리를 제대로 못합니다.

 

이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지, 
하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
이것은 엔지니어들이 해결해야 할 문제 겠죠...

 

하지만 메모리 에러를 피할 수 있는 방법이 있습니다.

 


[BitmapFactory.Options.inSampleSize]

 

BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
같은 이름이지만 Signature가 다른 메소드의 차이점은
BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.

BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.


여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.

 

inSampleSize 옵션은,
애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.

 

inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.

 

2의 지수만큼 비례할 때 가장 빠르다고 합니다.
2, 4, 8, 16... 정도 되겠죠?

 

그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
inSampleSize 옵션에 4를 주어서 decoding 한 다음에,

Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

당연한 이야기 이겠지만,
내가 원하고자 하는 사이즈가 딱 1/4 크기라면

Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.

 

inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.

 


[Appendix]

 

inSampleSize 옵션을 사용하면

SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.

 


[Outro]

 

Android의 기본 어플리케이션 소스를 분석 하다보면
상당히 테크니컬한 기법들을 많이 얻을 수 있습니다.
어떻게 이런 방법으로 만들었나 싶을 정도로 매우 정교하고 복잡하게 만들어져 있지요.
참 대단한 것 같습니다.

 

아 그리고 왜 dstWidth와 dstHeight 변수 선언이 없냐고 따지시는 분들 설마 없겠죠?



[출처] 비즈페이님의 블로그

저작자 표시
신고
posted by By훈트 2011.01.24 18:13
가끔씩 마주치게 되는 "OutOfMemoryError : bitmap size exceeds VM budget" 에러는 메모리 누수가 주요 원인입니다. 이와 관련된 링크를 모아봤습니다.

* 액티비티가 멈출 때 비트맵을 재활용(즉 GC)되게 하라

- bitmap 이미지인 경우 recycle() 호출
- onPause에서 수행하는게 좋음
- ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();

* 이미지를 미리 줄여서 읽어들여라

- BitmapFactory.Options.inSampleSize 활용

* Activity Context에 대한 참조(reference)를 오랫동안 유지하지 말아라

- Drawable.setCallback(null) 사용
- WeakReference를 가진 static 내부 클래스
- 이미지를 static 변수로 처리하지 마라

* 외부(outer) 클래스의 상태에 의존하지 않는 내부(inner) 클래스는 static으로 선언하라
- 내부클래스는 외부 클래스 인스턴스를 크게 만들며 또한 외부클래스 객체가 필요이상으로 오래 살아있게 되어 메모리를 더 차지할 수 있음
- 외부클래스의 상태 필드에 접근하지 않는(즉 외부 객체의 상태에 의존하지 않는) 내부클래스는 static으로 선언

* Attacking memory problems on Android



저작자 표시
신고
posted by By훈트 2011.01.21 16:59
안드로이드용 어플을 개발할때 안드로이드 SDK에서 제공하는 에뮬레이터를 사용해도 상관은 없지만, 배포하기 전에 최종 테스트는 반드시 실기로 해야 하며, 에뮬레이터 속도가 많이 느리기 때문에 USB 디버깅을 사용하여 실기에서 개발합니다.

디버깅시 케이블 연결이 번거롭거나, 단말이 USB 디버깅을 지원하지 않는 경우 원격으로 디버깅 환경을 구성 할 수 있습니다.

먼저 단말의 터미널에서 루트 권한을 획득합니다.
su

다음으로 adbd가 사용할 포트를 지정합니다.
setprop service.adb.tcp.port 5554
다음으로 adbd를 재시작 합니다.
stop adbd
start adbd


안드로이드 SDK의 tool 디렉토리에서 다음과 같은 명령으로 단말과 연결합니다.
adb connect <단말IP주소>:5554


정상적으로 연결되었는지 확인합니다.
adb devices



저작자 표시
신고
posted by By훈트 2011.01.20 11:03
세로: portrait, 세로의 길이가 가로 보다 길때
가로: Landscape, 가로의 길이가 세로 보다 길때 

만약 코드상에서 알고 싶다면
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int deviceWidth = displayMetrics.widthPixels;
int deviceHeight = displayMetrics.heightPixels;

if( deviceWidth > deviceHeight) 
     // 가로
else
  // 세로

* 나중에 알게된 사실이지만 현재 상태를 알려주는 함수가 있다.
droid.view.Display.getRotation() 를 이용해 보자..

* 참고자료
- 스크린 사이즈 구하는 방법
DisplayMetrics displayMetrics = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int pxWidth  = displayMetrics.widthPixels;
int pxHeight = displayMetrics.heightPixels;

//--- displayMetrics.density : density / 160, 0.75 (ldpi), 1.0 (mdpi), 1.5 (hdpi)
int dipWidth  = displayMetrics.widthPixels  / displayMetrics.density;
int dipHeight = displayMetrics.heightPixels / displayMetrics.density

- Activity 화면 사이즈를 구하는 방법
StringBuffer buf = null;
 WindowManager wm = null;
 Display display = null;
 DisplayMetrics metrics = null;
     
 wm = getWindowManager();
 display = wm.getDefaultDisplay();
 metrics = new DisplayMetrics();
 display.getMetrics(metrics);
     
 buf = new StringBuffer();
 buf.append("Window display id: " + display.getDisplayId() + "\n");
 buf.append("Window orientation: " + display.getOrientation() + "\n");
 buf.append("Window width: " + display.getWidth() + "\n");
 buf.append("Window height: " + display.getHeight() + "\n");
 buf.append("Window pixel format: " + display.getPixelFormat() + "\n");
 buf.append("Window refresh rate: " + display.getRefreshRate() + "\n");
 buf.append("Window width pixels: " + metrics.widthPixels + "\n");
 buf.append("Window height pixels: " + metrics.heightPixels + "\n");
 testMsg.setText(buf.toString());


[출처] 파이드라님 블로그

저작자 표시
신고
posted by By훈트 2011.01.20 09:32
ImageButton을 이용하여 클릭했을 경우, 포커스 있을 경우, 눌렀을 경우 등등 이벤트를 발생하는 법을 알려드리겠습니다.

이와 같은 효과를 얻기 위해서는 SELECTOR라는 속성을 이용해야 합니다.

1. drawable 폴더에 "btn_on.png","btn_off.png","icon_on_off.xml" 을 생성합니다.

2. icon_on_off.xml 을 열어 아래 코드를 삽입합니다.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- selected -->
     <item android:state_selected="true"
        android:drawable="@drawable/btn_on" />
     <!-- pressed -->
     <item android:state_pressed="true"
        android:drawable="@drawable/btn_on" />
    <!-- focused -->
    <item android:state_focused="true"
        android:drawable="@drawable/btn_on" />
    <!-- default -->
    <item android:drawable="@drawable/btn_off" />
</selector>

※ 여기서 주의할 점 default 이미지 값은 맨 하단에 명시해야 정상적으로 작동된다는 점 잊지 마세요!!

3. ImageButton 을 사용하는 레이아웃 XML 파일을 열어서 아래와 같이 속성을 지정합니다.

<ImageButton android:text="Button On Off" 
   android:id="@+id/menu1" 
  android:background="#000000"
  android:src="@drawable/menu_n1" 
  ... ></ImageButton>

※ 여기서 또 주의할 점 background에 #00000000을 넣어주지 않으면 안드로이드 기본 버튼 위에 이미지를 올린다는 점!

4. 끝 ^^

여러분 모두 다이나믹하고 멋진 레이아웃 구성해 보시길 바랍니다..ㅎㅎ

p.s) 다양한 상태에 따른 이미지 지정도 가능 하다는 점.. ^^
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/bt_2" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/bt_2" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/bt_2" />
    <item android:drawable="@drawable/bt_1" />
</selector>

[출처]안드로즈님의 블로그
저작자 표시
신고
posted by By훈트 2011.01.13 13:55

Form.Location 이라는 속성을 이용해서 창의 위치를 결정할 수 있습니다.
그리고 Form.Show() 라는 메써드를 이용해서 창을 보여줄 수 있습니다.
그렇다면!!
위치를 지정하고 그 위치에 창을 띄울 수 있을 것 같은데... 어라? 잘 안됩니다 ㅡㅡ;
일단 Show() 메써드를 사용한 후에는 Location 속성을 이용해서 창이 잘 움직이는데 처음 시작할 때는 잘 안되는군요~
Show() 메써드를 실행하기 전에 지정한 Location 속성은 Form의 위치에 영향을 주지 못하는 것 같습니다.

이럴 때!!
Form.StartPosition 속성을 이용합니다.
오호라! 시작 위치를 지정할 수 있을 것 같은 속성 이름이군요!

Form.StartPosition 속성은 Point 객체가 아니고 FormStartPosition 이라는 열거형입니다.
그렇다면 어떻게 처음 위치를 지정할까요?

Form.StartPosition = FormStartPosition.Manual; //이렇게 지정해 주면 Location을 시작 위치로 사용합니다.
이 방법을 사용하면 Form을 원하는 위치에 띄울 수 있겠죠? ^^

form1.StartPosition = FormStartPosition.Manual;
form1.Location = new Point(10,10);
form1.Show();
저작자 표시
신고
posted by By훈트 2011.01.07 12:43

Protocol Java interface 같다 어떤 protocol 따르는(adopting하는클래스는 protocol에서 선언된 모든 메소드들을 구현해야한다.


  다음은 object copy하는 copyWithZone 메소드가 담긴 NSCopying protocol 선언과 이것을 adopt하는 클래스의 @interface이다.


    @protocol NSCopying


    - (id) copyWithZone: (NSZone*) zone;


    @end



    @interface Car : NSObject <NSCopying> // <NSCopying, NSCoding> 같이 

                                                                // 여러개의 protocal adopt 수도 있다.

    {

        // instance variables...

    }


    // methods...

    // protocol 안에 선언되어 있는 메소드는 따로 다시 선언할 필요 없다.


    @end



  copyWithZone 메소드의 zone 할당할 메모리 영역을 나타내는 NSZone 타입의 매개변수이다. object copy 메소드를 호출할 경우내부적으로 copyWithZone 메소드로 변환되어 호출된다따라서 클래스에 따라  copyWithZone 메소드를 구현함으로써  클래스에 맞는 올바른 copy 되도록   있다.


  다음은 <NSCopying> adopt하여 deep copy 되도록 하는 Engine, Tire, Car 클래스의 copyWithZone 메소드 구현이다.@interface 부분은 생략하겠다.


    @implementation Engine


    - (id) copyWithZone: (NSZone*) zone

    {

        /* allocWithZone 메소드는 NSObject 클래스의 class method로서

         * 해당 클래스의 새로운 객체를 생성한다.

         * 객체 자신이 아닌 [self class] 사용함으로써 

         * subclass 역시 [super copyWithZone] 호출하여 copy 사용할  있다. */

        // Engine instance variable 가지지 않으므로 instance variable copy 과정 없이

        // 바로 init으로 초기화하면 된다.

        Engine* engineCopy = [[[self classallocWithZone:zone] init];

        return engineCopy;

    }


    @end


    @implementation Tire


    - (id) copyWithZone: (NSZone*) zone

    {

        Tire* tireCopy = [[[self classallocWithZone: zone]

                          initWithPressure: pressure treadDepth: treadDepth];

        return tireCopy;

    }


    @end


    @implementation Car


    - (id) copyWithZone: (NSZone*) zone

    {

        Car *carCopy = [[[self classallocWithZone: zone] init];

        

        carCopy.name = self.name;

        

        // engineCopy copy 의해 생성된 것이므로 memory management rule 의하여 release까지 책임을 져야 한다.

        // 여기에서는 autorelease 걸어주었다.

        Engine* engineCopy = [[engine copyautorelease];

        carCopy.engine = engineCopy;

        

        int i;

        for(i = 0; i < 4; i++)

        {

            Tire* tireCopy = [[self tireAtIndex: i] copy];

            // tire역시 copy 생성되었으므로 autorelease 걸어준다.

            [tireCopy autorelease];

            

            [carCopy setTire:tireCopy atIndex:i];

        }

        

        return carCopy;

    }


    @end




  앞서 id 타입은 어떠한 object 가리킬  있는 generic 타입이라고 설명하였다다음과 같이 id protocol 붙여줌으로써 protocol 만족하는 object들만을 가리키게   있다.


    - (void) setObjectValue: (id<NSCopying>) obj;



  Objective-C 2.0 이후부터는 protocol 선언에 @optional @required라는 modifier 추가되었다@optional  구현하지 않아도 괜찮은 메소드들을 나타내고( 장에서 설명한 informal protocol처럼), @required 기존의 protocol 선언된 메소드들처럼  구현되어야 하는 메소드들을 나타낸다


    @protocol BaseballPlayer


    - (void) slideHome;

    - (void) catchBall;

    - (void) throwBall;


    @optional

    - (void) drawHugeSalary;


    @required

    - (void) swingBat;


    @end




Reference

[1] Dalrymple. M., Learn Objective-C on the Mac, Apress.



저작자 표시
신고
posted by By훈트 2011.01.04 10:31
[문제점]Code Sign error: Provisioning profile '*' specifies the Application Identifier '*' which doesn't match the current setting '*'

The executable was signed with invalid entitlements. The entitlements specified in your application's Code Signing Entitlements file do not match those specified in your provisioning profile.

 (0xE8008016).


[해결방법]

1. File -> New File ->  Code Signing -> Entitlements -> *.plist 생성
2. Project  -> Edit Project Setting -> Build -> Code Signing -> Code Signing Entitlements -> *.plist 로 수정..


저작자 표시
신고
posted by By훈트 2011.01.04 10:27

IOS Provisioning Portal 에 오신걸 환영합니다.

본 내용은 개인개발자등록이 아닌 Team 개발자 등록을 목적으로 작성되었습니다. IOS Provisioning Profile 목적은 한마디로 정의해서 개발자 혹은 개발팀에서 개발한 프로그램을 Apple Device (iPhone, iPod touch 등) 에 올리기 위한등록과정입니다. 물론 상업적인 목적인 경우는 AppStore 에 최종배포를 할 수도 있으며 이와 별도로 Mac개발이나 Safari개발인 경우에도 개발등록을 해야 애플제품 프로그램을 배포할 수 있습니다.

제품을 만든다면 반드시 장치에 올려봐야 하는데, 그 이유는 시뮬레이터에서는 Accelerometer, 가속도계, 카메라 사용 그리고 위치기반 서비스 등 서비스가 매우 제한적이기 때문이다.

궁극적으로는 진행하는 모든 작업은 ‘프로비저닝 프로파일(Provisioning Profile)을 얻기 위한 것

[원본파일] IOS Developer Program- Standard Program User Guide for IOS 4 ver2.7

1. https://developer.apple.com/ios/manage/overview/index.action

2. iPhone 개발자 ID와 PW 입력

(등록이 안되었다면 Apple Developer Connection (http://developer.apple.com) 에서 팀개발 등록)

3. https://developer.apple.com/membercenter/index.action > Invitations 팀멤버를 등록.

3.1. Program & Add-on 에서 개발팀이 개발하고자하는 분야(Mac 개발, iPhone 개발, Safari 개발)를 선택 결정.( 각 개발별 99$/1년 소요되며 Safari 개발은 무료임.)

Team Group (팀멤버구성)

  • Team Agent : 아이폰 개발자 프로그램에 최초 가입하고 애플과의 계약서에 사인한 어드민이며 배포하는 모든 제품에 대한 법적인 책임을 진다. 그러므로 모든 배포는 Agent를 통해서만 가능하다. 에이전트는 어드민과 멤버를 추가할 수 있으며 인증서 발급을 승인하고, 패포용 프로비저닝 프로파일을 관리할 수 있다.
  • Team Admin : 다른 어드민과 멤버를 추가하고 관리할 수 있으며 멤버로부터의 인증서 발급신청을 승인할 수 있고 아이폰/아이팟 터치를 프로그램 포털에 등록 할 수 있다. 하지만 배포용 인증서를 만들 수 없으므로 배포는 할수 없다.
  • Team Member : 개발용 인증서를 신청할 수 있으며, Provisioning Profile을 이용해 개발장치에 프로그램을 인스톨할 수 있다.
  • 개인개발자 등록과정은 1인이 Admin, Agent, Member 되는 것으로 별도의 팀을 만들수는 없다.

Certificates (인증서발급)

Certificates

팀멤버라면 개별적으로 iOS 개발 인증서를 요청하여 발급 받아야합니다.

1. 인증서 발급 요청서를 만들기. ( Utility 폴더> Keychain Access )

2. 키체인 접근 > 환경설정 클릭

3. 키체인 접근 > 인증지원 > 인증기관에서 인증서 요청…

4. 인증서 신청하는 멤버의 이메일과 save to disk 선택

(공개키까지 함께 관리하기 원한다면 “자시의 키페어 정보 지정”선택)

5. 공개키페어 정보 2048bits와 RSA 알고리즘 지원

Devices (디바이스 등록)

개발한 에플리케이션을 아이폰 장치에 올려보려면 장치의 정보가 프로그램 포털에 등록되어 있어야 한다.

등록은 프로그램 가입 후 언제든지 추가할 수 있지만 등록한 장치정보를 삭제하는 것은 일년에 한 번 프로그램 갱신할떄만 가능합니다. 관리하는 아이폰 장치가 많다면 애플이 제공하는 별도의 프로그램(iPhone Configuration Utility)을 이용해 일괄 등록할 수도 있다.

1. 개발장비의 UDID (Unique Device Identifier) 40 HEX문자열을 취득함.

  • Organizer 를 통해서 취득방법

  • iTunes 를 통해서 취득방법

2. 개인장비 등록하기

  • 장비명칭(장비명칭은 가급적 영어로 기재)을 기재후 UDID 등록

ADD ID (애플리케이션 구분 아이디 만들기)

AppStore나 사용자의 아이폰 환경에서 애플리케이션을 구분하기 위해 사용하는 문자열입니다. com.yourcompany.AppID 역순으로 적은 뒤 애플리케이션 실행 파일이름을 붙입니다. 다양한 애플리케이션을 하나의 App ID로 묶으려면 프로그램 포털에서 실행파일 이름을 와일드 카드 문자(*)로 정해줍니다.

ex) com.google.*

Provisioning (프로비져닝 프로파일 만들기)

프로비저닝 프로파일은 우리가 프로젝트를 빌드하면서 포함시켜야 하는 정보로서 인증서와 정치정보 그리고 App ID를 모두 포함되고 있다. 프로비저닝 프로파일을 만들면서 인증서와 App ID 그리고 개발장비를 체크해 주면 됩니다.

프로비저닝 프로파일 3가지 종류

  • 개발용 프로파일 : 개발 테스트 목적으로 준비된 프로파일.
  • 배포용 프로파일 : 임시배포용(Ad-Hoc)과 App Store 등록용 프로파일로 구분. 배포용 인증서는 에이전트만 할수 있습니다.

1. 프로비저닝 프로파일 생성

Profile Name은 통상적으로 프로젝트 이름을 명시합니다.

2. 프로비젼 프로파일을 다운로드받아서 Xcode > Organizer 의 Provisioning 위에 삽입(+ 키버튼)

3. Xcode 빌드 & 인스톨

  • Build 선택을 Device 4.x 와 Debug or Release 를 선택합니다.
  • Target > Copy Bundle identifier > Add ID 세팅(예 com.google.* or com.google.iAdSample)
  • Target 을 선택해서 애플리케이션 프로퍼티스의 identifier 를 수정합니다.

4. Xcode > Project > Edit Project Setting > Build

  • Code Signing > Any iPhone OS Device 에서 등록된 프로비젼 파일을 선택합니다.

자 여기까지 오시느라 수고했습니다.

나의 아이폰에 어플리케이션이 제대로 들어간것이 확인되면 성공입니다.


[출처] COM+EDDY'S BLOG

저작자 표시
신고