2015. 11. 8. 21:42

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

dngchn's [Android]2014. 11. 11. 09:57

다음 지도 api 를 이용한 안드로이드 앱 개발 시작!


다음 지도 api를 이용하여 지하철 역 정보 관련 앱을 만들자.


Posted by dngchn
dngchn's [iOS]2014. 11. 11. 08:27

NSObject는 Objective-C의 최상위 클래스 임.

최상위 클래스 이므로 부모 클래스가 없음.

+ 로 시작하는 Class Method

- 로 시작하는 Instance Method

 

NSObject * object = [NSObject alloc];


NSObject의 클래스 메서드인 alloc를 호출하여 그 리턴 값을 object라는 변수에 넘겨 줌(저장함)

alloc의 역할: 새로운 인스턴스 객체를 만들어(메모리 할당) 그 객체의 주소를 리턴 함.

 

모든 클래스는 NSObject를 부모로 섬김으로(NSObject를 상속하므로) alloc 메서드는 어느 클래스에나 존재함.

 

NSArray * array = [NSArray alloc];
[array init];


init는 인스턴스 메서드 임.

init의 역할: 객체의 초기화를 담당. 성공하면 자신의 주소를 리턴, 실패하면 자신을 해제 한 후 nil을 리턴.

그러므로 위의 코드는 엄밀히 보면 init 실패 시 array는 의미없는 주소를 갖게 됨.


NSArray * array = [NSArray alloc];
array = [array init];


또는


NSArray * array = [[NSArray alloc] init];


이렇게 바꿔야 함.

위와 같이 생성 후 사용이 끝나면 해제 해야 함.


[array release];


'dngchn's [iOS]' 카테고리의 다른 글

[내가쓰는강좌 1] Objective-C, 메모리 관리  (0) 2014.11.07
Posted by dngchn
dngchn's [iOS]2014. 11. 7. 18:18

일반적인 언어들의 메모리 관리 타입 3가지

1. C/C++

포인터 개념으로 모든 메모리 관리(할당 및 회수)를 직접 한다(코드레벨에서 관리 함수를 제공하여 그 권한과 책임을 개발자에게 줌).

장점: 세부적인 메모리 관리가 가능

단점: 잘 못하면 메모리 누수 등 문제 발생 함. 개발자 부담이 큼.

 

2. Java/C#

가비지 콜렉터 개념으로 별도의 메모리 관리 함수를 제공하는 것이 아니라 시스템 상에서 사용 안하는 메모리를 자동으로 제거해 주는 개념임. 개발자가 할일은 없음.

장점: 개발자가 메모리 관리에 신경 쓰지 않아도 됨.

단점: 모바일 환경에서는 성능상 좋을게 없음(느림).

 

3. Objective-C

Retain Count 개념으로 언어적으로 retain count를 관리하는 메서드를 제공하여 사용 안하는 메모리가 일종의 가비지 컬렉터에 의해 회수 되는 개념임.

장단점: 1번과 2번의 중간적인 개념이어서, 개발자의 부담은 1번 보다는 줄지만, 객체 사용 후 뒤처리를 해줘야 해서 2번 보다는 신경쓸 일은 있음. 2번 보다는 성능상 좋음.

Posted by dngchn
dngchn's [C#]2014. 11. 6. 11:46

Random Number

亂數

亂(어지러울 란)자를 쓰니, 어지러운 수, 규칙없는 수가 되겠다.

굳이 우리말로 하자면.. 음.. 뭘까.. 꼴림수...?

암튼 난수가 필요하다.

아이스크림 쏘기 사다리를 타거나,

나 같은 경우는 디비에 더미데이터를 왕창 때려 넣기 위해서..


C#에서는 Random 클래스를 제공한다.

기본적인 사용법은 아래와 같다.

Random r = new Random();
randomNumber = r.Next(1, 11);

간단하다, Random 타입 객체를 생성하고, Next 메서드를 호출하여 난수를 얻는다.

Next 메서드의 인자로는 최소값, 최대값을 준다.

(*최대값은 원하는 최대값 + 1값을 넘긴다. 1부터 10까지 중 난수를 얻고자 한다면,

Next(1, 11); 과 같이...)


자, 한번 코드로 확인해보자.

우선 간단히 WPF로 프로젝트를 하나 만들자(WPF가 아닌 윈폼도 전혀 무방하다..).

화면엔 간단히 리스트 박스 하나로 꽉 채우자. 아래 처럼.

너무 썰렁한가..


코드도 썰렁하다.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {            
            string logString = string.Empty;

            for (int i = 0; i < 10; i++)
            {
                Random r = new Random();
                logString += (r.Next(1, 11).ToString() + " ");
            }
            
            this.resultLog.Items.Add(logString);
        }
    }

타이머를 하나 두어 반복적으로 실행시키고, 타이머 핸들러에서는 매 주기마다

1부터 10까지 수중 하나를 10개씩 만들어 리스트 박스에 표시하도록 했다.

어떤까.. 뭐 간단한 코드이니 더 들여다 볼것도 없다.

실행해보자.

...

이게 뭐냐.. 주기마다 난수가 생성되긴 하지만, 매 주기마다 반복문을 돌며 난수가 아닌 동일한 수가 만들어졌다.

분명, for문을 통해 반복적으로 Random 객체를 새로 생성해서 난수를 얻는데, 왜 10번 모두 같단 말인가..

(만일 주기마다 10개가 아닌 1개씩만 찍어보기로 했다면, 위 코드와 결과에서 어떤 이상함도 발견하지 못했을 수도 있다.)

...

이유는 간단하다. Random 객체는 시간을 seed로 삼아 생성된다. 위 코드에서는 반복문을 열라 빨리 돌면서,

거의 동시간(?)대, 즉 시간적으로 큰 차이를 보이지 않는 순간에 모두 생성되어, 동일한 구간의 난수를 만들어 내니,

10번의 결과가 모두 같은 것이었다.


그럼 이를 어떻게 해결하는가..

위 문제의 원인대로, Random 객체를 서로 비슷하지 않은 시간에 생성하면 될 것이다.

그러나, 위와 같은 시나리오에서는 Random 객체를 난수가 필요할 때마다 매번 생성시킬 필요는 없다.

처음 하나 만들어 두고 주구장창 써도 괜찮을 것이고, 오히려 이편이 매번 같은 시간대에 동일한 seed로 Random 객체를

생성시키지도 않을 것이다.


어디 한번 해보자.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            Random r = new Random();
            string logString = string.Empty;
            
            for (int i = 0; i < 10; i++)
                logString += (r.Next(1, 11).ToString() + " ");
            
            this.resultLog.Items.Add(logString);
        }
    }
}

Random 객체 생성 부분을 for문 밖으로 빼냈다.

(아예, 멤버로 선언하고, timer 핸들러 밖으로 빼내도 무방할 것이다. 타이머의 주기가 엄청 빨라지면 똑같이 동일한 문제가 발생할 수도 있으니...)


자, 이제 실행해보자.

뭐 이정도면 충분히 골고루다. 충분히 어지럽다... 끝.


Posted by dngchn
dngchn's [Android]2014. 11. 5. 11:30

하나의 솔루션만을 열어서 사용하던 Visual Studio에 익숙한 사용자라면,

나처럼 이클립스에 초보인 사용자라면, 처음에 분명 비슷한 고민이 생기리라.

아래와 같은 상황이라면 어떨까..


늘어가는 프로젝트, 바로 어제 작업했던 프로젝트 이름 고르기도 헷갈리고,

무엇보다, 처음 이클립스 띄우면 모든 프로젝트를 컴파일 하느라 버벅이는 듯 하다.

뭔가 폴더 구조를 만들어 묶어서 관리하고 싶은데.. . 라는 생각이 들었을때,

이를 위해 이클립스에서는 Working Set이라는 것을 제공한다.

말그대로 작업 묶음으로 이해하면 되겠다.


워킹셋을 이용하기 위해 Package Explorer에서 마우스 우클릭으로 New->Java Working Set을 아래와 같이 선택한다.


아래와 같이 뜨는 창에서 적당한 워킹셋의 이름을 주고, 해당 워킹셋에 포함시킬 프로젝트를 선택하고 Finish 버튼을 누르면 끝이다.


자, 이렇게 하고 Package Explorer를 보자. 엥? 뭐야.. 달라진게 없잖아... 라고 생각할 수도 있겠다.

아래와 같이 Package Explorer의 상단 메뉴 중 아래 화살표를 눌러 'Top Level Elements' -> 'Working Sets'으로 선택한다.

그러면 종전과는 다르게 Package Explorer가 워킹셋으로 묶여서 정리되어 표시된다.


한가지 더 팁이라면, 워킹셋을 대상으로 마우스 우클릭하여 'Close Project'메뉴를 선택하면, 말 그대로 사용하지 않는 프로젝트들을 이클립스 구동시마다 컴파일되지 않도록 제외시켜둘 수 있다(닫아둔다).

자.. 어떤가, 별것 아니지만, 점점 많아지는 프로젝트를 워킹셋을 잘 활용하면 효율적으로 관리할 수 있겠다.


Posted by dngchn
dngchn's [Android]2014. 11. 4. 22:47

지난 예제를 조금 확장해보자.

우선 새롭게 뜨는 뷰에 부모(?)뷰에서 데이터를 전달해보기로 한다.

이를 위해 필요한 것은 아래와 같다.

anotherIntent.putExtra("key", "value");

말 그대로 어떠한 값(value)을 하나의 키(key)로 넣어두는 것이다.

왜? 넣어두어야 새로운 뷰에서 꺼내어 참조할 수 있으니까.


그럼 어떻게 꺼낼까? 간단하다.

Intent myIntent = getIntent();
myIntent.getStringExtra("key");

알고있는 키를 통해 얻어내면 된다.


그럼 이와 같은 기본 개념을 갖고 예제를 통해 확인해보자.

우선 부모 뷰에서 EditText를 하나 두어, 사용자가 무언가 데이터를 입력하도록 하고,

이 데이터가 새로이 보여지는 뷰에 전달되도록(정확히는 새로운 뷰에서 꺼낼수 있도록) 하여,

뷰간의 데이터가 전달됨을 확인해보자.


우선 부모 뷰의 레이아웃(activity_main.xml)이다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
    
    <EditText
        android:id="@+id/toSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
    
    <Button
        android:id="@+id/nextButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Next"
        /> 

</LinearLayout>

EditText를 배치하여 새로운 뷰로 전달할 메시지를 입력 받는다.


부모 뷰의 액티비티(MainActivity.java) 코드이다.

public class MainActivity extends ActionBarActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Button btnNext = (Button) findViewById(R.id.nextButton);
		btnNext.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {				
				Intent anotherIntent = new Intent(getApplicationContext(), AnotherActivity.class);
				TextView toSend = (TextView) findViewById(R.id.toSend);				
				anotherIntent.putExtra("message", toSend.getText().toString());
				startActivity(anotherIntent);				
			}
		});
	}

startActivity를 호출하기 전에 'putExtra'를 통해 EditText에 입력된 텍스트를 'message'를 키로 삼아 넣어둔다.


새롭게 뜨는 뷰의 레이아웃(activity_another.xml)이다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <TextView
        android:id="@+id/sentFrom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
    
    <Button
        android:id="@+id/closeButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Close"
        />   

</LinearLayout>

부모 뷰로부터 받은 메시지를 보여주기 위한 TextView를 위치 시킨다.


새롭게 뜨는 뷰의 액티비티(AnotherActivity.java) 코드이다.

public class AnotherActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);	
		setContentView(R.layout.activity_another);
		
		TextView sendMessage = (TextView) findViewById(R.id.sentFrom);
		Intent myIntent = getIntent();		
		sendMessage.setText(myIntent.getStringExtra("message"));		
		
		Button btnClose = (Button) findViewById(R.id.closeButton);
		btnClose.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {				
				finish();				
			}
		});
	}
}

'getStringExtra'를 통해 부모 뷰로부터 전달된 데이터를 받아 표시한다.

데이터를 넘길 때 사용한 키('message')를 이용하여 얻어낸다.


이렇게 하여 실행해보면 아래와 같이 잘 동작됨을 확인할 수 있다.



만약 데이터의 흐름을 이번과 반대로 즉, 새로운 뷰에서 생성된 데이터가 부모 뷰로 가게 하려면 어떻게 해야 할까?

이는 다음 글에서 계속해서 알아보도록 하자.


Posted by dngchn
dngchn's [Android]2014. 11. 3. 13:54

안드로이드 앱 상에서 빈번히 발생하는 뷰의 전환 방법에 대해 이야기 해보자.

하나의 뷰는 Activity에 의해 관리된다. 뷰, 즉 사용자가 보는 화면이 전환된다는 의미는 Activity가 전환된다는 의미이다.

현재 보여지고 있는 뷰에서 다른 뷰로의 전환은, 전환하고자 하는 다른 뷰를 말 그대로 아래와 같이 시작시키면 된다.


StartActivity(anotherIntent);


위에서 인자인 anotherIntent는 Intent 타입 객체이다.

새롭게 화면상에 보이고자 하는 intent를 만들어 인자로 넘기는 구조이다.

이는 보통 아래와 같이 작성하여 준비한다.


Intent anotherIntent = new Intent(getApplicationContext(), AnotherActivity.class);


우선, 위와 같이 기본적인 개념만 잡고 직접 실습을 통해 시작해보자.

우선 재빨리 New->Android Application Project메뉴를 통해 ChangeViewTest 이름의 프로젝트를 생성하자.

기본 Hello world 프로젝트에 Activity(View)를 하나 추가하여 전환시켜보자.

일단 기본 뷰에 버튼을 하나 두고, 누르면 다른 뷰로 전환, 다른 뷰에서 버튼을 눌러 닫으면, 원래 뷰로 돌아오는 시나리오로 만들어 보자.

또 다른 뷰를 위해 레이아웃(activity_another.xml)과 액티비티(AnotherActivity.java) 추가한다.



우선, 기존 레이아웃과 액티비티 먼저 준비하자.

기존 레이아웃(activity_main.xml)에는 버튼 하나 추가 한다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
    
    <Button
        android:id="@+id/nextButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Next"
        /> 

</LinearLayout>


기존 액티비티(MainActivity.java)에는 버튼에 대한 핸들러를 아래와 같이 추가한다.

기본 개념대로 Next 버튼을 클릭했을 때, 새롭게 띄워질 뷰에 대한 Intent를 준비하여 'startActivity'를 통해 새로운 액티비티를 시작해준다. 간단하다.

public class MainActivity extends ActionBarActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Button btnNext = (Button) findViewById(R.id.nextButton);
		btnNext.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent anotherIntent = new Intent(getApplicationContext(), AnotherActivity.class);
				startActivity(anotherIntent);				
			}
		});
	}


이제 새로이 추가한 레이아웃과 액티비티를 다음과 같이 작성한다.

우선 레이아웃,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <Button
        android:id="@+id/closeButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Close"
        />   

</LinearLayout>


그리고 액티비티,

public class AnotherActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_another);
		
		Button btnClose = (Button) findViewById(R.id.closeButton);
		btnClose.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				finish();				
			}
		});
	}
}

위에서도 어려울 것 없다. 새롭게 보여지는 뷰는 달랑 버튼 하나를 갖고 있으며,

버튼이 클릭되면 자신을 죽인다(finish).


자, 위 기본 개념대로 새롭게 뷰를 전환할 준비가 다 되었다. 컴파일 하고 결과를 확인 해보자.

...

...

잘 되는지?

아마도 에러가 날 것이다. 이렇게 까지 먼저 해보도록 하는 이유는, 다음에서 필요한 과정을 깜빡 잊기 쉬워,

다시 한번 이렇게 강조 하고자 함이다.

우리는 앱에 새롭게 액티비티를 추가하였다. 이에 대한 사실을 명확히 알려야 한다. 다시 말해 우리의 앱을 실행 시킬 그 무언가의 주체에게 우리 앱은 이러저러한 액티비티가 있다고 알릴 필요가 있다. 어디에?

바로 AndroidManifest.xml에!


매니페스트 파일(AndroidManifest.xml)에 다음과 같이 액티비티를 추가하자.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.changeviewtest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!--새롭게 추가한 액티비티 정의 -->
        <activity android:name=".AnotherActivity"></activity>
    </application>

</manifest>


자, 이제 다시 실행해보자. 아래와 같이 화면이 뜨고, Next를 누르고, Close를 눌러보자.

성공이다! 우리가 새롭게 끼워 넣은 뷰가 보이고 닫히고, 단순하기 그지 없지만, 우리가 의도한 시나리오 대로 잘 동작 한다.





이것으로 아직 끝나지 않았다. 뭐 대충 화면 전환은 되지만, 실제 앱이 동작하는 시나리오를 생각 하자면,

원래 뷰와 새롭게 뜨고 죽는 뷰 사이에 무언가 데이터를 주고 받을 필요가 있을 것이다.

예를 들자면, 새롭게 뜨는 뷰를 통해 무언가 사용자에게 정보 입력을 받기위해서는 새로 뜬 뷰가 죽고 나서

사용자가 입력한 정보를 원래 뷰로 돌려줘야할 필요도 있을 것이고,

반대로 새로운 뷰가 뜰때 원래 뷰가 갖는 데이터를 보여줘야 할 필요도 있을 것이다.

이와 같은 시나리오에 대해서는 다음 글에서 한번 이번 예제를 확장시켜 알아보자.


Posted by dngchn