グローバル配列の操作
この章では、以下の項目について説明します。
-
グローバル配列の概要 — グローバル配列の概念を示し、Java Native SDK がどのように使用されるかを簡単に説明します。
-
ノードの作成、アクセス、および削除 — グローバル配列のノードを作成、変更、または削除する方法、およびノード値を取得する方法を示します。
-
グローバル配列のノードの検索 — グローバル配列のノードへの高速アクセスを可能にする反復メソッドについて説明します。
-
クラス IRIS のサポートされているデータ型 — ノード値を特定のデータ型として取得する方法の詳細を説明します。
この章の例では、irisjv という名前の IRIS オブジェクトが既に存在し、サーバに接続されていると想定します。以下のコードは、標準の JDBC 接続を確立し、IRIS のインスタンスを作成します。
//Open a connection to the server and create an IRIS object
String connStr = "jdbc:IRIS://127.0.0.1:1972/USER";
String user = "_SYSTEM";
String pwd = "SYS";
IRISConnection conn = (IRISConnection) java.sql.DriverManager.getConnection(connStr,user,pwd);
IRIS irisjv = IRIS.createIRIS(conn);
IRIS のインスタンスの作成方法の詳細は、createIRIS() のクイック・リファレンスのエントリを参照してください。JDBC 接続の作成方法の一般的な情報は、"InterSystems ソフトウェアでの Java の使用法" の "JDBC 接続の確立" を参照してください。。
グローバル配列の概要
グローバル配列は、すべてのスパース配列のように、ツリー構造です (シーケンシャル・リストではありません)。グローバル配列の背後にある基本概念は、ファイル構造に例えて示すことができます。ツリーの各ディレクトリは、ルート・ディレクトリ識別子とそれに続く一連のサブディレクトリ識別子で構成されるパスによって一意に識別され、ディレクトリにはデータが含まれることも、含まれないこともあります。
グローバル配列の仕組みも同じです。ツリーの各ノードは、グローバル名識別子と一連の添え字識別子で構成されるノード・アドレスによって一意に識別され、ノードには値が含まれることも、含まれないこともあります。例えば、以下は 6 つのノードで構成されるグローバル配列で、そのうち 2 つのノードには値が含まれます。
root -->|--> foo --> SubFoo="A"
|--> bar --> lowbar --> UnderBar=123
値はその他のノード・アドレス (root または root->bar など) に格納できますが、それらのノード・アドレスが値なしの場合、リソースは無駄になりません。InterSystems ObjectScript グローバルの表記では、値を持つ 2 つのノードは次のようになります。
root("foo","SubFoo")
root("bar","lowbar","UnderBar")
グローバル名 (root) の後に、括弧で囲まれたコンマ区切り添え字リストが続きます。この両者で、ノードのパス全体を指定します。
このグローバル配列は、Native SDK Set() メソッドへの 2 つの呼び出しで作成されます。
irisObject.Set("A", "root", "foo", "SubFoo");
irisObject.Set(123, "root", "bar", "lowbar", "UnderBar");
グローバル配列 root は、最初の呼び出しが値 "A" をノード root("foo","SubFoo") に割り当てるときに作成されます。ノードは任意の順序で、任意の添え字セットを使用して作成できます。これら 2 つの呼び出しの順序を逆にした場合でも、同じグローバル配列が作成されます。値なしノードは自動的に作成され、不要になると自動的に削除されます。詳細は、この章で後述する “ノードの作成、アクセス、および削除” を参照してください。
この配列を作成する Native SDK コードを、以下に例示します。IRISConnection オブジェクトは、サーバへの接続を確立します。この接続は、irisjv という名前のクラス IRIS のインスタンスによって使用されます。Native SDK メソッドを使用してグローバル配列を作成し、生成された永続値をデータベースから読み取った後、グローバル配列を削除します。
package nativedemo;
import com.intersystems.jdbc.*;
public class NativeDemo {
public static void main(String[] args) throws Exception {
try {
//Open a connection to the server and create an IRIS object
String connStr = "jdbc:IRIS://127.0.0.1:1972/USER";
String user = "_SYSTEM";
String password = "SYS";
IRISConnection conn = (IRISConnection) java.sql.DriverManager.getConnection(connStr,user,password);
IRIS irisjv = IRIS.createIRIS(conn);
//Create a global array in the USER namespace on the server
irisjv.set("A", "root", "foo", "SubFoo");
irisjv.set(123, "root", "bar", "lowbar", "UnderBar");
// Read the values from the database and print them
String subfoo = irisjv.getString("root", "foo", "SubFoo");
String underbar = irisjv.getString("root", "bar", "lowbar", "UnderBar");
System.out.println("Created two values: \n"
+ " root(\"foo\",\"SubFoo\")=" + subfoo + "\n"
+ " root(\"bar\",\"lowbar\",\"UnderBar\")=" + underbar);
//Delete the global array and terminate
irisjv.kill("root"); // delete global array root
irisjv.close();
conn.close();
}
catch (Exception e) {
System.out.println(e.Message);
}
}// end main()
} // end class NativeDemo
NativeDemo は、以下の行を出力します。
Created two values:
root("foo","SubFoo")=A
root("bar","lowbar","UnderBar")=123
この例では、conn という名前の IRISConnection オブジェクトが、USER ネームスペースに関連付けられているデータベースへの接続を提供します。Native SDK メソッドは以下のアクションを実行します。
-
IRIS.createIRIS() は、conn を介してデータベースにアクセスする、irisjv という名前の IRIS の新しいインスタンスを作成します。
-
IRIS.set() は、新しい永続ノードをデータベースに作成します。
-
IRIS.getString() は、データベースに対してクエリを実行して、指定されたノードの値を返します。
-
IRIS.kill() は、指定されたノードとそのサブノードすべてをデータベースから削除します。
次の章では、これらのすべてのメソッドについて詳細に説明し、例を示します。
Native SDK 用語の用語集
ここに示す概念の概要については、前のセクションを参照してください。この用語集の例は、以下に示すグローバル配列構造を指しています。Legs グローバル配列には、10 個のノードと 3 つのノード・レベルがあります。10 個のノードのうち 7 つには、値が含まれます。
Legs // root node, valueless, 3 child nodes
fish = 0 // level 1 node, value=0
mammal // level 1 node, valueless
human = 2 // level 2 node, value=2
dog = 4 // level 2 node, value=4
bug // level 1 node, valueless, 3 child nodes
insect = 6 // level 2 node, value=6
spider = 8 // level 2 node, value=8
millipede = Diplopoda // level 2 node, value="Diplopoda", 1 child node
centipede = 100 // level 3 node, value=100
指定された親ノードの直下にあるノードです。子ノードのアドレスは、親添え字リストの末尾に 1 つの添え字を追加して指定します。例えば、親ノード Legs("mammal") には子ノード Legs("mammal","human") および Legs("mammal","dog") があります。
ルート・ノードの識別子は、グローバル配列全体の名前でもあります。例えば、ルート・ノード識別子 Legs は、グローバル配列 Legs のグローバル名です。添え字とは異なり、グローバル名には文字、数字、およびピリオドのみを含めることができます ("グローバル命名規則" を参照)。
グローバル配列の要素で、グローバル名と任意の数の添え字識別子で構成されるネームスペースによって一意に識別されます。ノードは、データを含むか、子ノードを持つか、またはこれらの両方を持つ必要があります。
ノード・アドレス内の添え字の数。'レベル 2 ノード' は、'2 つの添え字を持つノード' のもう 1 つの表現方法です。例えば、Legs("mammal","dog") は、レベル 2 ノードです。ルート・ノード Legs の 2 レベル下で、Legs("mammal") の 1 レベル下です。
グローバル名とすべての添え字を含む、ノードの完全なネームスペースです。例えば、ノード・アドレス Legs("fish") は、ルート・ノード識別子 Legs と 1 つの添え字 "fish" を含むリストで構成されます。コンテキストに応じて、Legs (添え字リストなし) はルート・ノード・アドレスまたはグローバル配列全体を参照することができます。
グローバル配列ツリーの基点にある添え字なしノードです。ルート・ノードの識別子は、その添え字なしのグローバル名です。
特定のノードのすべての下位ノードは、そのノードのサブノードと呼ばれます。例えば、ノード Legs("bug") には 2 つのレベルの 4 つの異なるサブノードがあります。9 つの添え字付きノードはすべて、ルート・ノード Legs のサブノードです。
ルート・ノードの下にあるノードはすべて、グローバル名および 1 つまたは複数の添え字識別子のリストを指定して処理されます (グローバル名に添え字リストを加えたものが、ノード・アドレスです)。
多くの Native SDK メソッドは有効なノード・アドレスを指定する必要がありますが、ノード・アドレスは必ずしも既存のノードを指す必要はありません。例えば、set() メソッドは value 引数とターゲット・アドレスを取り、そのアドレスに値を格納します。ターゲット・アドレスにノードが存在しない場合は、新しいノードが作成されます。
ノードには、サポートされている任意の型の値を含めることができます。子ノードを持たないノードには値を含める必要があり、子ノードを持つノードは値なしにすることができます。
ノードは、データを含むか、子ノードを持つか、またはこれらの両方を持つ必要があります。子ノードを持っているがデータを含まないノードは、値なしノードと呼ばれます。値なしノードは、下位レベルのノードへのポインタとしてのみ存在します。
グローバル命名規則
グローバル名と添え字は、次の規則に従います。
-
ノード・アドレスの長さ (グローバル名とすべての添え字の長さの合計) は最大 511 文字です (入力した一部の文字は、この制限のために、複数のエンコードされた文字としてカウントされることがあります。詳細は、“グローバル参照の最大長” を参照してください)。
-
グローバル名には文字、数字、およびピリオド ('.') を使用でき、最大 31 文字の有効文字を使用できます。文字で始まる必要があり、ピリオドで終了することはできません。
-
添え字は文字列または数値にすることができます。文字列の添え字は大文字と小文字が区別され、すべての文字 (制御文字と非表示文字を含む) を使用できます。長さの制限は、ノード・アドレス全体の最大長が 511 文字という制限のみです。
ノードの作成、アクセス、および削除
Native SDK には、データベース内で変更を実行することのできるメソッドが 3 つ用意されています。set() および increment() はノード値を作成または変更することができ、kill() は 1 つのノードまたは一連のノードを削除することができます。ノード値はタイプ固有のゲッター・メソッド (getInteger() や getString()) などで取得します。
-
ノードの作成とノード値の設定 — set() と increment() の使用方法について説明します。
-
ノード値の取得 — サポートされている各データ型のゲッター・メソッドのリストを示します。
-
ノードの削除 — kill() の使用方法について説明します。
ノードの作成とノード値の設定
set() メソッドと increment() メソッドを使用して、指定した値を持つ永続ノードを作成したり、既存のノードの値を変更したりできます。
IRIS.set() は、任意のサポートされているデータ型の value 引数を取り、指定されたアドレスにその値を格納します。ターゲット・アドレスにノードが存在しない場合は、新しいノードが作成されます。
以下の例では、set() の最初の呼び出しによって、新しいノードがサブノード・アドレス myGlobal("A") に作成され、このノードの値が文字列 "first" に設定されます。2 回目の呼び出しによって、サブノードの値が変更され、整数 1 に置き換えられます。
irisjv.set("first", "myGlobal", "A"); // create node myGlobal("A") = "first"
irisjv.set(1, "myGlobal", "A"); // change value of myGlobal("A") to 1.
set() は、サポートされている任意のデータ型の値を作成および変更できます。既存の値を読み取るには、次のセクションで説明するように、データ型ごとに異なるゲッター・メソッドを使用する必要があります。
IRIS.increment() は number 引数を取り、その数だけノードの値をインクリメントし、そのインクリメントした値を返します。number 引数は、Double、Integer、Long、または Short のいずれかになります。
ターゲット・アドレスにノードがない場合、このメソッドはノードを作成し、値として number 引数を割り当てます。このメソッドはスレッドセーフなアトミック処理を使用して、ノードの値を変更します。したがって、ノードは決してロックされません。
以下の例では、increment() の最初の呼び出しによって、新しいサブノード myGlobal("B") が値 -2 で作成されます。次の 2 回の呼び出しによって、それぞれ -2 だけインクリメントされ、最終値は -6 になります。
for (int loop = 0; loop < 3; loop++) {
irisjv.increment(-2,"myGlobal", "B");
}
set() または increment() の 2 番目の引数は、グローバル配列名です。グローバル配列名には、文字、数字、およびピリオドを使用できます。名前は文字で始まる必要があり、ピリオドで終了することはできません。グローバル名の後の引数は添え字で、これには数値または文字列を指定できます (大文字と小文字が区別され、英数字に限定されません)。詳細は、“グローバル命名規則” を参照してください。
ノード値の取得
set() メソッドはサポートされているすべてのデータ型で使用できますが、各データ型には別個のゲッターが必要です。ノード値は、以下のいずれかのデータ型になります : Boolean、byte[]、Double、Float、Integer、Long、Short、String、Date、Time、Timestamp に加え、Object、IRISList、java.io.InputStream と java.io.Reader のサブクラス、および java.io.Serializable を実装するオブジェクト。
以下のメソッドを使用して、これらのデータ型からノード値を取得します。
-
数値データ型 : getBoolean()、getShort()、getInteger()、getLong()、getDouble()、 getFloat()
-
文字列およびバイナリ・データ型 : getBytes()、 getString()
-
オブジェクトおよび $list データ型 : getObject()、 getIRISList()
-
一時データ型 : getDate()、getTime()、 getTimestamp()
-
その他のデータ型 : getInputStream()、 getReader()
データ型の詳細は、この章で後述する “クラス IRIS のサポートされているデータ型” を参照してください。
ノードの削除
IRIS.kill() は、指定したノードとそのすべてのサブノードを削除します。ルート・ノードを削除した場合、または値を持つすべてのノードを削除した場合、グローバル配列全体が削除されます。
以下の例では、グローバル配列 myGlobal には最初、以下のノードが含まれています。
myGlobal = <valueless node>
myGlobal("A") = 0
myGlobal("A",1) = 0
myGlobal("A",2) = 0
myGlobal("B") = <valueless node>
myGlobal("B",1) = 0
この例では、myGlobal("A") と myGlobal("B",1) の 2 つのサブノードで kill() を呼び出して、グローバル配列全体を削除します。
最初の呼び出しによって、ノード myGlobal("A") とその両方のサブノードが削除されます。
irisjv.kill("myGlobal", "A");
// also kills child nodes myGlobal("A",1) and myGlobal("A",2)
2 番目の呼び出しによって、値を持つ最後の残りのサブノード myGlobal("B",1) が削除されます。
irisjv.kill("myGlobal","B",1);
残りのどのノードにも値がない場合は、グローバル配列全体が削除されます。
-
親ノード myGlobal("B") は、値がなく、サブノードもなくなったため、削除されます。
-
その結果、ルート・ノード myGlobal は値がなく、サブノードもなくなったため、グローバル配列全体がデータベースから削除されます。
グローバル配列のノードの検索
Native SDK は、1 つのグローバル配列の一部または全部に対して反復処理する方法を提供します。以下のトピックでは、さまざまな反復メソッドについて説明します。
-
一連の子ノードにわたる反復 — 指定した親ノードの下のすべての子ノードにわたって反復する方法について説明します。
-
すべてのレベルのサブノードの検索 — サブノードの存在をテストしてノード・レベルに関係なくすべてのサブノードにわたって反復する方法について説明します。
一連の子ノードにわたる反復
子ノードは、同じ親ノードの直下にある一連のサブノードです。現在のターゲット・ノードの子は、ターゲット・アドレスに添え字を 1 つのみ追加してアドレス指定できます。同じ親の下のすべての子ノードは相互に兄弟ノードです。例えば、以下のグローバル配列には、親ノード ^myNames("people") の下に 6 つの兄弟ノードがあります。
^myNames (valueless root node)
^myNames("people") (valueless level 1 node)
^myNames("people","Anna") = 2 (first level 2 child node)
^myNames("people","Julia") = 4
^myNames("people","Misha") = 5
^myNames("people","Ruri") = 3
^myNames("people","Vlad") = 1
^myNames("people","Zorro") = -1 (this node will be deleted in example)
反復子は、照合順 (この場合は、Anna、Julia、Misha、Ruri、Vlad、Zorro というアルファベット順) にノードを返します。これは、反復子の機能ではありません。ノードが作成されると、InterSystems IRIS によって自動的に、ストレージ定義で指定された照合順にノードが格納されます。この例のノードは、作成された順序に関係なく、示された順序で格納されます。
ここでは、以下のメソッドを示します。
-
反復子を作成して一連の子ノードを走査するために使用されるメソッド
-
jdbc.IRIS.getIRISIterator() は、指定されたノードから開始して、グローバルの IRISIterator のインスタンスを返します。
-
IRISIterator.next() は、照合順で次の兄弟ノードの添え字を返します。
-
IRISIterator.hasNext() は、照合順に別の兄弟ノードがある場合に true を返します。
-
-
現在のノードに作用するメソッド
-
IRISIterator.getValue() は、現在のノード値を返します。
-
IRISIterator.getSubscriptValue() は、現在の添え字 (最後に成功した next() の呼び出しと同じ値) を返します。
-
IRISIterator.remove() は、現在のノードとそのサブノードすべてを削除します。
-
以下の例は、^myNames("people") の下の各子ノードを反復処理します。値が 0 以上の場合は添え字とノード値を出力し、値が負の場合はノードを削除します。
// Read child nodes in collation order while iter.hasNext() is true
System.out.print("Iterate from first node:");
try {
IRISIterator iter = irisjv.getIRISIterator("myNames","people");
while (iter.hasNext()) {
iter.next();
if ((Long)iter.getValue()>=0) {
System.out.print(" \"" + iter.getSubscriptValue() + "\"=" + iter.getValue()); }
else {
iter.remove();
}
};
} catch (Exception e) {
System.out.println( e.getMessage());
}
-
getIRISIterator() の呼び出しによって、^myNames("people") の直接の子の反復子インスタンス iter が作成されます。
-
while ループが繰り返されるたびに、以下のアクションが実行されます。
-
next() は、照合順で次に有効なノードの添え字を特定し、そのノードに反復子を配置します(最初の反復では、添え字は "Anna" で、ノード値は 2 です)。
-
getValue() から返されたノード値が負である場合は、remove() が呼び出されてノードが削除されます (すべてのサブノードも含みます。これは、現在のノードに対する kill() の呼び出しと同じです)。
それ以外の場合は、getSubscriptValue() と getValue() を使用して、現在のノードの添え字と値が出力されます。
-
-
この順序に子ノードがそれ以上ないことを示す false を hasNext() が返すと、while ループが終了します。
このコードは、以下の行を出力します (要素 "Zorro" は、値が負であるため出力されていません)。
Iterate from first node: "Anna"=2 "Julia"=4 "Misha"=5 "Ruri"=3 "Vlad"=1
この例は、非常に単純であり、いくつかの状況で失敗する可能性があります。最初または最後のノードから開始したくない場合、どうなるでしょうか。コードが、値なしノードから値を取得しようとする場合、どうなるでしょうか。グローバル配列の複数のレベルにデータがある場合、どうなるでしょうか。以下のセクションでは、これらの状況を処理する方法を説明します。
すべてのレベルのサブノードの検索
次の例では、少し複雑なサブノードのセットを検索します。新しい子ノード "dogs" を ^myNames に追加し、それをこの例のターゲット・ノードとして使用します。
^myNames (valueless root node)
^myNames("dogs") (valueless level 1 node)
^myNames("dogs","Balto") = 6
^myNames("dogs","Hachiko") = 8
^myNames("dogs","Lassie") (valueless level 2 node)
^myNames("dogs","Lassie","Timmy") = 10 (level 3 node)
^myNames("dogs","Whitefang") = 7
^myNames("people") (valueless level 1 node)
[five child nodes] (as listed in previous example)
ターゲット・ノード ^myNames("dogs") には 5 つのサブノードがありますが、そのうちの 4 つのみが子ノードです。4 つのレベル 2 サブノードに加え、レベル 3 サブノード ^myNames("dogs","Lassie","Timmy") もあります。検索では "Timmy" は見つかりません。これは、このサブノードが "dogs" の子ではなく "Lassie" の子であるため、その他のサブノードの兄弟ではないからです。
用語ノード・レベルは、添え字リスト内の添え字数を表します。例えば、^myGlobal("a","b","c") は、“レベル 3 ノード” です。これは、“3 つの添え字を持つノード” を別の方法で表現しているだけです。
ノード ^myNames("dogs","Lassie") には子ノードがありますが、これには値がありません。この場合、getValue() の呼び出しでは null が返されます。以下の例では、逆の照合順で ^myNames("dogs") の子が検索されます。
// Read child nodes in descending order while iter.next() is true
System.out.print("Descend from last node:");
try {
IRISIterator iter = irisjv.getIRISIterator("myNames","dogs");
while (iter.hasPrevious()) {
iter.previous();
System.out.print(" \"" + iter.getSubscriptValue() + "\"");
if (iter.getValue()!=null) System.out.print("=" + iter.getValue());
};
} catch (Exception e) {
System.out.println( e.getMessage());
}
このコードは以下の行を出力します。
Descend from last node: "Whitefang"=7 "Lassie" "Hachiko"=8 "Balto"=6
前の例の検索では、グローバル配列 ^myNames のノードのいくつかが検出されません。これは、検索範囲がさまざまな方法で制限されるためです。
-
ノード ^myNames("dogs","Lassie","Timmy") は、^myNames("dogs") のレベル 2 サブノードではないため検出されません。
-
^myNames("people") の下のレベル 2 ノードは、^myNames("dogs") の下のレベル 2 ノードの兄弟ではないため検出されません。
いずれの場合でも、問題は、previous() および next() が、同じ親の下にある開始アドレスと同じレベルのノードしか検出しないことです。兄弟ノードの各グループに対して異なる開始アドレスを指定する必要があります。
ほとんどの場合は、既知の構造を処理することになるため、入れ子になった単純な呼び出しを使用してさまざまなレベルを走査します。構造に任意の数のレベルがあるようなあまり一般的でない場合には、以下の jdbc.IRIS メソッドを使用して、指定されたノードにサブノードがあるかどうかを確認できます。
-
isDefined() — 指定されたノードが存在しない場合は 0 を返し、ノードが存在し、それに値がある場合は 1 を返します。ノードに値はないが、サブノードがある場合は 10 を返し、値とサブノードの両方がある場合は 11 を返します。
isDefined() が 10 または 11 を返す場合、サブノードが存在し、前述の例で説明したように反復子を作成することによって処理することができます。再帰アルゴリズムでは、このテストを使用して任意の数のレベルを処理できます。
クラス IRIS のサポートされているデータ型
わかりやすくするために、この章の前述の各セクションの例では、常に Integer または String のノード値を使用していましたが、IRIS クラスでは、以下のサポートされているデータ型に対応するデータ型固有のメソッドも提供されています。
以下の IRIS メソッドは、ノード値が数値であると想定し、適切な Java 変数への変換を試みます : getBoolean()、getShort()、getInteger()、getLong()、getDouble()、または getFloat()。ターゲット・ノードに値がないか、ターゲット・ノードが存在しない場合、数値フェッチ・メソッドは UndefinedException をスローします。
Integer のノード値を指定すると、すべての数値メソッドは意味のある値を返します。getInteger() メソッドおよび getLong() メソッドは、Double 値または Float 値に適用しても信頼できる結果が生成されず、これらの値に対して例外がスローされる場合があります。
InterSystems IRIS データベースでは、String、byte[]、および IRISList のオブジェクトは、すべて文字列として格納され、元のデータ型についての情報は維持されません。IRIS getString()、getBytes()、および getIRISList() のメソッドは、文字列データを取得して、それを希望の形式で返します。
文字列ゲッターはノード値が数値ではないと想定し、それを適切に変換しようと試みます。ターゲット・ノードに値がない、またはターゲット・ノードが存在しない場合は、null を返します。これらのメソッドはタイプ・チェックを実行しないため、ノード値が間違ったデータ型であっても通常は例外をスローしません。
IRIS クラスは、Java クラス Date、Time、Timestamp、InputStream、および Reader 向けのゲッターもサポートします。Serializable を実装するクラスは、getObject() で取得できます。
-
getDate()、getTime()、getTimestamp() — java.sql データ型 Date、Time、および Timestamp を取得します。
-
getInputStream() — java.io.InputStream を実装するオブジェクトを取得します。
-
getReader() — java.io.Reader を実装するオブジェクトを取得します。
-
getObject() — ターゲット・ノードの値を取得し、それを Object として返します。
java.io.Serializable を実装するオブジェクトは、getObject() の戻り値を適切なクラスにキャストすることによって取得できます。
これらのメソッドは処理速度のために最適化されており、タイプ・チェックを実行しません。いずれかのメソッドが間違ったデータ型の値をフェッチしようとした場合に例外をスローする処理に、アプリケーションが依存しないようにしてください。例外がスローされる場合もありますが、メソッドが通知なしで失敗し、不正確な値または意味のない値が返される可能性の方が高くなります。