Добавление классов в TObjectDictionary

Vizit0r
Дата: 13.07.2019 18:10:04
Вычитываю XML, добавляю в TObjectDictionary

  TAssetType = record
    padding : Byte;
    directory : String;
    FileExt : String;
  end;

  TAssetBoolType = array[Boolean] of TAssetType; //False - unpacked, True - packed;

  TAssetClass = class
  private
    fVal : TAssetBoolType;
    function Get(T: Boolean): TAssetType;
  public
    property Val[T: Boolean] : TAssetType read Get; default;
    constructor Create;
  end;


----------------


  fUOPAssets := TObjectDictionary<Byte,TAssetClass>.Create([doOwnsValues]);

  for i := 0 to XMLDoc.Root.ItemCount - 1 do
  begin
    XML_Element := XMLDoc.Root.Items[i];
...
    if not fUOPAssets.TryGetValue(Num,AssetClass) then
      AssetClass := TAssetClass.Create;

    AssetClass.fVal[IsFilePacked].padding := XML_Element.Properties.IntValue('resourcetpaddingype',0);
    AssetClass.fVal[IsFilePacked].directory := XML_Element.Properties.Value('directory','').ToLower;
    AssetClass.fVal[IsFilePacked].FileExt := XML_Element.Properties.Value('suffix','').ToLower;

    fUOPAssets.AddOrSetValue(Num, AssetClass);
  end;


Какого-то беса результат TAssetClass.Create - есть указатель на тот же адрес, что и в прошлой итерации цикла. Уже и перекрывал конструктор, без толку.
Вроде просто всё, проще некуда. А не работает, точнее работает неправильно.

Содержимое словаря в приложенной картинкой, стрелками разных цветов выделил дублирующиеся указатели на объект.
Словарь заполняется один раз, освобождается в конце работы, в процессе - только чтение.

Что я делаю не так?
x1ca4064
Дата: 13.07.2019 18:37:24
Vizit0r
Что я делаю не так?


Судьба Num не ясна, думаю, в нем собака и зарыта :)
Vizit0r
Дата: 13.07.2019 19:02:15
    Num := XML_Element.Properties.IntValue('resourcetype',$FF);
    if Num = $FF then
      Continue;


$FF для пропуска строк без 'resourcetype', ну это понятно.
x1ca4064
Дата: 13.07.2019 19:31:46
Vizit0r,

А как этот код расположен по отношению к остальному?

Я подозреваю, что у Вас Num меняется в процессе: .Create не вызывается, меняется Num, старый объект с новым Num добавляется.
Vizit0r
Дата: 13.07.2019 20:28:39
на отладке смотрел - нормально меняется на каждой итерации, в соотвествии с resourcetype в каждом элементе.

Выложу почти полный исходник:

под спойлером типы и классы.

+
type
  TUOPAssets = class;

  TGetUOPAssets = function : TUOPAssets of object;

  TAssetType = record
    padding : Byte;
    directory : String;
    FileExt : String;
  end;

  TAssetBoolType = array[Boolean] of TAssetType; //False - unpacked, True - packed;

  TAssetClass = class
  private
    fVal : TAssetBoolType;
    function Get(T: Boolean): TAssetType;
  public
    property Val[T: Boolean] : TAssetType read Get; default;
    constructor Create;
  end;

  TUOPAssets = class
  private
    fUOPAssets : TObjectDictionary<Byte,TAssetClass>;
    FInited : Boolean;
  public
    constructor Create(ShardDir : String);
    property Inited : Boolean read FInited;
    property Assets : TObjectDictionary<Byte,TAssetClass> read fUOPAssets;
  end;

implementation
function TAssetClass.Get(T: Boolean): TAssetType;
begin
  Result := fVal[T];
end;

constructor TAssetClass.Create;
begin
  inherited Create;
end;


вот весь конструктор целиком, с разбором XML

+
constructor TUOPAssets.Create(ShardDir : String);
var UOPPackage : TMythicPackage;
    FileBuf : TArray<byte>;
    XMLDoc : TJclSimpleXML;
    XML_Element : TJclSimpleXMLElem;
    XMLString : String;
    IsFilePacked : Boolean;
    i  : Integer;
    AssetClass : TAssetClass;
    Asset : TAssetBoolType;
    Num : Byte;
begin
  inherited Create;
  FInited := False;
  if not FileExists(shardDir + 'MainMisc.uop') then
    Exit;
  UOPPackage := TMythicPackage.Create;
  UOPPackage.Open(shardDir + 'MainMisc.uop');
  if UOPPackage.FileCount = 0 then
  begin
    UOPPackage.DisposeOf;
    Exit;
  end;
  FileBuf := UOPPackage.UnpackByName('data/assetmap.xml');
  if Length(FileBuf) = 0 then
  begin
    UOPPackage.DisposeOf;
    Exit;
  end;
  XMLString := TEncoding.ANSI.GetString(FileBuf);
  fUOPAssets := TObjectDictionary<Byte,TAssetClass>.Create([doOwnsValues]);

  try
    XMLDoc := TJclSimpleXML.Create;
    XMLDoc.LoadFromString(XMLString);
  except
    Exit;
  end;
  if XMLDoc.Root.name <> 'assetmap' then Exit;

  for i := 0 to XMLDoc.Root.ItemCount - 1 do
  begin
    XML_Element := XMLDoc.Root.Items[i];
//    if XML_Element.Name <> 'patternmap' then Continue;
    if Assigned(XML_Element.Properties.ItemNamed['fileFormat']) then
      IsFilePacked := XML_Element.Properties.ItemNamed['fileFormat'].Value = 'packed'
    else
      IsFilePacked := True;
    Num := XML_Element.Properties.IntValue('resourcetype',$FF);
    if Num = $FF then
      Continue;
    if not fUOPAssets.TryGetValue(Num,AssetClass) then
      AssetClass := TAssetClass.Create;

    AssetClass.fVal[IsFilePacked].padding := XML_Element.Properties.IntValue('resourcetpaddingype',0);
    AssetClass.fVal[IsFilePacked].directory := XML_Element.Properties.Value('directory','').ToLower;
    AssetClass.fVal[IsFilePacked].FileExt := XML_Element.Properties.Value('suffix','').ToLower;

    fUOPAssets.AddOrSetValue(Num, AssetClass);
  end;
  FInited := True;
  XMLDoc.DisposeOf;
  UOPPackage.DisposeOf;
end;


а вот файл, который разбирается.

+
<assetmap>
   <patternmap resourcetype="1" fileFormat="raw" padding="8" directory="Data/WorldArt/" suffix="*" patternMatch="1" />
   <patternmap resourcetype="1" fileFormat="packed" padding="8" directory="Build/WorldArt/" suffix=".dds"/>
   <patternmap resourcetype="2" directory="Data/Maps/" suffix=".dat" />
   <patternmap resourcetype="3" fileFormat="raw" directory="Data/Definitions/Terrain/" suffix=".xml" />
   <patternmap resourcetype="3" fileFormat="packed" directory="Build/TerrainDefinition/" suffix=".bin" />
   <patternmap resourcetype="5" directory="Data/Statics/" suffix=".dat" />
   <patternmap resourcetype="6" fileFormat="raw" padding="8" directory="Data/Definitions/TileArt/" suffix=".tileart" />
   <patternmap resourcetype="6" fileFormat="packed" padding="8" directory="Build/TileArt/" suffix=".bin" />
   <patternmap resourcetype="8" padding="8" directory="Data/Textures/" suffix="*" patternMatch="1" />
   <patternmap resourcetype="8" fileFormat="packed" padding="8" directory="Build/TerrainTexture/" suffix=".dds"/>
   <bitmaskmap resourcetype="9" fileFormat="raw" directory="Data/Sectors/" suffix=".sector">
      <section prefix="Facet_" mask="0x3f" padding="2"/>
      <section prefix="/" mask="0xffffffc0" padding="8"/>
   </bitmaskmap>
   <bitmaskmap resourcetype="9" fileFormat="packed" directory="Build/Sectors/" suffix=".bin">
      <section prefix="Facet_" mask="0x3f" padding="2"/>
      <section prefix="/" mask="0xffffffc0" padding="8"/>
   </bitmaskmap>
   <patternmap resourcetype="10" padding="8" directory="Data/Definitions/LegacyTerrain/" suffix=".legacyterrain" />
   <patternmap resourcetype="11" fileFormat="raw" padding="8" directory="Data/Definitions/Animation/" suffix=".animation" />
   <patternmap resourcetype="11" fileFormat="packed" padding="8" directory="Build/AnimationDefinition/" suffix=".bin" />
   <patternmap resourcetype="12" fileFormat="raw" padding="8" directory="Data/Definitions/Animation/" suffix=".sequence" />
   <patternmap resourcetype="12" fileFormat="packed" padding="8" directory="Build/AnimationSequence/" suffix=".bin" />
   <patternmap resourcetype="13" fileFormat="raw" padding="8" directory="Data/Definitions/Effect/" suffix=".effects" />
   <patternmap resourcetype="13" fileFormat="packed" padding="8" directory="Build/EffectDefinitionCollection/" suffix=".bin" />
   <bitmaskmap resourcetype="14" directory="Data/Anim/" suffix=".tga">
      <section isDirectory="1" patternMatch="1" mask="0xffffc000" padding="6"/>
      <section isDirectory="1" patternMatch="1" mask="0x3f00" padding="2"/>
      <section mask="0xff" padding="4"/>
   </bitmaskmap>
   <patternmap resourcetype="15" padding="3" directory="Data/LocalizedStrings/" suffix=".cliloc" />
   <bitmaskmap resourcetype="16" fileFormat="packed" directory="Build/AnimationFrame/" suffix=".bin">
      <section isDirectory="1" mask="0xffff00" padding="6"/>
      <section mask="0xff" padding="2"/>
   </bitmaskmap>
   <bitmaskmap resourcetype="25" fileFormat="packed" directory="Build/AnimationLegacyFrame/" suffix=".bin">
      <section isDirectory="1" mask="0xffff00" padding="6"/>
      <section mask="0xff" padding="2"/>
   </bitmaskmap>
   <bitmaskmap resourcetype="18" fileFormat="packed" directory="Build/Paperdoll/" suffix=".bin">
      <section isDirectory="1" mask="0xffffc0" padding="6"/>
      <section mask="0x3f" padding="2"/>
   </bitmaskmap>
   <patternmap resourcetype="19" fileFormat="raw" padding="6" directory="Data/Definitions/Multi/" suffix=".multi" />
   <patternmap resourcetype="19" fileFormat="packed" padding="6" directory="Build/MultiCollection/" suffix=".bin" />
   <simplemap resourcetype="20" fileFormat="raw" resourcenumber="0" filename="Data/Definitions/Multi/housing.xml" />
   <simplemap resourcetype="20" fileFormat="packed" resourcenumber="0" filename="Build/MultiCollection/housing.bin" />
   <simplemap resourcetype="21" fileFormat="raw" resourcenumber="0" filename="Data/Sectors/waypoint.xml" />
   <simplemap resourcetype="21" fileFormat="packed" resourcenumber="0" filename="Build/Sectors/waypoint.bin" />
   <patternmap resourcetype="22" fileFormat="raw" directory="Data/TileArtLegacy/" suffix="*" patternMatch="1" />
   <patternmap resourcetype="22" fileFormat="packed" padding="8" directory="Build/TileArtLegacy/" suffix=".dds"/>
   <patternmap resourcetype="23" fileFormat="raw" padding="8" directory="Data/Definitions/Facet/" suffix=".xml" />
   <patternmap resourcetype="23" fileFormat="packed" padding="8" directory="Build/FacetDefinition/" suffix=".bin" />
   <patternmap resourcetype="24" fileFormat="raw" padding="8" directory="EditorData/BrushMulti/" suffix=".multi" />
   <!-- Resource type 25 is only used by legacy -->
   <patternmap resourcetype="26" fileFormat="raw" directory="Data/TileArtEnhanced/" suffix="*" patternMatch="1" />
   <patternmap resourcetype="26" fileFormat="packed" padding="8" directory="Build/TileArtEnhanced/" suffix=".dds" />
 </assetmap>
white_nigger
Дата: 14.07.2019 01:35:28
Vizit0r
Какого-то беса результат TAssetClass.Create - есть указатель на тот же адрес, что и в прошлой итерации цикла.
Так код этому и не противоречит. Как написал - так и работает.
Vizit0r
Дата: 14.07.2019 07:21:52
а поподробнее?
AssetClass := TAssetClass.Create;
же должно создавать НОВЫЙ экземпляр класса.
Vizit0r
Дата: 14.07.2019 08:18:58
докопался.

оказывается, в TDictionary.DoSetValue
происходит

ValueNotify(oldValue, cnRemoved);
ValueNotify(Value, cnAdded);


а на cnRemoved в TObjectDictionary происходит уничтожение объекта.


вот уж чего не ожидал.
Cobalt747
Дата: 14.07.2019 09:40:18
Vizit0r
докопался.

оказывается, в TDictionary.DoSetValue
происходит

ValueNotify(oldValue, cnRemoved);
ValueNotify(Value, cnAdded);


а на cnRemoved в TObjectDictionary происходит уничтожение объекта.


вот уж чего не ожидал.

Рекомендую обращать внимание на параметры конструктора используемых объектов.
Vizit0r
Дата: 14.07.2019 10:31:38
обращал, и doOwnsValues ставил вполне осознанно.