델파이 XE4 버전에서는 iOS 및 ARM
컴파일러 지원을 위해,
기존의 델파이와는 다른 새로운 델파이
컴파일러를 도입했습니다.
기존 델파이 컴파일러와의 호환성을 위해
대부분의 문법들은 하위호환되지만
델파이로 모바일 개발을 하기 위해서는
알아두어야 할 주의해야 할 부분들이 상당히 많습니다.
현재 엠바카데로에서 델파이 프로덕트 매니저를
맡고 있는 마르코 칸투는
문서 "The Delphi Language for Mobile Development"에서
이러한 주의점들을 간략히 설명하고 있습니다.
모바일 개발을 생각하는 델파이 개발자들에게는
아주 중요한 문서이므로,
총 4회로 나누어 이 문서를 번역해서 올립니다.
이번 글은 그중 마지막, 네번째입니다.
기타 언어 변경들
문자열 타입 변경과 객체 메모리 관리 외에도,
새로운 델파이 ARM 컴파일러에는
여러분이 간단히 채용할 수 있는,
이미 적용되었거나 곧 적용될 변경 사항들이 있습니다.
머지 않아
with 문은 델파이 언어에서 deprecated 되거나
제거될 것입니다.
여러분은 with를 지금부터
코드에서 제거해나갈 수 있겠고,
대부분의 델파이 개발자들은
이것이 좋은 생각이라고 동의할 것입니다.
이 키워드의 숨겨진 함정들 때문이죠.
포인터 사용을 줄이거나 없애십시오.
우리는 자동 메모리 관리로 가고 있기 때문에
직접적인 포인터 사용을 피할 것을 권합니다.
TList (내부적으로 포인터에 기반) 대신
제네릭 컨테이너 클래스를 사용하는 것이
델파이 RTL 라이브러리가 진행중인 마이그레이션의
좋은 예이며, 델파이 개발자들도 자신의 코드에서
비슷한 변환 작업을 할 것을 추천합니다.
어셈블리 코드를 제거하십시오.
어셈블리 코드는 새로운 컴파일러로
이식이 불가능하며 ARM CPU에 적용되지도 않습니다.
volatile 어트리뷰트는 다른 쓰레드에 의해 변경될
필드를 표시하기 위해 사용되므로,
코드 생성 과정이 별도의 쓰레드에서 공유되지 않는
레지스터나 다른 임시 메모리 주소의
값 복사를 최적화하지 않습니다.
RTL 관련 고려사항들
지금까지 LLVM에 대한 소개와
컴파일러의 변경 사항들에 대해 설명했고,
이 문서의 마지막 파트는
모바일 플랫폼을 지원하기 위한
런타임 라이브러리(RTL)와 그 변경 사항에 초점을 둡니다.
이 마지막 부분은 앞부분들에 비해서
광범위하거나 자세하지는 않을 것이며,
런타임 라이브러리의 측면에서
모바일 플랫폼을 위해
마이그레이션을 하거나
새 애플리케이션을 작성할 때
여러분이 마주칠 수 있는 이슈들을 간단히 훑어볼 것입니다
(유저 인터페이스나 데이터베이스, 네이티브 디바이스 센서 등에 대해서 다루지는 않습니다).
이 짧은 섹션에서는,
다른 운영체제를 지원하기 위한
일반적인 제안들, 파일 액세스와 관련된 정보,
패키지와 라이브러리에 대한 지원 등을 거론합니다.
RTL과 크로스플랫폼
몇가지 권고안들을 설명해보겠습니다.
고수준 컴포넌트나 클래스가 있다면
최대한 직접 API 호출을 피하십시오.
이렇게 함으로써 코드를 다른 플랫폼으로
포팅하기 쉽게 됩니다(당장 혹은 언젠가는).
크로스플랫폼 유닛들을 우선적으로 사용하십시오.
예를 들어 파일 관리를 위해서는
옛 스타일의 파일 관리 파스칼 루틴보다는
IOUtils 유닛의 레코드들과
그 클래스 메소드들을 사용할 것을 추천합니다.
바로 아래에서 IOUtils 유닛 사용의
몇가지 예를 볼 수 있습니다.
여러분의 코드를 Mac이나
모바일로 포팅 가능하게 하려면
윈도우스럽고 윈도우에 한정된 API를 피하십시오.
좋은 예는 MSXML을 사용하지 않고
델파이 기반의 ADOM XML 처리 엔진을
사용하는 것입니다.
이 엔진은 모바일에서도 동작합니다.
다른 예는 몇몇 소켓 및 웹 라이브러리들 대신
Indy를 우선 사용하는 것입니다.
말할 필요도 없이, COM, ActiveX, ADO, BDE,
그리고 다른 윈도우에 한정된 델파이
기술들은 모바일에서 동작하지 않습니다
(물론 Mac에서도 안됩니다)
Generics.Collections 유닛에 선언된
제네릭 컨테이너 클래스들을 사용하십시오.
옛 스타일의 Contnrs 유닛은 모바일 플랫폼에는
존재하지 않습니다.
이것은 Contnrs 유닛이 포인터 리스트
(제네릭이 아닌 TList 클래스)에 기반하여
ARC 메모리 관리 모델에서 제대로
동작하지 않기 때문입니다.
TStringList의 각 요소에 객체를 연결하여
사용하는 것은 지원되긴
하지만, 그보다는 제네릭 딕셔너리를 사용할 것을
강력하게 권합니다.
문자열을 키로 하여 TDictionary와 같이
사용할 수 있습니다.
딕셔너리를 사용하는 것이 더 깔끔할 뿐만 아니라,
딕셔너리는 해시 테이블을 사용하므로
대부분의 경우에 훨씬 빠르기도 합니다.
아래에서 더 자세히 설명할 것입니다.
포인터 기반 구조와 메소드의 사용을 피하십시오.
네이티브 API 호출에 필요한 경우가
아니라면 말입니다.
위의 목록은 분명히 훨씬 길어질 것입니다.
당장은 이 정도만 나열했으며,
다음으로 파일 액세스와 라이브러리에 대해
따로 자세히 살펴봅시다.
파일 액세스
델파이의 몇 버전 전부터,
전통적인 파스칼의 파일 액세스 루틴들
(한 폴더 내에서 검색하기 위한 FindFirst와 FindNext처럼)은
고차원 레코드들로 대체되었으며,
IOUtils (Input/Output utilities) 유닛에 모여있습니다.
IOUtils를 사용하는 것은 여러분의 코드를
크로스플랫폼으로 만들기 위해 추천되는 방식입니다.
이 유닛에는 대부분 클래스 메소드로 정의된
3개의 레코드가 있는데,
각각 .NET 클래스들과 대응됩니다.
TDirectory => System.IO.Directory
TPath => System.IO.Path
TFile => System.IO.File
TDirectory는 폴더를 뒤져 파일과
서브폴더를 찾기 위한 것이라는 것을
분명하게 알 수 있지만,
TPath와 TFile의 차이점은
감이 잘 오지 않을 수 있겠습니다.
TPath는 파일 이름과 디렉토리 이름을 다루기 위해
사용되며, 드라이브, 패스나 확장자 등을
제외한 파일 이름을 뽑아내거나
UNC 경로를 다루는 메소드들을 가지고 있습니다.
반면 TFile 레코드는
파일의 시간과 속성, 파일 조작, 파일 쓰기나
복사 등을 할 수 있게 해줍니다.
예를 들어, 여러분이 모바일 플랫폼에서
여러분의 애플리케이션과 함께 특정 파일을 함께
배포한다면, 윈도우에서 사용자의 문서 폴더를
액세스하는 것과 같은 방법으로 애플리케이션의
“documents” 폴더를 액세스할 수 있습니다.
var
myfilename: string;
begin
myfilename := TPath.GetHomePath + PathDelim
+ 'Documents' + PathDelim + 'thefile.txt';
if TFile.Exists(myfilename) then
...
폴더와 파일을 찾는 기능도 지원됩니다.
다음의 코드는 지정된 초기 폴더(BaseFolder)
아래의 폴더들을 읽어들인 후 한 단계만
아래로 이동하여 서브폴더의 파일들을 읽습니다.
var
pathList, filesList: TStringDynArray;
strPath, strFile: string;
begin
if TDirectory.Exists(BaseFolder) then
begin
ListBox1.Items.Clear;
ListBox1.Items.Add('Searching in ' + BaseFolder);
pathList := TDirectory.GetDirectories(BaseFolder,
TSearchOption.soTopDirectoryOnly, nil);
for strPath in pathList do
begin
ListBox1.Items.Add(strPath);
filesList := TDirectory.GetFiles(strPath, '*');
for strFile in filesList do
ListBox1.Items.Add('- ' + strFile);
end;
ListBox1.Items.Add('Searching done in ' + BaseFolder);
end
else
ListBox1.Items.Add('No folder in ' + BaseFolder);
end;
스트링리스트를 제네릭 딕셔너리로 바꾸자
오랫동안 저 자신을 포함한 많은 델파이 개발자들은
TStringList 클래스를 지나치게 많이 사용해왔습니다.
단순한 문자열의 리스트로 사용하는 목적 외에도
이름/값 쌍들의 리스트로도 사용하고,
문자열과 연관된 객체들을 리스트로 저장하고
그 객체들을 검색하기 위한 목적으로도
사용해왔습니다.
델파이는 제네릭을 완벽하게 지원하므로,
맥가이버 칼처럼 사용해온
TStringList 대신 전용의 맞춰진 컨테이너 클래스로
바꾸는 것이 낫습니다.
예를 들면, 문자열 키와 객체 값을 가진
제네릭 딕셔너리가 TStringList 보다 더 깔끔하고
안전하며(타입 캐스트가 덜 일어나기 때문),
또 더 빠르게 실행됩니다(해시 테이블을 이용).
이들 사이의 차이점을 알아보기 위해
다음의 예제를 살펴봅시다.
private
sList: TStringList;
sDict: TDictionary< string,TMyObject>;
이 리스트들은 아래와 같은 코드로
임의의 같은 값이 채워집니다.
sList.AddObject(aName, anObject);
sDict.Add(aName, anObject);
리스트의 각 요소를 가져와
각각에 대해 이름으로 검색을 하는
두 메소드를 이용하여 속도 테스트를 진행했습니다.
두 메소드 모두 문자열의 리스트를 검색하지만
첫 번째는 스트링리스트에서 객체를 찾고,
반면 두 번째는 딕셔너리를 사용합니다.
첫 번째 경우 해당 타입으로 되돌리기 위해
as 타입 캐스트가 필요한 반면
딕셔너리에서는 이미 해당 클래스 타입에 연결되어
있다는 점에 주목하십시오.
아래는 두 메소드의 메인 루프입니다.
theTotal := 0;
for I := 0 to sList.Count ? 1 do
begin
aName := sList[I];
// 이름을 검색
anIndex := sList.IndexOf(aName);
// 객체를 얻어냄
anObject := sList.Objects[anIndex] as TMyObject;
Inc(theTotal, anObject.Value);
end;
theTotal := 0;
for I := 0 to sList.Count - 1 do
begin
aName := sList[I];
// 객체를 얻어냄
anObject := sDict.Items[aName];
Inc(theTotal, anObject.Value);
end;
딕셔너리의 해시된 키에 비해
정렬된 스트링리스트(바이너리 서치)는
얼마나 많은 시간이 걸릴까요?
놀랍지도 않지만 딕셔너리가 더 빠릅니다.
윈도우 플랫폼에서 테스트한 결과를
밀리초 단위로 보여드립니다
(모든 플랫폼에서 비슷한 결과가 나옵니다).
StringList: 2839
Dictionary: 686
입력된 값들이 동일하므로 결과도 동일하지만,
소요된 시간은 상당히 다르죠
딕셔너리는 스트링리스트에 비해
약 4분의 1의 시간이 걸렸습니다
(요소 수 백만 개로 테스트한 결과입니다)
물론, 이 예제와 이 짧은 섹션은
제네릭 딕셔너리와 컴파일러 수준에서
제네릭이 도입된 후 델파이 RTL에
추가된 최신 데이터 구조들의 강력함을
보여주는 데 있어서 빙산의 일각에 불과합니다.
이런 새로운 구조들은 모든 플랫폼에서 좋은
선택이지만,
메모리 관리 모델의 변경 때문에 iOS에서는
더욱 더 적절합니다.
라이브러리 및 패키지
델파이의 강력한 기능들 중 하나는
더 모듈화된 방식으로 애플리케이션을 배포하기 위해
런타임 패키지를 이용하는 것입니다.
패키지는 특수한 목적의 동적 링크 라이브러리로,
윈도우에서는 DLL이며
Apple 플랫폼들에서는 dylib입니다.
윈도우와 OS X에서 런타임 패키지를 common 폴더에
배포하여 여러 애플리케이션들에 공유하도록 하거나
혹은 서로 다른 프로그램들 사이의 잠재적인
충돌을 피하기 위해 특정 애플리케이션
폴더에 배포할 수 있습니다.
iOS 플랫폼에서는 두 가지 모두 허용되지 않습니다.
물리적인 iOS 디바이스에 공유 라이브러리를
배포할 수 없으며
(iOS 시뮬레이터에서는 사용할 수 있습니다만),
이것은 오직 Apple만이 운영체제 레벨에서 할 수 있는
작업이기 때문입니다.
동적 라이브러리를 애플리케이션에
추가할 수도 없는데
이것은 실행 파일만이
메인 프로그램이 될 수 있기 때문입니다.
이 제한은 런타임 패키지에 해당하는 문제가 아니라
더 일반적인 문제입니다.
예를 들어 midas.dll이나 dbExpress 드라이버 같은
표준 델파이 라이브러리들은
애플리케이션 실행 파일에 정적으로 링크됩니다.
InterBase 클라이언트 라이브러리도 마찬가지입니다.
사실, 컴파일러는 정적 라이브러리에 대한
참조를 인식하고 최종 실행 파일에 링크하는 방법을
가지고 있습니다
(기술적인 이슈이므로
이 문서에서 꺼내고 싶지는 않군요).
결론
여러분이 이 문서에서 살펴본 것처럼,
LLVM 아키텍처에 기반한 ARM용
델파이 컴파일러가 발표됨에 따라,
델파이 언어는 중대한 이행을 진행하고 있습니다.
하위 호환성을 유지하려는 노력을 계속하고 있지만,
우리는 개발자들이 전진하고 있는
새로운 기능들을 완전히 받아들이기를
기대하고 있습니다.
이 문서에서 설명한 언어 변경 사항들과
특히 ARC 지원은 델파이가 앞으로
더 나아갈 수 있게 해줄 것입니다.
이들 변경들의 일부는 새 플랫폼에 의한 것이며,
다른 일부는 델파이 언어의 거친 부분을 다듬고
델파이에 새롭고 사용자들에게
편리한 기능들을 추가하기 위한 것입니다.
'Delphi > 문법' 카테고리의 다른 글
델파이 shl 과 shr 설명 (0) | 2021.06.18 |
---|---|
제네릭 - 델파이 TDictionary 사용 (0) | 2021.06.17 |
모바일 개발을 위한 델파이 언어 -3- (0) | 2021.06.15 |
모바일 개발을 위한 델파이 언어 -2- (0) | 2021.06.14 |
모바일 개발을 위한 델파이 언어 (0) | 2021.06.11 |
댓글