ListView의 구분선 삭제하기

|

ListView의 구분선 삭제

ListView에서 각 칸을 구분하는 구분선 삭제는 XML에서 ListView의 속성에 다음 속성을 추가하면 됩니다.

android:divider="@null"
android:dividerHeight="0dp"


ListView 예제

  <ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/layout_input"
    android:divider="@null"
    android:dividerHeight="0dp" />

버클리 소켓(Berkely Socket)에 대하여

|

버클리 소켓이란

버클리 소켓은 원래 BSD 4.2 운영체제의 일부로 배포되었다가, 그 후 여러 OS들이나 플랫폼, 프로그래밍 언어 등에 포팅되면서 네트워크 프로그래밍의 표준처럼 여겨지게 되었습니다.


소켓 생성

소켓 생성은 다음 명령어를 이용해서 할 수 있습니다.

SOCKET socket(int af, int type, int protocol);


Address Family

af 파라메터는 주소 패밀리(Address Family)를 의미하며, 다음과 같은 값을 가질 수 있습니다.

상수값 의미
AF_UNSPEC 지정하지 않음
AF_INET 인터넷 프로토콜 (IPv4)
AF_INET6 인터넷 프로토콜 (IPv6)
AF_IPX IPX(Internetwork Packet Exchange) 프로토콜


Type

type 파라메터는 소켓을 통해서 주고 받는 패킷의 종류를 의미합니다.

상수값 의미
SOCK_STREAM 순서가 보장되는 데이터 스트림
SOCK_DGRAM 데이터그램(Datagram)을 패킷을 전송
SOCK_RAW Raw level의 패킷을 전송
SOCK_SEQPACKET SOCK_STREAM과 비슷하나 패킷 수신 시 항상 전체를 읽어들여야 함

SOCK_STREAM 형태의 타입으로 지정을 하면 소켓은 상태유지형(stateful) 연결 형태가 됩니다. 신뢰성이 있고 순서가 보장되는 스트림(Stream) 형태로 데이터를 처리할 수 있습니다. TCP 프로토콜에 어울리는 소켓 형식입니다.

SOCK_DGRAM 타입은 UDP 프로토콜에 어울리는 소켓 형식으로 연결 상태를 유지할 필요가 없기 때문에 최소한의 리소스만 할당하여, 개별 데이터그램 단위로만 데이터를 주고 받을 수 있게 됩니다. 신뢰성이나 패킷의 순서를 보장할 필요가 없습니다.


Protocol

protocol 파라메터는 소켓이 실제로 사용할 프로토콜의 종류를 명시합니다.

상수값 필요 소켓 타입 의미
IPPROTO_TCP SOCK_STREAM TCP 세그먼트 패킷
IPPROTO_UDP SOCK_DGRAM UDP 데이터그램 패킷
IPPROTO_IP 또는 0 상관없음 주어진 소켓 종류의 디폴트 프로토콜을 사용

protocol을 ‘0’으로 하면 운영체제가 알아서 적절한 프로토콜을 선택해주기 때문에 다음과 같이 사용해도 됩니다.

SOCKET tcpSocket = socket(AF_INET, SOCK_STREAM, 0);

SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, 0);


소켓 종료

소켓을 종료할 때는 다음 코드로 종료합니다.

int closesocket(SOCKET socket);

만약 소켓을 닫기 전에 전송과 수신을 종료하려면

int shutdown(SOCKET socket, int how);

함수를 호출하면 됩니다. how 파라메터는 다음과 같습니다.

상수값 의미
SD_SEND 전송을 중단
SD_RECEIVE 수신을 중단
SD_BOTH 송수신을 모두 중단

키워드 'virtual' 심화 탐구

|

C++에서 자주 쓰이는 virtual 키워드에 대해서 알아보도록 하겠습니다.

먼저 가장 대표적인 특징으로는 오버라이딩이 될 수 있습니다.


오버라이딩

상속시 `virtual` 함수만 정상적으로 오버라이딩(overriding)될 수 있습니다.

다음 예제를 살펴보도록 하겠습니다.

class Parent {

 public:
  void hello() {
    printf("Hello. This is the parent.\n");
  }
};

class Child: public Parent {
 public:
  void hello() {
    printf("Hello. This is the child.\n");
  }
};

여기서 만약 다음 코드를 호출하면 결과는 어떤게 나올까요?

  Child c;
  c.hello();

결과는 Hello. This is the child.가 화면에 출력될 것입니다. 결과만 봐서는 굳이 virtual 키워드를 사용하지 않더라도 정상적으로 오버라이딩된 것처럼 보입니다.

하지만, 만약 다음 코드처럼 부모 클래스의 참조를 이용해서 호출을 하면,

  Child c;
  Parent& ref = c;
  ref.hello();

결과는 Hello. This is the parent.가 나옵니다. 부모 클래스의 hello() 함수가 virtual이 아니기 때문에 이런 결과가 발생하게 됩니다.

즉, 단순하게 사용할 때는 virtual 키워드를 사용하지 않더라도 마치 정상적으로 오버라이딩된 것처럼 보였지만, 실제로는 각 함수들이 정상적으로 오버라이딩이 된 것이 아니라 부모의 함수와 자식의 함수가 각각 따로 존재했을 뿐이라는 것을 알 수 있습니다.


vtable

virtual 키워드를 사용하지 않은 경우에는 부모의 함수와 자식의 함수가 각각 개별적으로 하드코딩되어져서 두 함수 모두 존재하게 됩니다. 이 때 함수의 이름은 컴파일 타임 타입에 맞춰져서 각각 다른 이름으로 저장이 됩니다.

하지만 virtual 키워드를 사용한 경우에는 해당 함수를 vtable(Virtual Table)이라고 부르는 특수한 메모리 영역에서 함수 주소값을 관리하도록 합니다.

즉, 함수가 실행될 때 vtable을 참조하여 실제로 올바르게 오버라이딩된 함수를 찾아서 실행해주기 때문에 virtual 키워드를 사용해야만 ‘정상적으로 오버라이딩 되었다’라고 할 수 있습니다.


virtual 키워드에 대한 논쟁

그래서 많은 프로그래머들은 모든 함수들을 virtual로 선언하는 것을 권하고 있습니다. 그렇다면 애시당초 virtual 키워드를 사용하지 않더라도 디폴트로 virtual처럼 취급하면 되지 않냐는 의문점이 남습니다.

Java 언어는 모든 메소드를 `virtual`로 취급하고 있습니다.

virtual 키워드가 만들어진 배경에는 vtable을 사용하는데 드는 오버헤드때문이었습니다. 함수 주소값을 참조해서 실제 함수를 찾는 과정이 필요하기 때문에 성능 저하가 발생할 수 밖에 없었고, C++ 언어를 디자인하던 사람들은 이러한 이유 때문에 프로그래머에게 선택권을 주는 것이 더 낫다고 판단했습니다. (실제로 virtual 키워드를 사용하기 때문에 발생하는 오버헤드는 무시해도 될만큼 작습니다.)


소멸자의 virtual 필요성

모든 함수를 virtual로 선언하는 것에는 거부감이 있는 프로그래머들도 클래스의 소멸자만큼은 무조건 virtual로 선언해야 한다는 것에는 동의합니다.

만약 소멸자가 virtual이 아닌 경우에는 객체 소멸시 메모리 해제의 일부가 누락될 수 있는 상황이 발생할 수 있습니다.

예를 들면 다음과 같은 상황입니다. 다음 상황에서 Parent 클래스의 소멸자가 virtual이 아닌 경우 문제가 발생합니다.

  Parent* p = new Child();
  delete p;

위와 같은 경우는 ~Parent()는 호출이 되지만, 소멸자가 virtual이 아니면 ~Child()는 호출 되지 않습니다. 즉, Child 클래스의 소멸자에서 이루어지는 메모리 해제 등은 호출이 안되기 때문에 메모리 누수(Memory Leak) 등이 발생할 수 있습니다.

ListView의 가장 아래쪽으로 자동 스크롤 시키기

|

안드로이드 ListView의 가장 아래쪽 아이템으로 자동 스크롤 시키는 코드입니다.


alwaysScroll 옵션 적용

먼저 ListView에 alwaysScroll 옵션을 적용시켜줍니다.

XML 레이아웃내의 ListView attribute에

android:transcriptMode="alwaysScroll"

항목을 추가시키거나, Java 코드에서

listview.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

와 같이 작성하시면 됩니다.


자동 스크롤 코드

ListView의 아이템이 변경되었다는 이벤트가 왔을 때 아래 코드를 수행하시면, ListView의 최하단 칸으로 강제 이동 시켜줄 수 있습니다.

listview.setSelection(adapter.getCount() - 1);

Git 설명서 - (10) 원격 브랜치

|

원격 브랜치

원격 브랜치는 원격 서버에 있는 브랜치를 말합니다. 물론 로컬에도 서버에서 Pull로 가져온 원격 브랜치 정보가 모두 존재합니다.

원격 브랜치는 ‘(remote)/(branch)’ 형태의 이름을 가집니다. 예를 들어 origin/master와 같은 이름입니다.

다음 예제는 ‘git.ourcompany.com’ 이라는 원격 Git 서버가 있으며, 이 서버로부터 clone 하여 로컬에 저장소를 가지는 경우에 대한 예제입니다.

clone을 하게 되면, 로컬에서는 원격 저장소의 별명을 origin이라는 이름을 기본(Default)으로 붙입니다. 즉, 다음 그림과 같은 형태의 소스 트리를 가지게 됩니다.

image


다른 개발자가 원격 저장소의 master 브랜치에 commit 한 경우

이 경우 개발자간 히스토리는 서로 달라지게 되고 다음 그림과 같은 상태가 됩니다.

image

원격 저장소의 내용을 로컬 저장소에 업데이트하려면 git fetch origin 명령을 사용해야 합니다. 이렇게 하면, 현재 로컬 저장소에 없는 서버의 내용을 모두 내려받게 되고 로컬의 origin/master 포인터를 가장 최신 commit으로 이동시켜줍니다.

image

물론, 로컬에서 기존에 작업하던 내용이 있기 때문에 소스 분기는 이루어지게 됩니다. fetch로 내려받았기 때문에 소스 정합은 수동으로 해주어야 합니다.

fetch로 내려받은 브랜치의 내용을 정합하려면 git merge origin/(브랜치 이름)으로 명령을 내려주면 됩니다.


작업한 내용을 원격 저장소에 Push

로컬 저장소의 내용을 원격 저장소에 업로드할 때는 push 명령어를 이용합니다. git push (remote) (branch)와 같은 형태로 사용할 수 있습니다.

$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new branch]      serverfix -> serverfix

위 명령은 로컬의 ‘serverfix’라는 브랜치를 origin이라는 이름을 가진 원격 저장소에 ‘serverfix’라는 이름의 브랜치로 Push 하는 명령입니다.