tag:blogger.com,1999:blog-30197003709828923892024-03-09T02:08:30.055+03:00Werewolf UnLtd.Блог посвящен процессам разработки и оптимизации программного обеспечения. Delphi, Oracle, MS SQL etc.AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.comBlogger14125tag:blogger.com,1999:blog-3019700370982892389.post-3058408324895960762014-11-07T11:17:00.000+03:002014-11-07T11:17:47.834+03:00Delphi FireDAC Array DML (BULK INSERT) MySQL, PostgreSQL и возможно другие<div dir="ltr" style="text-align: left;" trbidi="on">
На заметку: при использовании запроса INSERT INTO с массивом параметров нужно обязательно ставить <b>пробел перед VALUES</b> иначе запрос будет выполняться для каждой записи массива отдельно.<br />
Пример<br />
INSERT INTO TABLE1(FIELD1, FIELD2)[Пробел тут]VALUES(:p1, :p2)<br />
Проблема в разборе запроса и определении места ключевого слова VALUES.
</div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-59256408873823935912014-05-30T16:59:00.000+04:002014-05-30T16:59:50.544+04:00Delphi быстро читаем stdin<div dir="ltr" style="text-align: left;" trbidi="on">
Для консольных приложений бывает необходимо быстро считать входные данные.<br />
Сразу к коду:<br/>
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[16]">const
rbsize = 65355;
var
d: longint;
rb: array [0 .. 85 * rbsize] of char;
procedure ReadInput;
var
PInput: ^TTextRec;
c: byte;
begin
d := 0;
PInput := @Input;
PInput^.BufSize := 0;
while PInput^.BufEnd = PInput^.BufSize do
begin
SetTextBuf(Input, rb[d], rbsize);
Read(c);
Inc(d, PInput^.BufEnd);
PInput^.BufPos := PInput^.BufEnd;
end;
end;</pre><br/>
После выполнения процедуры ReadInput массив rb[0..d] будет содержать данные из файловой переменной Input(stdin).
Размер массива rb статичен и может быть изменен в зависимости от задачи.
</div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-33372639369944746022013-08-27T12:35:00.000+04:002013-08-27T12:35:29.888+04:00Определяем, что запущенный процесс написан на Delphi<div dir="ltr" style="text-align: left;" trbidi="on">
Если программа написана на Delphi и использует модуль Controls, то при её запуске будет создан глобальный атом вида<br />
'Delphi[восьмизначный идентификатор процесса (PID)]'.<br />
Таким образом чтобы проверить написан ли запущенный процесс с использованием Delphi (модуля Controls) достаточно проверить наличие атома с соответствующим идентификатором процесса:<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[3]"> ShowMessage(
BoolToStr(
GlobalFindAtom(
PChar(Format('Delphi%.8X', [StrToInt(Edit1.Text)]))
) <> 0
, True)
);</pre>
<br />
Естественно можно написать программу и без использования Controls. В таком случае можно проверить поле TimeDateStamp PE заголовка программы - для программ написанных на Delphi он равен $2A425E19 (19.06.1992 22:22:17)
</div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-33917311469028637342013-04-26T20:25:00.001+04:002013-04-26T20:25:56.843+04:00Прошел игру bandit<div dir="ltr" style="text-align: left;" trbidi="on">
Прошел игру <a href="http://www.overthewire.org/wargames/bandit/">BANDIT от OVERTHEWIRE</a> =)<br />
все уровни простые.<br />
понравились уровни с nc =)<br />
<br />
<pre>bandit9:UsvVyFSfZZWbi6wgC7dAFyFuR6jQQUhR
bandit10:truKLdjsbJ5g7yyJ2X2R0o3a5HQJFuLk
bandit11:IFukwKGsFW8MOq3IRFqrxE1hxTNEbUPR
bandit12:5Te8Y4drgCRfCx8ugdwuEX8KFC6k2EUu
bandit13:8ZjyCRiBWFYkneahHwxCv3wb2a1ORpYL
bandit14:4wcYUJFw0k0XLShlDzztnTBHiqxU3b3e
bandit15:BfMYroe26WYalil77FoDi9qh59eK5xNr
bandit16:cluFn7wTiGryunymYOu4RcffSxQluehd
bandit17:xLYVMN9WE5zQ5vHacb0sZEVqbrp7nBTn
bandit18:kfBf3eYk5BPBRzwjqutbbfE887SVc5Yd
bandit19:IueksS7Ubh8G3DCwVzrTd8rAVOwq3M5x
bandit20:GbKksEFF4yrVs6il55v6gwY5aVje5f0j
bandit21:gE269g2h3mw3pwgrj0Ha9Uoqen1c9DGr
bandit22:Yk7owGAcWjwMVRwrTesJEwB7WVOiILLI
bandit23:jc1udXuA1tiHqjIsL8yaapX5XIAI6i0n
bandit24:UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ</pre>
</div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-89060194519072718822013-01-19T15:43:00.000+04:002013-01-19T15:48:29.169+04:00Delphi Региональные настройки и TFormatSettings<div dir="ltr" style="text-align: left;" trbidi="on">
Часто региональные настройки систем пользователей отличаются, при том что все они в одном языковом регионе. При этом в Delphi по-умолчанию используются настройки системы при загрузке региональных параметров.<br />
Чтобы принудительно загружать из системы настройки нужного языка необходимо до инициализации модуля SysUtils вызвать <a href="http://msdn.microsoft.com/ru-ru/library/windows/desktop/dd374051(v=vs.85).aspx" target="_blank">SetThreadLocale</a>($0419); (для русской языка)<br />
Для этого можно создать модуль, например так:
<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[7]">unit Locale;
interface
implementation
uses
Windows;
initialization
SetThreadLocale($0419);
end.
</pre>
И включить его в файл проекта первым модулем (Для продвинутых кодеров до первого обращения к SysUtils).<br />
Проверить результат можно так:
<br />
<pre class="brush: delphi;gutter:false;toolbar: true">Caption := FormatDateTime(LongDateFormat + ' ' + LongTimeFormat, Now);</pre>
<br />
P.S.: Пока писал эту заметку заметил баг в SysUtils.TFormatSettings.TranslateDateFormat.FixDateSeparator (код от XE2) =)<br />
в этой процедуре происходит подмена разделителя дат на слэш '/'<br />
в моем случае разделитель дат точка '.', длинный формат даты 'd MMMM yyyy ''г.'''<br />
в итоге получаю '19 Январь 2013 г<span style="font-size: x-large;"><b>/</b></span>' - забавно<br />
Скорее всего функция должна игнорировать текст в кавычках.<br />
Баг уже зарегистрирован в <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=104942" target="_blank">QС</a></div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-70101742342078573292013-01-19T15:01:00.000+04:002013-01-19T15:01:08.013+04:00Delphi Оптимальная локализация приложений<div dir="ltr" style="text-align: left;" trbidi="on">
При разработке на Delphi часто приходится писать программы для использования в одном языковом регионе, т.е. для пользователей которым нужен только один язык в программе.<br />
Есть разные методы локализации, в том числе <a href="http://delphi.about.com/od/delphitips2008/qt/hookresourcestr.htm" target="_blank">подмена указателей</a> в <a href="http://docwiki.embarcadero.com/Libraries/XE2/en/System.PResStringRec" target="_blank">PResStringRec</a>. Этот способ плох лишним использованием памяти и дополнительными действиями в runtime - фактически нелокализованные строки остаются в модуле и никогда не используются.<br />
Оптимальным же способом локализации является подмена модулей с переводом содержащихся в них строк. Например, можно взять стандартный модуль RTLConsts.pas скопировать его в папку проекта и перевести содержимое. В таком случае перевод будет скомпилирован и прилинкован к модулю.<br />
<br />
Подмену указателей можно использовать для временной подмены строк. Например, для отображения диалога с нестандартым текстом кнопок. Для этого надо переделать процедуру HookResourceString в функцию:
<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[8]">function HookResourceString(ResStringRec: PResStringRec;
NewStr: PChar): PChar;
var
OldProtect: DWORD;
begin
VirtualProtect(ResStringRec, SizeOf(ResStringRec^),
PAGE_EXECUTE_READWRITE, @OldProtect);
Result := PChar(ResStringRec^.Identifier);
ResStringRec^.Identifier := Integer(NewStr);
VirtualProtect(ResStringRec, SizeOf(ResStringRec^),
OldProtect, @OldProtect);
end;</pre>
Сам код временной подмены:<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[8,9]">
const
sAccept: PChar = '&Accept';
sDecline: PChar = '&Decline';
var
SaveYes: PChar;
SaveNo: PChar;
begin
SaveYes := HookResourceString(@SMsgDlgYes, sAccept);
SaveNo := HookResourceString(@SMsgDlgNo, sDecline);
MessageDlg('Do you accept terms?', mtConfirmation, mbYesNo, 0);
HookResourceString(@SMsgDlgYes, SaveYes);
HookResourceString(@SMsgDlgNo, SaveNo);
MessageDlg('Do you accept terms?', mtConfirmation, mbYesNo, 0);
end;
</pre>
<br /></div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-70372459479406276392013-01-19T14:08:00.000+04:002013-01-19T14:08:33.736+04:00Delphi HInstance<div dir="ltr" style="text-align: left;" trbidi="on">
Глобальная переменная <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/SysInit.HInstance" target="_blank">SysInit.HInstance</a> инициализируется для exe модуля в SysInit._InitExe вызовом HInstance := <a href="http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms683199(v=vs.85).aspx" target="_blank">GetModuleHandle</a>(nil)<br />
<div>
Фактически значение данной переменной - это указатель на адрес в памяти по которому загружен PE модуль.</div>
<div>
Используя этот адрес можно, например, получить дату сборки проекта, получив прямой доступ к PE заголовку файла в памяти:</div>
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[6]">function GetLinkDate: TDateTime;
begin
Result := FileDateToDateTime
(PInteger(
PImageNtHeaders(
Cardinal(PImageDosHeader(HInstance)._lfanew) + HInstance)^
.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]
.VirtualAddress + HInstance + 4
)^);
end;</pre>
</div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-72008792902139615542012-11-03T01:12:00.000+04:002012-11-03T01:12:31.801+04:00Прошел игру natas<div dir="ltr" style="text-align: left;" trbidi="on">
Прошел игру <a href="http://www.overthewire.org/wargames/natas/">NATAS от OVERTHEWIRE</a> =)<br />Первые уровни простые.<br />
понравились уровни с подбором =)<br />
<br />
<pre>natas9:sQ6DKR8ICwqDMTd48lQlJfbF1q9B3edT
natas10:s09byvi8880wqhbnonMFMW8byCojm8eA
natas11:SUIRtXqbB3tWzTOgTAX2t8UfMbYKrgp6
natas12:sh7DrWKtb8xw9PIMkh8OQsgno6iZnJQu
natas13:IGCXqS4x472aoHZYaidvmeoWj2GmuRYz
natas14:sSkCeug1bdrYejzAaBhgwI3qJXDKqlgh
natas15:m2azll7JH6HS8Ay3SOjG3AGGlDGTJSTV
natas16:3VfCzgaWjEAcmCQphiEPoXi9HtlmVr3L
natas17:9HBzt5ljtPAgmaYvNfZ8chZVq50oepsx
</pre>
</div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-85676801935175829432012-07-09T13:03:00.001+04:002012-07-09T13:03:28.811+04:00Автоматизация в IDA<div dir="ltr" style="text-align: left;" trbidi="on">
При разборе чужого кода в IDA периодически приходится выполнять ряд повторяющихся действий.<br />
Для автоматизации процесса можно использовать встроенный интерпретатор idc:<br />
File -> IDC command... (Shift + F2)<br />
<br />
Для удобства можно добавить комбинацию клавиш на повтор последней команды:<br />
<br />
<div style="text-align: left;">
AddHotkey("Shift-I", "___idc0");</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
После добавления комбинации можно описать тело автоматизации.</div>
<br />
Например для обработки null-terminated строк идущих подряд:<br />
<br />
<br />
MakeStr(ScreenEA(),BADADDR);//создать null-terminated строку в текущем положении курсора<br />
Jump(FindUnexplored(ScreenEA(),1));//перейти к следующему не определенному полю.<br />
<br />
<br /></div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-77149846991410665942012-01-17T12:43:00.000+04:002012-01-17T12:43:22.240+04:00<div dir="ltr" style="text-align: left;" trbidi="on">
Прошел тест на воинское звание на <a href="http://multimedia.mil.ru/multimedia/games/go2army.htm">сайте минобороны</a> =)<br />
с результатом можно ознакомиться ниже<br />
<a name='more'></a>
<br />
<div style="background-image: url('http://multimedia.mil.ru/images/military/go2army/widget_5.png'); display: block; float: left; height: 375px; position: relative; width: 498px;">
<div style="float: left; height: 100%; position: relative; width: 100%;">
<a href="http://multimedia.mil.ru/multimedia/games/go2army.htm" style="border: none; clear: both; float: right; height: 67px; margin-right: 125px; margin-top: 14px; outline: medium none; overflow: hidden; position: relative; text-indent: -9999px; width: 248px;">Тест на воинское звание</a>
<br />
<div style="clear: both; color: #dc2525; float: left; font: italic 20px/24px Georgia; height: 24px; margin-top: 115px; position: relative; text-align: center; top: 0px; width: 100%;">
Я — полковник!
</div>
<div style="clear: both; color: #4f4f4f; float: left; font: 11px/14px 'Arial'; left: 23px; margin-top: 25px; position: relative; width: 453px;">
Вы полковник. Вам предстоит командовать полком, бригадой, а может быть и целой дивизией. А это уже полноценный, самостоятельный, живой организм и то, как он будет жить и работать зависит в основном от вашего управленческого таланта. Помните, что современная российская армия постоянно реформируется, меняет свой облик, становится более гуманной по отношению к военнослужащим. Президент приказал сделать российские войска компактными, но при этом более мобильными и боеспособными. Именно вам придется работать над тем, чтобы все эти изменения не оставались только на бумаге.</div>
</div>
</div>
</div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-46278006611403167372012-01-17T12:38:00.000+04:002012-01-17T12:38:54.565+04:00Оптимальное копирование из Recordset в TStrings<div dir="ltr" style="text-align: left;" trbidi="on">
При работе с ADO компонентами часто возникает необходимость копирования данных из Recordset в структуры, которые в дальнейшем используются в программе. Такими структурами могут быть объекты определенного класса, массивы записей, всякого рода списки и т.п.<br />
О том как скопировать данные с наименьшими затратами я опишу далее.<br />
<br />
<a name='more'></a>Обертка для ADO компонент Delphi описывает класс TCustomADODataSet от которого наследуются TADODataSet, TADOTable, TADOQuery и TADOStoredProc. В данном классе есть ссылка на ADO интерфейс Recordset (набор данных).<br />
Типичный способ загрузки данных из ADO компонент сводится к циклу:<br />
<pre class="brush: delphi;gutter:false;toolbar: true;">
while not Eof do
begin
//обработка записи из набора данных
Next;
end;</pre>
Данный цикл является причиной увеличения времени обработки.<br />
Чтобы избавиться от такого цикла можно использовать функцию GetRows у интерфейса Recordset. Для примера покажу загрузку списка идентификаторов и названий в TStrings:<br />
//функция копирования<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[17]">
function ADOIdNameToStrings(ds: TCustomADODataSet): TStrings;
//вспомогательные структуры
type
TRec = record
id: TVarData;
Name: TVarData;
end;
TRecArray = array[0..0] of TRec;
PRec = ^TRecArray;
var
i: Integer;
PlainData: Variant;
vcData: PRec;
begin
Result := TStringList.Create;
TStringList(Result).Duplicates := dupAccept;
PlainData := ds.Recordset.GetRows(Integer(adGetRowsRest), adBookmarkFirst, VarArrayOf([0, 1]));
Result.BeginUpdate;
Result.Capacity := VarArrayHighBound(PlainData, 2);//выделяем память под все записи
vcData := VarArrayLock(PlainData);//получаем указатель на массив из пар Variant
try
for i := Result.Capacity downto 0 do
with vcData^[i] do
Result.AddObject(Name.VOleStr, TObject(id.VInteger));
//TStringList(Result).Sorted := True;//при необходимости можно отсортировать
finally
VarArrayUnlock(PlainData);
Result.EndUpdate;
end;
end;</pre>
</div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-22736663295008837992011-08-18T13:27:00.001+04:002011-08-18T13:31:44.058+04:00TcxGridDBChartView и автоматический расчет осей (Delphi)<div dir="ltr" style="text-align: left;" trbidi="on">Для справки<br />
В devExpress компоненте TcxGrid при построении графиков с использованием TcxGridDBChartView есть 3 варианта расчета минимального и максимального значения осей:<br />
<div style="text-align: left;"></div><ul style="text-align: left;"><li>mmvZeroBasedAuto (по-умолчанию) - максимальное значение берется из данных, минимальное = 0 или при наличии отрицательных значений минимум из данных</li>
<li>mmvAuto - максимальное и минимальное значение берется из данных</li>
<li>mmvCustom - максимальное и минимальное значение берется из соответствующих свойств оси</li>
</ul>Значение присваивается свойству MinMaxValues осей (например, DiagramLine.AxisValue.MinMaxValues)<br />
<br />
</div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-69685503488411819082011-08-17T23:18:00.001+04:002011-08-18T13:29:20.364+04:00FreeAndNil, Assigned and Destructor (Delphi)<div dir="ltr" style="text-align: left;" trbidi="on"><span class="Apple-style-span" style="font-size: large;">FreeAndNil и зачем он нужен</span><br />
Каждый объект в Delphi имеет свой жизненный цикл.<br />
Обычно он сводится к:<br />
<div style="text-align: left;"></div><ul style="text-align: left;"><li>созданию объекта</li>
<li>манипуляциям с объектом</li>
<li>удалению объекта</li>
</ul>При этом, объявляя переменную объекта класса мы объявляем указатель на объект данного класса<br />
<div style="text-align: left;">Например выражение MyList: TList; объявляет указатель с именем MyList - это все равно что мы бы написали MyList: Pointer;, с тем лишь отличием, что в первом случае указатель будет типизированным.</div><div style="text-align: left;"><br />
<a name='more'></a><br />
</div><div style="text-align: left;">Эти объявленные указатели могут использоваться повторно:</div><div style="text-align: left;">MyList := TList.Create;</div><div style="text-align: left;">...</div><div style="text-align: left;">MyList.Free;</div><div style="text-align: left;">MyList := TList.Create;</div><div style="text-align: left;">...</div><div style="text-align: left;">MyList.Free;</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">в данном случае значение указателя MyList изменяется 2 раза при присвоении MyList := TList.Create;, при этом второй раз значение указателя заменяется на новое. </div><div style="text-align: left;"><br />
</div><div style="text-align: left;"><span class="Apple-style-span" style="font-size: large;">Знаменитый Access Violation (AV | EAccessViolation)</span></div><div style="text-align: left;">После второго вызова MyList.Free; указатель MyList не изменяется и указывает на "освобожденный" блок памяти. Отсюда растут ноги знаменитого Access Violation (AV | EAccessViolation). Если после MyList.Free; обратиться к полю, свойству или методу класса TList через указатель MyList, например c := MyList.Count;, мы получим эту распространенную ошибку.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;"><span class="Apple-style-span" style="font-size: large;">Free vs FreeAndNil или освобождение объектов</span></div><div style="text-align: left;">Итак мы разобрались с переменными-указателями на объекты. Давайте рассмотрим освобождение объектов. Для освобождения в Delphi предусмотрено 2 стандартных механизма:</div><div style="text-align: left;"></div><ol style="text-align: left;"><li><u>метод</u> Free класса TObject</li>
<li>процедура FreeAndNil из модуля SysUtils</li>
</ol>Рассмотрим метод Free:<br />
<div style="text-align: left;"></div><div style="text-align: left;">procedure TObject.Free;</div><div style="text-align: left;">begin</div><div style="text-align: left;"> if Self <> nil then</div><div style="text-align: left;"> Destroy;</div><div style="text-align: left;">end;</div><br />
<div style="text-align: left;">Все что делает данные метод - вызывает стандартный деструктор Destroy при условии что указатель на объект не равен nil</div><div style="text-align: left;">отсюда получаем ошибку AV при попытке повторного освобождения объекта если писать так, например:</div><div style="text-align: left;"></div><div style="text-align: left;">MyList := TList.Create;</div><div style="text-align: left;">...</div><div style="text-align: left;">MyList.Free;//указатель на объект не очищается</div><br />
<div style="text-align: left;"></div><div style="text-align: left;">MyList.Count;//AV - пытались вызвать метод освобожденного объекта</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">Рассмотрим процедуру FreeAndNil:</div><div style="text-align: left;"></div><div style="text-align: left;">procedure FreeAndNil(var Obj);</div><div style="text-align: left;">var</div><div style="text-align: left;"> Temp: TObject;</div><div style="text-align: left;">begin</div><div style="text-align: left;"> Temp := TObject(Obj);//сохраняем временный указатель на объект</div><div style="text-align: left;"> Pointer(Obj) := nil;//очищаем указатель</div><div style="text-align: left;"> Temp.Free;//освобождаем объект</div><div style="text-align: left;">end;</div><div style="text-align: left;">Наш пример с "двойным" освобождением завершится без ошибок</div><div style="text-align: left;">MyList := TList.Create;</div><br />
<div style="text-align: left;"></div><div style="text-align: left;"></div><div style="text-align: left;">...</div><div style="text-align: left;">FreeAndNil(MyList);//указатель на объект очищается</div><br />
<div style="text-align: left;"></div><div style="text-align: left;">FreeAndNil(MyList);//срабатывает проверка на self<>nil в TObject.Free; в procedure FreeAndNil(var Obj);</div><div style="text-align: left;">Машинный код для TObject.Free составляет 12 байт это 6 строк ассемблерного псевдокода</div><div style="text-align: left;">для FreeAndNil - 35 байт или 16 строк ассемблерного псевдокода (из них 5 байт или одна строчка кода - вызов той же TObject.Free) оверхэд составляет 30 байт =) или 10 строк</div><div style="text-align: left;">Что дает оверхэд - очистка указателя при чем до вызова TObject.Free.</div><br />
<br />
<div style="text-align: left;"><br />
</div></div>AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0tag:blogger.com,1999:blog-3019700370982892389.post-79683449149835042362011-06-30T22:39:00.004+04:002011-09-02T18:54:52.024+04:00Работа с TList (Delphi)<div dir="ltr" style="text-align: left;" trbidi="on">
<div>
<div dir="ltr" style="text-align: left;" trbidi="on">
Класс <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList">TList</a> - один из наиболее часто используемых классов в Delphi. <br />
Данный класс реализует методы работы с массивом указателей:<br />
<ul style="text-align: left;">
<li>Добавление и удаление элементов списка</li>
<li>Манипуляции над элементами списка (<a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Exchange">перестановка</a>, <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Move">сдвиг</a>, сортировка) </li>
<li>Поиск по списку </li>
</ul>
Как оптимальнее использовать данный класс в работе рассмотрим подробнее...<br />
<a name='more'></a><br />
<a href="http://www.blogger.com/post-edit.g?blogID=3019700370982892389&postID=7968344914983504236" name="more"></a><br />
<div>
<div>
Первое о чем хотелось сказать это, конечно, наполнение списка.<br />
Для добавления элемента в список класс <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList">TList</a> предусматривает метод <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Add">Add</a>. При этом если выделенной под список памяти не хватает она выделяется автоматически. Чаще всего списки наполняются в циклах с известным числом итераций. Для того, чтобы быстрее занести данные в список рекомендуется предварительно установить свойство <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Capacity">Capacity</a> списка равным числу добавляемых элементов. Это приведет к тому, что для списка один раз будет выделена память необходимая для всех добавляемых элементов. Данный подход позволяет работать с памятью оптимальнее. Если не устанавливать свойство <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Capacity">Capacity</a> то при добавлении первых двенадцати элементов список будет расширен 3 раза (по 4 элемента), при добавлении элементов до 76го еще 4 раза (по 16 элементов) далее будет расширяться на четверть текущего размера при необходимости увеличения списка. При этом если блок нового размера не будет вписываться в свободные блоки памяти на месте, весь список будет скопирован в новый блок памяти, что может сказаться на производительности. Второе преимущество данного подхода в том, что если в системе недостаточно памяти можно сразу при установке свойства <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Capacity">Capacity</a> получить ошибку <a href="http://docwiki.embarcadero.com/VCL/en/SysUtils.EOutOfMemory">EOutOfMemory</a>.<span class="Apple-style-span" style="line-height: 19px;"> </span>Что позволит тратить меньше времени на удаление созданных до ошибки элементов. Так что на заметку:<br />
<pre class="brush: delphi;gutter:false;toolbar: true;highlight:[2]">List.Clear;
List.Capacity := Count;
for I := 1 to Count do List.Add(...);</pre>
<br />
Второй момент это чтение данных.<br />
В большинстве класс <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList">TList</a> используется для хранения списка указателей на объекты. Для этого наследуются от данного класса или делегируют списочные свойства приватному полю этого класса. При этом часто публикуют индексное свойство для получения элемента по индексу преобразовывая его в необходимый класс. Такое архитектурное решение при последовательном чтении элементов в цикле дает лишние затраты процессорного времени. Рекомендуется при последовательном чтении в цикле обращаться напрямую к публичному свойству <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.List">List</a> класса <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList">TList</a>. На заметку:<br />
<pre style="border-bottom-color: rgb(47, 111, 171); border-bottom-style: dashed; border-bottom-width: 1px; border-left-color: rgb(47, 111, 171); border-left-style: dashed; border-left-width: 1px; border-right-color: rgb(47, 111, 171); border-right-style: dashed; border-right-width: 1px; border-top-color: rgb(47, 111, 171); border-top-style: dashed; border-top-width: 1px; line-height: 1.1em; padding-bottom: 1em; padding-left: 1em; padding-right: 1em; padding-top: 1em; white-space: pre-wrap; word-wrap: break-word;">for I := 0 to Count - 1 do
CurrentItem := TMyClass(List.List^[I]);</pre>
Таким образом мы снимаем лишние затраты на вызов функции <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TList.Get">Get</a> с встроенной проверкой на выход за границы массива и в случае наследования класса(ов) на лишние вызовы inherited Get(Index).<br />
<br />
До связи =)</div>
</div>
</div>
</div>
</div>
AlWerewolfhttp://www.blogger.com/profile/02606095273852470185noreply@blogger.com0