「 宣言した変数を呼び出そうとすると、『存在しません』というエラーが出る… 」
「 変数や定数って一体どこに宣言すればいいの…? 」
プログラムを書いていく中で、そんな場面に出会ったことはないでしょうか?
今回は、変数の取り扱いにおいて重要な考え方である、「スコープ」について学んでいきましょう。初学者の方でもわかりやすいよう、できるだけ噛み砕いた表現で、わかりやすく解説していきますので、安心して読み進めていただければと思います。
また、本記事ではPraygroundを使って実際にコードを書きながら解説をしていきます。
まだ使用したことがない方は、この機会にPlaygroundを起動して、ぜひ一緒に手を動かしながら進めていきましょう。
スコープとは、定数・変数が利用できる範囲のことを指します。
スコープはプログラミングにおいて重要な概念ですが、初めのうちは混乱しやすい一つのポイントとなります。
しかし、一度理解してしまえば難しいことではありませんので、ゆっくり学習を進めていきましょう。
まず、スコープを学習する上で理解しておきたい定数・変数について触れていきます。
定数とは、”値を入れる箱”のような役割を持ちます。
let <定数名> = <値> とコードを記述することで、
定数名が与えられた定数に、値を代入することができます。
(例) let level = 5
[“5”という値が格納された”level”という名前の定数]
変数も定数と同じく、値を格納する箱の役割を持ちます。
var <変数名> = <値> とコードを記述することで、
変数名が与えられた変数に、値を代入することができます。
(例) var level = 5
[“5”という値が格納された”level”という名前の変数]
定数も変数も同じ「値を箱に代入する」という機能を持ちますが、
この二つの大きな違いとして、一度入れた値を上書き出来るかどうかという違いがあります。
定数(let)
一度格納した値を後から上書きすることは出来ません。❌
変数(var)
一度格納した値を後から上書きすることが出来ます。⭕️
定数・変数の機能を理解した上で、スコープの学習に入りましょう。
スコープとは、定数や変数が利用できる範囲のことを指します。
プログラミング初心者はこのスコープでエラーになったりして、つまづきやすいポイントになります。必ずマスターしておきましょう。
こちらは簡易的なコードの一例です。
let level = 5 // ①
if level < 10 { // ②
let message = "初級"
print(message)
}
①のコード解説
5(値)が代入された定数level(定数名)を定義しています。
②のコード解説
「if」とは、「もし〜だったら」という意味合いを持ちます。
「もし定数「level」の値が10未満だったら」という式をコードで表しています。
if文で記述された条件式が当てはまっていた場合、後の処理ブロック「[{ … }」内のコードが実行されます。
処理ブロック「{ … }」内の式で、定数「message」に文字列”初級”を代入して、定数「message」をプリント出力しています。
では、このコードにおける定数のスコープ(利用できる範囲)を見ていきましょう。
今回のコードには二つの定数が出てきました。
let level = 5
let message = “初級”
この二つの定数は、それぞれ利用できる範囲が違います。
これはなぜかというと、
「定数や変数を定義する場所によって、利用できる範囲が変わる」のが理由です。
◆定数「level」 の場合
定数levelはif文の外側で定義されています。
利用範囲は青枠の部分、つまり全体で利用することができます。
◆定数「message」 の場合
定数messageはif文の中で定義されています。
利用範囲は緑枠の部分、
if文の処理ブロック範囲でのみ限定的に利用することができます。
では試しに、Praygroundを用いて、先ほどのコード例を変化させてスコープ範囲を確認してみましょう。
if文の処理ブロックに記述されていた、定数「message」をプリント出力するコードを、
if文処理ブロックの外に出してみました。
[Before]
let level = 5
if level < 10 {
let message = "初級"
print(message) ⬅︎
} if ここまで
[After]
let level = 5
if level < 10 {
let message = "初級"
} // if ここまで
print(message) ⬅︎
この状態でコードを実行してみると、コンパイルエラーが発生します。
error:
Cannot find “message” in Scope
このエラーは、「”message”は存在しませんでしたよ」とXcode が教えてくれています。定数「message」はif文のブロック内で定義しているため、ブロック外で扱うことはできないということがわかりますね。
では次に、定数「level」のスコープ範囲もPraygroundで確認していきましょう。
if文の処理ブロック内に、定数levelのプリント出力を追記しました。
[Before]
let level = 5
if level < 10 {
let message = "初級"
print(message)
} if ここまで
[After]
let level = 5
if level < 10 {
let message = "初級"
print(message)
} if ここまで
print(level) ⬅︎
では、この状態でコードを実行してみます。
定数messageの値に続いて、定数levelの値も問題なくprint出力されていますね。
定数levelはif文のブロックの外で定義されているため、if文の処理ブロック内でも扱うことができるということがわかります。
これらがスコープの基本的な概念となります。
定数や変数は、定義された場所が囲まれたブロック内でのみ、扱うことができるということを覚えておきましょう。
関数や制御構文内で定義されるスコープのことを指します。
前項目の定数messageがローカルスコープに当てはまります。
制御構文や関数など、どの範囲にも含まれない広範囲なスコープのことを指します。
前項目の定数levelがグローバルスコープに当てはまります。
ここまでスコープの概念について学習してきました。
ではそもそも、なぜプログラミングにおいてスコープというものが必要なのでしょうか。
その答えとしては、「影響する範囲を狭くしたいから」という理由があります。
スコープとは、「利用できる範囲」と解説しました。
これは、「依存する範囲」と言い換えることもできます。
プログラミングコード上では、今回学習した定数や変数だけでなく、メソッドやクラス、構造体(struct)等でも同様にスコープ範囲が存在します。
実際の開発では、これらの要素を数十、数百と扱いながら開発を進めていきます。
各要素の影響範囲が広ければ広いほど、それらがお互い依存し影響する範囲は大きくなり、管理がしづらく複雑になっていくのがわかります。
各要素の用途に合わせて利用範囲を適切に狭めることで、お互いの依存範囲をできるだけ減らし、見通しを良くして要素を理解可能な状態で扱えるようにすることがとても重要です。
例えば前項目の定数messageは、利用範囲が狭められたローカルスコープでしたね。
コード上でmessageが利用されていたのはif文のブロック内だけでした。
つまり、if文のブロック内でだけ利用できるように要素を定義することで、適切に利用範囲を狭めることができるということです。
「実際に利用する範囲内で要素を宣言する」のがポイントです。
ローカルスコープとグローバルスコープ、どちらにも同じ定数名・変数名が存在していた場合どうなるのでしょうか?エラーが発生するのでしょうか?
答えとしては、「スコープは適用される優先度がある」ため、異なるスコープ範囲の中で同じ要素が利用されていてもエラーにはなりません。
さて、ここで一つ問題です。
グローバルスコープで定義された変数numberを用いて、for文(ループ文)の処理ブロック内で変数numberが何度も利用されているコード文があります。
右記に出力されているのがコードの実行結果なのですが、出力された各要素は、コード内のどの部分による出力結果によるものでしょうか?考えてみてください。
…どうでしょうか、答えは導き出せたでしょうか?
では、解答を見てみましょう。
少し複雑な処理の流れで初めはわかりにくいかもしれませんが、各スコープのどの範囲が優先して実行されているのか、じっくり処理を追ってみてください。
コードを見るとfor文の中にさらにfor文が書かれており、入れ子状態となっているのがわかりますね。ブロックが入れ子になればなるほど、スコープ範囲は狭くなり、適用される優先順位は高くなります。
「スコープの狭い変数が優先される」というのをポイントとして押さえておきましょう。
Access Controlとは、定数・変数、メソッド、クラス、構造体などに特定のアクセスレベルを割り当てることができる機能のことを指します。アクセス拡張子とも呼ばれます。
Swiftには、五段階のAccess Controlがあります。下に行けば行くほど、アクセスできるスコープは小さくなっていきます。
各段階の特徴を見ていきましょう。
open(オープン)
Access Controlのなかで、最もアクセス範囲が広いスコープ設定です。
モジュール内外の全てのアクセス(読み書き)が可能であり、継承やオーバーライドも可能です。
public(パブリック)
Access Controlのなかで2番目に広いスコープ設定です。
「open」との違いとして、継承やオーバーライドはできません。
internal(インターナル)
Access Controlのなかで3番目に広いスコープ設定です。
同一のモジュール内でのみアクセス(読み書き)を許可します。
要素にAccess Controlを設定しなかった場合、デフォルトでこの「internal」が設定されます。
fileprivate(ファイルプライベート)
Access Controlのなかで4段階目のスコープ設定です。
同ファイル内でのみアクセス(読み書き)が可能です。
private(プライべート)
Access Controlのなかで最も制限的なスコープ設定です。
定義されたスコープ内のアクセス(読み書き)のみ可能です。
ここまで五段階のAccess Control(アクセスコントロール)をご紹介しました。
では、これらはどのようにして使い分ければ良いのでしょうか?
答えとしては、前項目に説明したスコープ範囲指定におけるポイントと同様に、Access Controlもできるだけ狭い範囲で設定をしてください。
影響範囲を狭めることで、バグの原因を予防することにもつながり、またバグが発生した時に原因が特定しやすくなります。
まずは一番狭い範囲「private(プライベート)」から設定し、必要があれば徐々に広げていくようにしましょう。
以上、Swift言語の基礎文法スコープ(変数の有効範囲)の解説でした。
・スコープは小さく設定して、シンプルな関係にする
・Access Controlは、段階的に必要に応じて広げていく
これらのことに注意して、変数の影響範囲をコントロールしていきましょう。
スコープの概念はプログラミングにおいて重要なものです。基本をしっかりと押さえて、自身のプログラミングに活かしていきましょう。
\ SHARE /
アプリ開発が学べる勉強会を開催中!
CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。
徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!
本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!
まずは、現役エンジニアに相談できる無料相談をご利用ください。
2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。