20120117 :: OSX의 메모리 상태 보는 법

맥은 얼마 쓰지도 않아서 메모리를 다쓴다?

윈도에서 맥으로 넘어와서 버리기 힘든 나쁜 습관 중 하나는 메모리의 상태를 걱정하는 일이다. 무슨 소린고 하니 마치 메모리 먹는 괴물처럼 맥은 시스템에 달린 램을 거의 대부분 써버린다. 시스템의 메모리는 활성상태보기(activity)라는 앱을 실행해서 확인할 수 있는데, 여기서 보면 “여유 메모리”에 진짜 여유가 있는 경우가 좀처럼 없기가 십상이다.

그래서 메모리의 여유 공간을 확보해주는 앱들도 유/무료로 많이 팔리고 있는 중인데, 꼭 이런 앱을 돌릴 필요가 있을까 싶은 생각이 든다. 물론 윈도에서의 메모리 확보 프로그램과는 달리, 이 앱들은 시스템에 해악을 끼친다고는 생각되진 않는다. 아무튼 이러한 일종의 ‘오해’는 맥의 메모리 관리 방법에 대한 무지에서 비롯된다고 생각되는데, 이런 메모리 정보를 어떻게 이해해야 할까?

먼저 활성상태보기 앱을 실행해보면 시스템 메모리가 현재 어떻게 사용되고 있는지를 보여준다. 각각의 내용은 다음과 같다.

  • 여유공간 : 말 그래도 여유공간이다. 시스템 메모리 중 사용되지 않고 있으며, 언제든 사용할 수 있는 공간이다.
  • 와이어드 : Wired 상태의 메모리는 특히 앱의 실행 코드가 올라가 있는 공간이다. 따라서 하드디스크 위의 가상메모리 공간으로 스와핑이 불가능하다.
  • 활성 : 활성 메모리 영역은 앱들이 정보를 올려두고 사용하는 공간이다. 메모리가 부족한 상황이 된다면 시스템은 이 영역의 데이터 중 당장 사용되지 않는 내용들을 가상 메모리로 옮겨주게 된다.
  • 비활성 : 아마 시스템을 사용하다보면 가장 많은 분량을 차지하게 되는 메모리 공간이다. 비활성 메모리는 최근에 종료한 앱이 사용했던 공간이다. 이 부분을 바로 해제해서 여유 공간으로 확보할 수 있지만, 실제로는 일반적인 사용자는 동일한 앱을 빈번히 종료/재시작하므로, 비활성 메모리에 이전 앱이 사용하던 정보를 남겨두면, 그 앱을 재실행할 때 빠르게 구동할 수 있다는 장점이 있다. 물론 메모리가 부족한 상황에서는 이 공간을 free 하여 필요한 메모리를 즉시 확보할 수 있다. 현재 시중에 나도는 메모리 확보용 앱은 이 비활성 메모리 영역을 해제해주는 것외에 하는 내용은 없다.

따라서 시스템에서 사용 가능한 메모리 공간은 “여유공간 + 비활성”의 크기 만큼이 되겠다. 그리고 이 두 영역을 합해도 필요한 메모리보다 부족하다면 시스템은 활성 메모리 중의 일부를 디스크로 옮겨서 필요한 메모리 영역을 확보하게 된다. 이 때 사용하는 가상 메모리의 크기가 VM이며, VM에 대해 얼마나 액세스했는가 하는 정보가 페이지출력/입력에 표시된다.

이 페이지 입력/출력의 양이 많으면 많을수록 더 많은 양의 메모리가 필요해서 그 만큼 페이징을 했다는 이야기이며, 하드디스크는 램에 비해 훨씬 더 속도가 느리므로 시스템의 전체적인 체감 성능이 떨어진 것처럼 느껴질 수 있다. 하지만 대부분의 경우에는 그 정도까지로 메모리를 사용하는 케이스는 좀 드물다.

맥이 메모리를 99%, 100%까지 쓰는 것은 메모리를 잘 못 관리하기 때문이 아니라, 그만큼 메모리를 잘 활용하고 있는 (그것도 극대화하여 사용) 증거이므로 굳이 메모리 관리 앱을 사용하여 여유 공간을 수동을 매번 잡아줄 필요는 없다.

덧 : 이는 몇 번 지적한 내용인데, 윈도용 메모리 확보 프로그램은 맥으로 치면 현재의 활성상태 메모리 영역에 있는 데이터를 디스크의 스왑공간으로 강제로 옮기는 기능을 수행한다. 따라서 일시적으로 여유공간이 증가하는 것처럼 보이지만, 필요이상의 하드디스크 액세스를 발생시키므로 오히려 앱을 사용하는 속도는 느려진다. 윈도에서의 메모리 관리도 시스템이 관리하는 것이 가장 낫다고 생각된다. 단, 윈도는 메모리 여유 공간이 제법 남아 있는 상황에서도 가상메모리로 페이징하는 일이 빈번하여 되려 같은 체감성능을 제공하는데 있어 더 많은 램 크기를 요구하는 경우가 많다.

참고자료 : http://support.apple.com/kb/HT1342?viewlocale=ko_KR&locale=ko_KR

20120116 :: 그래픽 컨텍스트를 사용하여 Finger Drawing 구현하기

Quartz 2D는 코어 그래픽의 프레임워크의 일부이며, iOS 및 OSX의 그래픽 엔진을 다루는 API 프레임워크로, 상당히 괜찮은 성능과 품질로 그래픽 처리를 할 수 있는 그래픽 엔진이다. 이전 버전의 iOS에서는 Quartz 프레임워크를 링크해야 사용할 수 있었는데, iOS5에서부터는 별다른 프레임워크 추가 없이 사용이 가능해졌다.

Mac 및 iOS에서의 그래픽은 장치독립적 그래픽이라는 이상을 추구하는데, 어떤 화면이나 기기 혹은 화면/출력물 등의 차이에 구애받지 않는 그래픽 구현을 목표로 한다. 쉬운 예를 들어 PC모니터에서는 1인치에 72개 이상의 픽셀을 찍는 것이 어렵지만 (물론 요즘은 레티나 같은 디스플레이가 나와서 거의 프린터 수준의 해상력을 갖는 것이 가능하고, 아이폰3GS의 경우에도 100ppi 이상의 해상력을 가지고 있기는 하다.) 프린터로 출력하는 경우에는 1인치에 300개의 픽셀을 찍는 것도 가능하다.

기기 혹은 매체마다 화면 해상도가 다르다보니, 보통 PC에서 보던 이미지를 종이에 출력하면 해상도 차이 때문에 아주 작게 줄어들어서 표현되거나 하는 경우가 있다. 애플은 화면에 표시되는 내용과 출력물의 내용의 괴리를 최대한 줄이고, 장치에 구애 받지 않고 최대한 비슷한 그래픽을 구현하기 위해 시스템 그래픽 엔진 자체를 벡터로 구현하는 방식을 추구하였고, 이에 대한 연구의 산물이 바로 쿼츠 엔진이다.OSX의 화면 효과가 자연스럽고, 멋진 이유 중 하나는 다름 아닌 이 쿼츠 엔진의 덕이라 볼 수 있다.

이 쿼츠는 iOS에서도 적용되어 있다. 따라서 이미지를 화면에 올릴 때 크기를 정수가 아닌 실수(float)형으로 크기를 지정해 주는데, 이는 픽셀 단위가 아니라 포인트(pt)단위로 그림의 크기를 지정하는 것이다. (pt 단위는 픽셀과 달리 cm, inch와 같은 실제 물리적인 길이 단위이다. 1포인트는 1/72 인치의 실제 길이 단위이다.)

그래픽 컨텍스트

쿼츠를 사용하여 그래픽을 구현하기 위해서는 그래픽 컨텍스트에 대한 이해가 필요하다. 그래픽 컨텍스트는 개념상, 가상의 캔버스라 생각하면 된다. 우리는 그래픽 컨텍스트라는 이 가상의 캔버스에 그림을 그리게 되고, 쿼츠 엔진은 이 가상의 캔버스에 그려진 그림을 실제 장치의 디스플레이에 투영하여 그래픽을 출력해주는 것이다. 그래픽 컨텍스트는 장치 독립적인 성격을 가지므로, 하나의 그래픽 컨텍스트는 다른 장치를 위한 그래픽으로 쉽게 전환이 가능하다. 따라서 그래픽 컨텍스트에 적용된 그래픽은 아이폰 및 아이패드용 화면 출력 뿐 아니라, 인쇄나 PDF를 만들기도 쉽게 지원된다.

뷰에 그림이 그려지는 것 또한 실제로는 이 그래픽 컨텍스트에 그림이 그려지는 것이라 생각하면 된다. 또한 컨텍스트에는 연관된 레이어(CGLayer)를 추가로 생성할 수 있고, 이 레이어에 그림을 그려 반복적으로 그려지는 이미지를 리소스를 절약하여 만들 수 있다. 재밌는 것은 레이어에도 그 자체의 컨텍스트가 따로 있다는 것이다. 레이어를 사용하여 오프스크린 그래픽을 구현할 때에는 어느 컨텍스트에 그림을 그리는지를 명확하게 인식하고 있는 것이 중요하다.

이번 글에서는 그래픽 컨텍스트를 사용하여 핑거 드로잉을 구현하는 부분에 대해 알아보고자 한다. 다소 이론적인 내용이 많은데, 자세한 내용은 개발자 문서를 참고하면 좋겠다.

핑거드로잉

손가락 터치에 대해 터치되는 부분을 따라 선이 그려지도록 하는 기능만을 우선 구현하기로 한다. 핑거드로잉을 지원하는 뷰 클래스 (UIView)를 하나 새로 만들 것이며, 이 뷰 클래스에서 핑거드로잉을 구현하도록 한다.

프로젝트 시작

새 프로젝트를 하나 만든다. 어차피 UIView 클래스를 새로 하나 만드는 것이 사실 구현의 전부이므로, single view 앱으로 시작한다. 프로젝트를 생성하였으면, 새 파일을 추가한다. Objective-C 파일을 추가하여 UIView를 선택한다. 이름은 CanvasView 정도가 좋을 것 같다.

앱 실행시에 해당 뷰가 전면에 표시되도록 ViewController 파일을 다음과 같이 수정해 준다.

ViewController.m

1
2
3
4
5
6
#import "CanvasView"
...
-(void)viewDidLoad {
CanvasView *canvas = [[CanvasView alloc] initWithFrame:self.view.frame];
[self.view addSubView:canvas];
}

인터페이스

새로 뷰를 만들 때 다음과 같은 형태를 구상할 수 있다.

  1. 터치할 때 레이어에 그림을 그린다.
  2. 레이어에 그려진 그림을 다시 현재 뷰의 컨텍스트에 그린다. (실제 그림이 그려진다.)
1
2
3
4
5
6
@interface CanvasView : UIView
{
CGContextRef layerContext;
CGLayerRef drawingLayer;
}
@end

헤더에서는 컨텍스트 1개와 레이어 1개를 선언한다. 터치가 움직일 때 그림을 그릴 레이어와 그 레이어의 그래픽 컨텍스트를 참조하기 위한 CGContextRef 구조체가 필요하다. 레이어를 생성하기 위해서는 역시나 컨텍스트가 필요한데, 이는 현재 뷰의 컨텍스트를 그대로 사용하기로 한다. 뷰를 코드 상에서 가져올 수도 (현재 예제) 있고 IB에서 끌어다 놓을 수도 있기 때문에 초기화 메서드는 2개를 작성한다.

구현부 – 초기화

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
30
31
32
33
34
35
36
#import "CanvasView.h"
@implementation CanvasView
-(id)initWithFrame:(CGRect)frame
{ // 이 메서드는 코드상에서 이 뷰가 초기화될 때 호출됨
self = [super initWithFrame:frame];
CGContextRef = UIGraphicsGetCurrentContext();
drawingLayer = CGLayerCreateWithContext(context, self.bounds.size,NULL);
layerContext = CGLayerGetContext(drawingLayer);
// 그래픽 컨텍스트에 그림을 그릴 때의 그래픽 속성
CGContextSetStrokeColorWithColor(layerContext, [[UIColor redcolor] CGColor]);
CGContextSetLineWidth(layerContext,1.3f);
CGContextRelease(context);
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
// 이 메서드는 IB에서 뷰가 추가되었을 때 초기화 시 호출됨
self = [super initWithCoder:aDecoder];
CGContextRef = UIGraphicsGetCurrentContext();
drawingLayer = CGLayerCreateWithContext(context, self.bounds.size,NULL);
layerContext = CGLayerGetContext(drawingLayer);
// 그래픽 컨텍스트에 그림을 그릴 때의 그래픽 속성
CGContextSetStrokeColorWithColor(layerContext, [[UIColor redcolor] CGColor]);
CGContextSetLineWidth(layerContext,1.3f);
CGContextRelease(context);
return self;
}
@end

터치가 발생하여 손가락이 움직이는 동안에는 손가락의 좌표를 추적하여, 해당 경로를 따라 선을 그린다. 이는 손가락이 움직일 때 마다 호출되는 메서드에 넣어 그려주면 된다.

구현부 – 터치 액션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint lastTouch, currentTouch;
UITouch *touch = [touches anyObject];
lastTouch = [touch previousLocationInView:self];
currentTouch = [touch locationInView:self];
CGContextBeginPath(layerContext);
CGContextMoveToPoint(layerContext,lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(LayerContext, currentTouch.x, currentTouch.y);
CGContextStrokePath(layerContext);
[self setNeedsDisplay];
}

터치 이벤트가 발생할 때 마다 레이어에 그림을 그렸다. 마지막 setNeedsDisplay는 뷰의 변경사항이 발생했을 때 뷰를 다시 그리도록 하기위해 보내는 메시지이다. 이 메서드는 따로 구현하지 않으며, 대신 drawRect: 메서드를 구현해야한다.

구현부 – 뷰 업데이트

1
2
3
4
5
-(void)drawRect:(CGRect)rect
{
CGContext currentContext = UIGraphicsGetCurrentContext();
CGContextDrawLayerInRext(currentContext, self.bounds, drawingLayer);
}

뷰를 업데이트하는 내용은 위와 같이 그려진 레이어를 현재 컨텍스트에 그리는 것으로 충분하다.

이제 앱을 빌드하고 실행하면, 까만 화면을 바탕으로 터치로 그림을 그리면, 빨간 선이 그려지는 것을 확인할 수 있게 된다.

다만 문제는 레티나 디스플레이를 장착한 아이폰4 및 아이팟터치 4 이상의 기기에서는 그림을 그리면 그릴수록 점점 느려진다. 특히 아이팟터치는 메모리 양이 작아서 그런지 이 증상이 심해진다. 이 증상에 대해서는 (아직 해결을 못한 것도 있고) 다음 기회에 따로 다루도록 하겠다.

오늘의 소스코드 : http://www.box.com/s/0dy3v15om4mgeee8a4i9

20120112 :: [OSX] 메일 첨부파일 때문에 하드디스크 용량이 걱정될 때

아이클라우드가 약 5GB 가량의 공간을 제공해주고 있고 그래서 iOS 기기의 무선 백업을 사용하지 않는다면 거의 이 만큼의 공간을 메일 용량으로 활용할 수 있는데, Gmail의 경우에도 점점 용량이 늘어나면서 상당히 많은 양의 공간을 제공하고 있다. 경우에 따라서는 Gmail 계정을 여러 개 사용하는 경우도 있을 수 있어서 메일함을 동기화하는 경우에는 적지 않은 디스크 공간이 메일에 의해 점유된다.

HDD를 사용하는 경우에는 별로 문제가 되지 않지만, 맥북에어와 같이 디스크 용량이 그리 넉넉치 못한 환경에서는 이런 메일 사용이 꽤나 부담이 되는데, 하지만 메일앱에 다운로드 받아둔 메일은 Spotlight에서 바로 검색이 가능해서 이 편리함을 뿌리치기가 힘들다.

그런 경우에는 메일 메시지는 하드디스크에 보관해두되, 비교적 용량이 큰 첨부파일들은 저장하지 않도록 하는 방법을 사용하면 메일 검색도 쉽게 할 수 있고, 디스크 용량도 절약할 수 있다.

메일 앱을 실행해서 환경설정 패널을 열어 계정 탭을 본다. 계정을 선택하면 오른쪽에 계정의 설정을 할 수 있는데, 이 곳에 “고급” 탭을 클릭해 본다.

위 그림에서 볼 수 있듯이 “오프라인 상태에서 보기 위해 메시지 복사본 유지”를 선택하는 옵션이 있는데, 기본값이 “모든 메시지와 첨부 파일”이다. 이를 “모든 메시지, 첨부파일은 생략함”으로 바꾸면 메일 메시지는 저장하지만 첨부파일은 메시지를 열어 별도로 다운로드 받도록 하기 때문에 필요한 파일만 그 때 그 때 찾아서 저장하면 된다.

20120111 :: [OSX] Mail 앱 초기화하기

잘쓰고 있는 메일앱이었는데… 예전에 사용하다가 계정을 없애버려서 삭제한 메일 계정이 보내는 메일 계정에서 계속 보여지는 문제가 있었다. 사실 안쓰면 그만인데, 그래도 찜찜해서 이리 저리 살펴보아도 이런 증상에 대한 이야기를 찾을 수가 없어서 결국 메일 앱을 초기화하기로 결정. 어차피 iCloud 메일이나 Gmail 만 쓰고 있고 보낸 메시지의 경우에도 모두 서버에 저장하고 있어 쿨하게 초기화 결정.

초기화라는 말은 거창해도 OSX에서 대부분의 기본앱들은 삭제와 재설치라는 과정이 필요없고 그저 설정들만 싹 지워버리면 된다. 다음은 메일앱을 초기화하는 과정이다.

1. 시스템 환경설정 > 메일,연락처에서 우선 등록되어 있는 계정을 모두 제거한다. 아이클라우드 메일 계정이 남아있는 경우에 뭔가 꼬이지는 않을까하는 생각에 모두 제거했다.

2. 메일 앱을 완전히 종료한다.

3. 파인더를 열어 라이브러리 폴더에서 다음 내용들을 삭제한다. 참고로, 라이언에서부터는 라이브러리 폴더가 숨겨져있는데, 설정을 변경할 필요 없이 파인더에서 ⇧⌘G를 눌러 ~/Library 라고 입력하면 라이브러리 폴더로 바로 이동할 수 있다.

  • ~/Library/Preferences/com.apple.Mail.plist
  • ~/Library/Application Support/Mail
  • ~/Library/Mail

4. 아이클라우드 설정 복원을 위해 다시 시스템 환경 설정 > 메일,연락처에서 아이클라우드 계정을 다시 설정해주고, 사용하려는 Gmail 계정도 다시 입력해준다.

5. 메일 앱을 실행해주면 끝

 

20120109 :: [팁] 맥의 모니터만 잠자기

맥북 계열의 경우에는 화면 밝기를 최소로하면 거의 화면이 꺼진 것에 가까울만큼 어둡게 되지만 (이것도 꺼진 것은 아니고 아주 어둡게 켜지는 것임) 아이맥의 경우에는 화면 밝기를 최소로하더라도 어느 정도 밝기가 유지된다.

전력 소모를 줄이기 위해서 잠자기로 들어가게 해 두면, 무선 네트워크 연결이 해제되거나 돌아가던 작업이 중단되는 문제가 있어서 모니터만 재우는 기능이 꽤나 쏠쏠한데, 의외로 많은 커뮤니티에서는 이것을 터미널에서 셸 스크립트를 돌려서 구현하거나 하는 분들도 있더라.

그냥 Control + Shift + Eject를 누르면 화면만 잠자기 모드로 들어간다.