COMPANY

BLOGS

  • user warning: Unknown column 'u.signature_format' in 'field list' query: SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.signature_format, u.picture, u.data, c.thread, c.status FROM comments c INNER JOIN users u ON c.uid = u.uid WHERE c.nid = 31028 AND c.status = 0 ORDER BY c.cid LIMIT 0, 50 in /var/www/www.4d.com/docs/modules/comment/comment.module on line 991.
  • warning: file_get_contents(http://www.telize.com/geoip/54.81.71.219) [function.file-get-contents]: failed to open stream: HTTP request failed! in /var/www/www.4d.com/docs/includes/common.inc(1762) : eval()'d code on line 4.

オブジェクト型を理解する

25.12.2014
by Keisuke Miyako

v14では,新しい変数タイプ『C_OBJECT』が使用できるようになりました。オブジェクト変数(および配列)は,内部構造がJSON形式であるため,さまざまなタイプのデータが収納できるだけでなく,Webアプリケーションや統合WebKit($4d)との親和性も優れています。さらに,フィールドに対するポインターを格納すれば,テンプレートとしても使用することができます。オブジェクト変数は非常に便利ですが,『参照型』という特殊な変数タイプであるため,通常の変数とは扱いが異なります。この記事では,特殊な変数タイプであるオブジェクト型を使用する上で知っておくべき事柄を説明しています。

 

典型的な間違いは,下記のようなコードを記述してしまうことです。

 

C_OBJECT($o)
ARRAY OBJECT($objs;0)
$values:="abcdefgh"

For ($i;1;Length($values))
  $v:=$values[[$i]]
  OB SET($o;"value";$v)
  APPEND TO ARRAY($objs;$o)
End for

テキスト型など,通常の変数であれば,配列に追加されたそれぞれの要素には,$valuesの内容が1文字ずつ代入されるところですが,オブジェクト型の場合,すべての要素が『h』,つまりOB SETで最後にセットされた値になります。$oという変数は,オブジェクトに対する『参照』であり,オブジェクトそのものではないからです。APPEND TO ARRAYで配列に追加した後に$oの内容を書き換えれば,次に追加される要素だけでなく,すでに配列の一部となっている要素もみな新しい値を参照するようになります。

 

 

このことは,同じように『参照型』であるポインターと比較すると,理解しやすいかもしれません。

 

C_POINTER($p)
ARRAY POINTER($ptrs;0)
$values:="abcdefgh"

For ($i;1;Length($values))
  $v:=$values[[$i]]
  $p:=->$v
  APPEND TO ARRAY($ptrs;$p)
End for

APPEND TO ARRAYで配列に追加されたポインターは,どれも同じ変数を参照するものであり,参照されている変数の内容が変われば,結果的にすべてのポインターはその値を参照することになります。配列にポインターを追加した『瞬間』の値がコピーされるわけではありません。

 

同じように,オブジェクト型の変数をループの中で再利用した場合,それは『参照』を使い回しているだけなので,参照されているオブジェクトの内容が変われば,配列に追加されたオブジェクト型の変数は,どれも同じ内容を参照することになります。

 

正解

上記の例では,毎回,オブジェクト変数をクリア (CLEAR VARIABLE) し,毎回,新しい変数を使用することにより,配列の各要素に別々の参照が代入されるようにすることができます。

 

C_OBJECT($o)
ARRAY OBJECT($objs;0)
$values:="abcdefgh"

For ($i;1;Length($values))
  $v:=$values[[$i]]
  CLEAR VARIABLE($o)
  OB SET($o;"value";$v)
  APPEND TO ARRAY($objs;$o)
End for

 

この場合,CLEAR VARIABLEにより,すでに配列の一部となった変数が消滅することはありません。参照カウントにより,そのオブジェクトは配列がクリアされる(そのオブジェクトを参照するものが存在しなくなる)まで保持されるからです。

 

あるいは,OB Copyでオブジェクトを複製する,という方法も有効です。

 

C_OBJECT($o)
ARRAY OBJECT($objs;0)
$values:="abcdefgh"

For ($i;1;Length($values))
  $v:=$values[[$i]]
  OB SET($o;"value";$v)
  APPEND TO ARRAY($objs;OB Copy($o))
End for

 

あるいは,あらかじめ配列のサイズを宣言しておく,という方法も有効です。

 

$values:="abcdefgh"
ARRAY OBJECT($objs;Length($values))

For ($i;1;Length($values))
  $v:=$values[[$i]]
  OB SET($objs{$i};"value";$v)
End for

 

まとめ

オブジェクト型の変数『C_OBJECT』は,『参照型』という特殊な変数タイプであるため,通常の変数とは扱いが異なります。ただ代入しただけでは,参照が複製されるだけで,値を複製したことになりません。オブジェクトを複製するためには,OB CopyまたはCLEAR VARIABLEを使用します。配列(ARRAY OBJECT)であれば,あらかじめサイズを宣言しておくことも有効です。 

 

【追記】2014年の4D Summitでは,次期バージョンより,オブジェクト型のフィールドも使用できるようになることがアナウンスされました。なお,4Dと同じデータベースエンジンを採用しているWakandaでは,バージョン10より,オブジェクト型のフィールド(ストレージ属性)を使用することができるようになりました。