20r6以降,クラスのプロパティ型をproperty
キーワードで宣言した場合,オブジェクト・コレクション・バリアント以外のプロパティにNull
を代入するコードは,シンタックスエラーが返されるようになりました。つまり,var
キーワードで宣言した変数と同じような扱いになります。過去バージョンでは,Null
を代入してもエラーになりませんでした。
クラスをインスタンス化した直後には,プロパティが未定義となっています。未定義であってNull
ではないことに留意してください。プロパティを未定義に戻したいのであれば,未定義の変数を代入することができます。
$object.myAttribute:="hello"
var $undefined // 初期化されていないバリアント型の変数は未定義
$object.myAttribute:=$undefined
プロパティそのものを消去したいのであればOB REMOVE
を使用してください。Null
を代入することによって未定義にできるのは,オブジェクト・コレクション・バリアント型の変数やプロパティだけです。
4Dは,Apple SiliconターゲットのコンパイルにApple clang(Xcode)を使用しています。OSのアップグレードに伴い,Xcodeもアップグレードした場合,初回のコンパイルで「デベロッパーツールが正しく設定されていない」というエラーが返されるかもしれません。
OKボタンをクリックすると,XCodeの標準的なインストール先である/Application/XCode.app にデベロッパーツールのパスが設定されますが,他にも問題がある場合,別のエラーが返されるかもしれません。
エラーメッセージをみると,XCodeのライセンスに合意する必要がある,ということがわかります。XCodeを起動するか,エラーメッセージで勧められているコマンドラインsudo xcodebuild -license
を実行して,ライセンスに合意すると,Apple Siliconターゲットのコンパイルができるようになります。
20r7ではFile
のパス検証が改良されています。過去バージョンでは
$testFile:=File("dummy.jpg"; fk platform path)
のようなコードはエラーを返しませんでしたが,20r7ではエラーが返されるようになりました。無効なファイルパスはTry
でハンドリングすることができます。
var $file:=Try(File("invalid path"))
If ($file#Null)
End if
特別な理由がない限り,メニュー番号#1
は削除しないでください。削除した場合,デザインモードのメニュー「アプリケーションをテスト」「アプリケーションをテスト(SDIモード)」や「アプリケーションをテスト(MDIモード)」が使用できなくなります。
ビルドされていない4Dから4D Serverにクライアントとして接続する場合,「サーバーに接続」画面を起動し,サーバー公開名を選択するか,サーバーIPアドレスまたはDNSホスト名を入力してサーバーにアクセスするのが標準的な手順ですが .4DLink ファイルをストラクチャ/プロジェクトファイルの代わりに開くことにより「サーバーに接続」画面をスキップして直接サーバーに接続することもできます。 .4DLink ファイルはOPEN DATABASE
で開くこともできます。
.4DLink ファイルは,XML形式の標準テキストファイルです。XML構造のスキーマは,アプリケーションパッケージ内のdatabase_link.dtd ファイルに記述されています。具体的には,下記の接続オプションを指定することができます。
is_remote
: true
: クライアント接続user_name
: ログインユーザー名password
: パスワードmd5_password
: ハッシュされたパスワードstructure_opening_mode
: 0
: 前回と同じ1
: インタープリター2
: コンパイル済server_database_name
: サーバー公開名server_path
: サーバーIPアドレスまたはDNSホスト名open_login_dialog
: true
: ログイン画面を表示するdata_opening_mode
: 1
: 前回と同じ2
: データファイルを選択する3
: データファイルを作成する。data_conversion_mode
: 0
: 確認する1
: アップグレードしない2
: アップグレードする。structure_conversion_mode
: 0
: 確認する1
: アップグレードしない2
: アップグレードする。open_tools
: MSCを起動する。open_in_custom_mode
: true
: カスタムメニューモードで起動するcreate_structure_file
: true
: 起動時に新規ストラクチャ/プロジェクトファイルを作成するstructure_file
: ストラクチャ/プロジェクトファイルのパスcreate_data_file
: true
: 起動時に新規データファイルを作成するdata_file
: データファイルのパス。skip_onstartup_method
: true
: 起動時にOn Startup を実行しないstartup_method
: On Startup の代わりに実行するメソッドdefinition_import_file
: カタログ定義ファイルのパス(内部仕様)resources_import_file
: リソース定義ファイルのパス(内部仕様)reopen_design_windows
: false
: デザインモード開始時にウィンドウを開かないexplorer_groups_file
: エクスプローラーの初期状態を記述したJSONファイルのパス(内部仕様)use_journal_file
: false
: ジャーナルファイルを使用しないuser_param
: コマンドラインパラメーター(CLIの--user-param
およびデータベースパラメーターのUser param valueと同じ)remote_shared_resources
: ビルド版クライアントでローカルキャッシュを共有する(ビルドキーのShareLocalResourcesOnWindowsClient
と同じ).4DLink ファイルは,XML形式の標準テキストファイルなので,文字列コマンド・XMLコマンド・PROCESS 4D TAGS
などを活用して動的に生成することができます。以前に接続したことのあるサーバーであれば,Alt
(Macはoption
)キーを押しながら「最近使用したプロジェクト」を選択することにより「サーバーに接続」画面で使用した直近15
回の .4DLink ファイルをみつけることができます。
さまざまなオプションが用意されていますが,使用する頻度が高いのは以下の項目でしょう。
注記: サーバーがサブネット上にUDPプロトコルで公開されていれば,サーバーIPアドレスまたはDNSホスト名は任意ですが,たとえ非公開サーバーであっても,サーバー公開名は必須です。サーバーIPアドレスまたはDNSホスト名だけで接続することはできません。
パスワードハッシュですが,19r3はbcrypt の$2y$10
モードが使用されており,20r3以降,bcrypt の$2b$10
モードが使用されています。過去のバージョンでは,MD5をアレンジした独自のハッシュ(Generate digestの_o_4D REST digest モード)が使用されていました。
19r3以降
$md5_password:=Generate digest($password; _o_4D REST digest)
で生成したパスワードをmd5_password
に渡すことはできません。
bcrypt アルゴリズムでは,入力値が同じであっても,違うハッシュが生成されますが,.4DLink ファイルでログインするためには,directory.json ファイルに記録されているハッシュ値をそのまま渡す必要があり,それ以外のハッシュ値は,たとえVerify password hashで合致する正しいハッシュ値であっても,ログインできません。
つまり
$md5_password:=Generate password hash($password; {algorithm: "bcrypt"; cost: 10})
で動的に生成したパスワードをmd5_password
に渡すことはできません。
CHANGE PASSWORD
・Set user properties
・ツールボックスで
パスワードを保存するたびに更新されるSettingsのdirectory.jsonからハッシュをコピーするか,平文のpassword
を使用する必要があります。
以下は .4DLink ファイルを動的に生成してサーバーに接続する例です。
var $is_remote; $open_login_dialog : Boolean
var $server_database_name; $server_path; $user_name; $password; $md5_password; $link : Text
$is_remote:=True
$user_name:="User"
$password:="password"
$server_database_name:="server"
$server_path:="172.16.197.1:19813"
$version:=Application version
$database_shortcut:=DOM Create XML Ref("database_shortcut")
DOM SET XML ATTRIBUTE($database_shortcut; "server_database_name"; $server_database_name)
DOM SET XML ATTRIBUTE($database_shortcut; "server_path"; $server_path)
DOM SET XML ATTRIBUTE($database_shortcut; "is_remote"; $is_remote)
DOM SET XML ATTRIBUTE($database_shortcut; "user_name"; $user_name)
//$md5_password:="$2b$10$tgBupl1q.qWkB52M92fGzeseedHNuGtFLwdXs2iMZ5LpxZSoLRKNm" //from directory.json
If ($password#"")
Case of
: ($version>="1930") & ($md5_password="")
DOM SET XML ATTRIBUTE($database_shortcut; "password"; $password)
: ($version>="1930")
DOM SET XML ATTRIBUTE($database_shortcut; "md5_password"; $md5_password)
Else
$md5_password:=Generate digest($password; 2) // _o_4D REST digest
DOM SET XML ATTRIBUTE($database_shortcut; "md5_password"; $md5_password)
End case
End if
DOM EXPORT TO VAR($database_shortcut; $link)
DOM CLOSE XML($database_shortcut)
var $linkFile : 4D.File
$linkFile:=Folder(Temporary folder; fk platform path).file(Generate UUID+".4DLink")
$linkFile.setText($link)
OPEN DATABASE($linkFile.platformPath)
$linkFile.delete()
標準コマンドは,スレッドセーフ(プリエンプティブモードで実行できる)あるいはアンセーフ(できない)のどちらかなので,スレッドセーフではないコマンドをプリエンプティブプロセスから呼び出そうとしているようなメソッドは,コンパイル時にエラーが返されます。ただし,Get database parameter
とSET DATABASE PARAMETER
は例外です。これらのコマンドはスレッドセーフですが,実際にプリエンプティブプロセスから呼び出せるかどうかはセレクター値に左右されます。スレッドセーフなセレクターは以下のとおりです。挙げられていないセレクターをプリエンプティブプロセスで指定した場合,ランタイムエラー「スレッドセーフではないコマンドをプリエンプティブプロセスから呼び出せません。」が返されます。
値 | 定数 | get | set |
---|---|---|---|
28 | 4D Server log recording | ✅ | ✅ |
34 | Debug log recording | ✅ | ✅ |
65 | (undocumented) | ✅ | ✅ |
79 | Diagnostic log recording | ✅ | ✅ |
80 | Log command list | ✅ | |
86 | Diagnostic log level | ✅ | ✅ |
90 | Circular log limitation | ✅ | ✅ |
95 | Cache flush periodicity | ✅ | ✅ |
108 | User param value | ✅ | |
110 | SMTP Log | ✅ | ✅ |
111 | Current process debug log recording | ✅ | ✅ |
112 | Is current database a project | ✅ | ✅ |
113 | Is host database a project | ✅ | ✅ |
116 | POP3 Log | ✅ | ✅ |
119 | IMAP Log | ✅ | ✅ |
121 | Pause logging | ✅ | ✅ |
オブジェクト型は,メモリに存在するオブジェクトに対する参照です。オブジェクト型を変数に代入したり,パラメーターとしてメソッドや関数に渡した場合,オブジェクトのコピーが作成される代わりにオブジェクトの参照カウントがインクリメントされます。オブジェクトを参照している変数に別のオブジェクトが代入された場合,あるいは変数やパラメーターがクリアされた場合,オブジェクトが消去される代わりにオブジェクトの参照カウントがデクリメントされます。参照カウントがゼロに達すると,オブジェクトが占有していたメモリが解放されます。
オブジェクト型またはコレクション型をオブジェクト型のプロパティまたはコレクション型のメンバーに代入した場合,代入しようとしているオブジェクト型またはコレクション型が代入されようとしているオブジェクト型またはコレクション型から直接的あるいは間接的に参照されているかどうかをチェックする必要があります。つまり,再帰的な参照の有無を調べる必要があります。再帰的な参照をきちんと管理しないと,正しいタイミングで参照カウントがゼロにならないためです。
代入の対象となっているオブジェクト型の一部分(たとえば上位のプロパティ)をすでに参照しているようなオブジェクト型またはコレクション型をオブジェクト型のプロパティまたはコレクション型のメンバーに代入した場合,参照の数に応じて代入に要する時間が長くなります。
$oMap:={}
For ($i; 1; $iterations)
$o:={col: []; o: {}}
$o.map:=$oMap
$oMap[String($i)]:=$o
End for
この例では,親オブジェクト$oMap
に対する参照がすべての子オブジェクトが持つ.map
プロパティに代入されているので,子オブジェクトを追加するたびに親オブジェクトの参照が増えてゆき,代入のスピードがどんどん遅くなってゆきます。それぞれの子オブジェクトは,{col: []; o: {}}
,つまり,1
個のオブジェクトと1
個のコレクションからなるシンプルなオブジェクト型としてインスタンス化されますが,.map
プロパティに親オブジェクト$oMap
を代入するためには,まず,代入しようとしている親オブジェクト$oMap
が代入されようとしている子オブジェクト$o
から参照されているかどうかをチェックする必要があるためです。回数を重ねるごとに親オブジェクトが持つ参照の数は増えてゆき,直接的な参照だけでなく,間接的な参照もすべてチェックしなければならないので,このチェックに要する時間はどんどん長くなります。もっとも,$o
はインスタンス化されたばかりのオブジェクトであり,すべての参照をチェックしたところで$oMap
からの参照はありません。相互に対する参照が生まれるのは,親オブジェクトを子オブジェクトを代入した後のことです。
つづく代入文でも,代入しようとしている子オブジェクト$o
が代入されようとしている親オブジェクト$oMap
から参照されているかどうかをチェックする必要がありますが,このときのチェックは瞬時に終わります。$o
の.map
プロパティが親オブジェクトを参照しているので,これは再帰的なオブジェクトです。
このことを理解すると,代入の順序を入れ替えるだけで,実行の速度が劇的に向上することに気づきます。
$oMap:={}
For ($i; 1; $iterations)
$o:={col: []; o: {}}
$oMap[String($i)]:=$o
$o.map:=$oMap
End for
子オブジェクト$o
を発展させる前に,まず,親オブジェクトに代入して,再帰的な参照を確立させているのがポイントです。この時点で$o
には,メンバーである1
個の空オブジェクトと1
個の空コレクション,そしてオブジェクトそのものを加えた合計3
個の参照しかありません。そして再帰的な参照もありません。なのでチェックはすぐに終わります。つぎに子オブジェクトの.map
プロパティに親オブジェクト$oMap
を代入しますが,この時点で両オブジェクトはすでに互いを参照しているので,やはりチェックは瞬時に終わります。
つまり,再帰的な参照を持つオブジェクト型またはコレクション型を代入文で構築する場合,まず部品を完成させてから本体に連結するのではなく,さきに参照だけ連結させてから個別の部品を「肉付け」したほうが良い,ということになります。オブジェクトの参照が少ないうちに再帰的な代入をしたほうが,オブジェクトの参照が多くなってから代入するよりも効率的だからです。