質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

6回答

3840閲覧

構造体(Swift)の型について

hirdd

総合スコア50

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

1グッド

1クリップ

投稿2017/01/10 08:43

swiftにおける構造体の型?について質問です。

現在、swiftでiOSアプリを開発していて、documents配下のファイルパスを取得しようとした際、以下のような記載をするかと思うのですが、

swift

1let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!

この.userDomainMaskって何者なのかと思い、⌘を押しながらクリックしてみたところ以下のようになっていました。
(fileManagerのExtensionに含まれている。)

swift

1public struct SearchPathDomainMask : OptionSet { 2 3 public init(rawValue: UInt) 4 5 6 public static var userDomainMask: FileManager.SearchPathDomainMask { get } // user's home directory --- place to install user's personal items (~) 7 8 public static var localDomainMask: FileManager.SearchPathDomainMask { get } // local to the current machine --- place to install items available to everyone on this machine (/Library) 9 10 public static var networkDomainMask: FileManager.SearchPathDomainMask { get } // publically available location in the local area network --- place to install items available on the network (/Network) 11 12 public static var systemDomainMask: FileManager.SearchPathDomainMask { get } // provided by Apple, unmodifiable (/System) 13 14 public static var allDomainsMask: FileManager.SearchPathDomainMask { get } // all domains: all of the above and future items 15 } 16

この定義が全く理解できません。
まず、SearchPathDomainMaskという構造体は、OptionSet型なのでしょうか??そして、public staticで宣言されている変数達の型はこの構造体自身の型になっている??
Google検索などしてみましたが、このような記載方法をしている例を上手く見つけられませんでした。。。
どなたかswiftの文法を説明頂ける方、ご教授いただけないでしょうか??
よろしくお願いいたします。

SatoTakeshiX👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

#構造体の文法の説明
public struct SearchPathDomainMask : OptionSet{}の文法的な意味は「構造体SearchPathDomainMaskはOptionSetというプロトコルを採用している」という意味になります。
Swiftでは、構造体(struct)や列挙型(enum)にもプロトコルを採用することができます。
Objective-Cではクラスしかプロトコルを採用できなかったのですが、それが拡張されました。

構造体でプロトコルを採用する文法は以下の通りです。構造体の型の名前の後ろに:(コロン)をつけて定義します。

struct <構造体の型の名前> :<プロトコル名>{ }

質問で書かれていた

SearchPathDomainMaskという構造体は、OptionSet型なのでしょうか??

ですが、クラスの定義とごっちゃになっていそうなので、クラスの定義もお伝えすると

class <クラスの型の名前>:<親クラス or プロトコル名>,<プロトコル名>{ }

というように、クラスの定義では:(コロン)の後は継承する親クラスか、採用するプロトコル名を指定して、その後「,(カンマ)」で区切ってプロトコル名を書いていきます。

クラスでは親クラスから継承できるんですが、構造体は継承ができないので:(コロン)の後は必ずプロトコルになります。


#OptionSetって何?
じゃあ、OptionSetプロトコルってどんな時に使うんじゃいという話になりますが、OptionSetにカーソル当てて⌘+クリックして定義を見るとどうやらビットマスクを行うためのプロトコルになるそうです。ちょっと抜粋します。

A type that presents a mathematical set interface to a bit mask.

You use the OptionSet protocol to represent bit mask types, where
individual bits represent members of the set. Adopting this protocol in
your custom types lets you perform set-related operations such as
membership tests, unions, and intersections on those types. What's more,
when implemented using specific criteria, adoption of this protocol
requires no extra work on your part.

*意訳:OptionSetは数学上の集合を表現する型です。OptionSetプロトコルを使用して、ビットマスクタイプを表し、それらを集合として表現できます。このプロトコルを採用することで論理和や論理積などの計算が楽に表現できます。

Swift2ですがOptionSetについて説明した記事があったので紹介します。

Swift 2.0 の新しいOption、OptionSetType
http://www.toyship.org/archives/2208

どうやら、iOSのアプリのフォルダ指定をビットマスクによって管理しているようです。
ビット自体の説明は以下の記事がわかりやすいです。

ビットをどう使うかよく判らない人へ
http://qiita.com/satoshinew/items/566bf91707b5371b62b6

userDomainMask等の型指定について

私も初めてこの形をみましたし、⌘+クリックの定義ファイルでは具体的な実装まではわからないのでなんとも言えないですが、

public static var userDomainMask: FileManager.SearchPathDomainMask { get }

と書いた時に結局userDomainMaskプロパティってOptionSetプロトコルを採用してることになるんですよね。

「構造体SearchPathDomainMaskのプロパティ(userDomainMaskなど)に自身の型を指定しているのか?」が何故なのかですが、予想するに、FileManagerのextensionでプロパティを増やしたいからだと思います。

extension(拡張)では保持型のプロパティの追加はできないです。
ですが、ネストする形でまず構造体(ここではSearchPathDomainMask)を定義して、その構造体のプロパティを追加するとextensionでもプロパティを追加できます。

今Playgroundで確認しました。

swift

1class A {} 2extension A { 3 struct StructA { 4 static var nestValue : A.StructA { 5 get { 6 return A.StructA() 7 } 8 } 9 } 10 //var addNumber :Int ->保持型のプロパティをextensinoでは追加できない!! 11} 12 13A.StructA.nestValue//A.StructA型になる 14

上記のコードを実行するとA.StructA.nestValueA.StructA型になります。

extensionってコードの可読性を上げるために、追加の機能は全てextensionで書かれることがあり、Foundationライブラリーはその書き方にならっているので、こんな書き方になっていると予想します。


【追記】 iOSのアプリのフォルダ指定をビットマスクによって管理するとはどうゆうことなのか?

ビットをどう使うかよく判らない人へ
ではビットフラグを作り、フラグを立てることで変数に複数の状態を表すことができると書かれています。
それを今回のSearchPathDomainMask例で提示します。

突然ですが、今すぐXcodeで言語をObjective-Cで新しいプロジェクトを作成して、ViewControllerのviewDidloadで以下のコードを書いてください

objective

1- (void)viewDidLoad { 2 [super viewDidLoad]; 3 //キャッシュディレクトリを取得する 4 NSArray *cachesArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSLocalDomainMask | NSUserDomainMask | NSNetworkDomainMask | NSSystemDomainMask , YES); 5 NSLog(@"キャッシュディレクトリ配列:%@", cachesArray); 6} 7

NSSearchPathForDirectoriesInDomains関数の第一引数には検索するディレクトリを入れます。
今回はNSCachesDirectoryを指定してキャッシュディレクトリを取得します。
第二引数はドメインマスクを指定します。今回はNSLocalDomainMask | NSUserDomainMask | NSNetworkDomainMask | NSSystemDomainMaskとor演算をして、全て選択します。

これを実行するとこんな結果が得られると思います。

Objectie

1キャッシュディレクトリ配列:( 2 "/Users/user/Library/Developer/CoreSimulator/Devices/C89BA657-CB47-4142-ADAD-0278E32047CF/data/Containers/Data/Application/D0584736-5F37-4A87-87E4-83F29064D0E9/Library/Caches", 3 "/Library/Caches", 4 "/System/Library/Caches" 5)

NSLocalDomainMaskとして"/Users/user/Library/Developer/CoreSimulator/Devices/C89BA657-CB47-4142-ADAD-0278E32047CF/data/Containers/Data/Application/D0584736-5F37-4A87-87E4-83F29064D0E9/Library/Caches"
NSUserDomainMaskとして"/Library/Caches"
NSSystemDomainMaskとして"/System/Library/Caches"
の3つのディレクトリが取得できました。

さて、先程のソースのNSUserDomainMaskにカーソル当てて⌘クリックするとこんな定義に飛びます。

Objectie

1//NSPathUtilities.h 2typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) { 3 NSUserDomainMask = 1, // user's home directory --- place to install user's personal items (~) 4 NSLocalDomainMask = 2, // local to the current machine --- place to install items available to everyone on this machine (/Library) 5 NSNetworkDomainMask = 4, // publically available location in the local area network --- place to install items available on the network (/Network) 6 NSSystemDomainMask = 8, // provided by Apple, unmodifiable (/System) 7 NSAllDomainsMask = 0x0ffff // all domains: all of the above and future items 8}; 9

NS_OPTIONS修飾子はObjective-CとSwiftを橋渡しする修飾子で、文法的にはC言語のenumになります。
(Objective-Cという言語は[]で囲われていない部分はC言語の文法で動く言語です。)
C言語のenumは数値に名前をつけられるものです。上記のソースでは「1という数値にNSUserDomainMaskという名前を2という数値にNSLocalDomainMaskを...以下略」としています。
なぜ2の階乗ごとの数値になっているかというと2進数で表した時に都合がいいからです。
この数値をビットマスクすることで複数のディレクトリを選択することができます。
例えば、最終的なNSSearchPathDomainMaskの値が2進数で

1001(イチゼロゼロイチ)

ならばNSUserDomainMask(2進法で1桁目。10進法で1の数字)とNSSystemDomainMask(2進法で4桁目。10進法で8の数値)が選択されていることがわかります。

2進数で各位に1があればそれが選択されているということが分かるのがビット演算で、NSSearchPathDomainMaskはそのようにしてディレクトリを選択しています。

因みに以下のように数値を直に記入しても同じ結果が得られると思います。C言語のenumは数値を置き換えたものだからです。

Objectie

1 //キャッシュディレクトリを取得する 2 NSArray *cachesArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, 1 | 2 | 4 | 8 , YES);//第二引数を1 | 2 | 4 | 8 にした。 3 NSLog(@"キャッシュディレクトリ配列:%@", cachesArray); 4

話をSwiftに戻します。
Swift2からOptionSetType(Swift3でOptionSetに改名)が追加されてこのビットマスク操作をインスタンスのメソッドによる操作でできるようになりました。

上記のObjective-CのコードをSwiftに置き換えてみます。

swift

1 var useMask = FileManager.SearchPathDomainMask.localDomainMask 2 useMask.insert(.userDomainMask) 3 useMask.insert(.networkDomainMask) 4 useMask.insert(.systemDomainMask) 5 6 let cachesArray = NSSearchPathForDirectoriesInDomains(.cachesDirectory, useMask, true) 7 print(cachesArray) 8

FileManager.SearchPathDomainMask.localDomainMaskで変数useMaskを作成し、insertメソッドで追加をしています。ビット演算は使っていません。
ビット演算を直接やるかOptionSetを採用してインスタンスによるメソッド操作に書き方にするかどちらがいいのかは議論があるところですが、少なくともAppleのFoundation開発者は「インスタンスによるメソッド操作に書き方」がいいと思ってOptionSetで定義しているのだと思います。

SearchPathDomainMaskとは関係ないので完全に余談ですが、Swift3から「Swift2までC言語の関数をブリッチして呼び出していたものをインスタンスのメソッドによる操作に置き換える」方向でアップデートされました。
「インスタンスによるメソッド操作に書き方」の方が受け入れられつつあります。

[iOS][Swift] Swift 3.0の変更点まとめ
http://dev.classmethod.jp/smartphone/iphone/implemented_proposals_for_swift3/
のSE-0044: Import as Memberを参照してください。

投稿2017/01/10 15:56

編集2017/01/14 03:54
SatoTakeshiX

総合スコア113

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirdd

2017/01/13 11:07

とても丁寧な説明に、感動してしまいました。ありがとうございます。 まず、”構造体の文法の説明”ですが、おっしゃる通り、class,構造体の定義をよく理解できていませんでした。おかげさまで、整理して理解する事が出来ました。 次に"OptionSetって何?”ですが、ビットマスクについてリンク先を参照した事で概要は理解する事が出来ました。しかし、"iOSのアプリのフォルダ指定をビットマスクによって管理する"とはどういう状態なのかよくイメージ出来ないのですが、簡単な説明を頂けると嬉しいです。 最後に、"userDomainMask等の型指定について"ですが、"予想するに、FileManagerのextensionでプロパティを増やしたいからだと思います"以降がよく理解出来ませんでした。ただ、プロパティを追加するだけであれば、structを使わずともextentionの中にそのままcomputed propertyを追加すれば、それで良いように思ってしまいました。そして自身の型を指定している理由も、いまいち理解しきれませんでした。記載頂いた例でA.structA型になると、例えば何の利点がありますか??教えて頂けると嬉しいです。
SatoTakeshiX

2017/01/14 01:27

"iOSのアプリのフォルダ指定をビットマスクによって管理する"について追記で補足しました。 "userDomainMask等の型指定について"の捕捉を追記予定です。もう少々お待ち下さい。
SatoTakeshiX

2017/01/14 03:34

"userDomainMask等の型指定について"にて、extentionのプロパティ追加方法での疑問を別回答で追記しました。(わかりやすいように書いていたら文字数制限を超えてしまいました。ごめんなさい。)ご確認ください!
guest

0

回答が文字数制限を超えてしまったため、別枠で回答します。ごめんなさい
#【追記】なぜextensinoでプロパティ追加にわざわざ構造体SearchPathDomainMaskを定義したのか?

Foundationの開発者がなぜわざわざpublic struct SearchPathDomainMask : OptionSet{}とFileManagerクラスに構造体SearchPathDomainMaskをネストする形で定義したのかですが、
「可読性を上げる」が考えられると思います。

@hirddさんがおっしゃるように、extensinoではコンピュートプロパティでプロパティは追加できるので、以下のようにuserDomainMaskなどを定義することができます。

swift

1class AltFileManager {}//自分で定義したのでAltFileManagerと名前をつけました。 2extension AltFileManager {//exitensinでプロパティを追加 3 struct SearchPathDomainMask :OptionSet { 4 let rawValue: UInt 5 } 6 //exitensinでプロパティを追加 7 static var userDomainMask : SearchPathDomainMask { 8 return SearchPathDomainMask(rawValue: 1 << 0) 9 } 10 //exitensinでプロパティを追加 11 static var localDomainMask : SearchPathDomainMask { 12 return SearchPathDomainMask(rawValue: 1 << 1) 13 } 14 //exitensinでプロパティを追加 15 static var networkDomainMask : SearchPathDomainMask { 16 return SearchPathDomainMask(rawValue: 1 << 2) 17 } 18 //exitensinでプロパティを追加 19 static var systemDomainMask : SearchPathDomainMask { 20 return SearchPathDomainMask(rawValue: 1 << 3) 21 } 22} 23//使用例 24var loalDomain = AltFileManager.localDomainMask 25//追加もできる 26loalDomain.insert(AltFileManager.networkDomainMask) 27

今回は、AltFileManagerというクラスに構造体でSearchPathDomainMaskをOptionSetを採用する形で定義し、コンピュートプロパティとしてuserDomainMask、localDomainMask、networkDomainMask、systemDomainMaskを定義しました。
使用する場合は、AltFileManager.localDomainMaskなどで各ドメインマスクを取得できます。
コンパイルも通ります。

動いてるならいいじゃないかと思いますが、ここはFoundationというiOSの根幹をなすライブラリーです。
利用する開発者が迷わないように定義するのが重要になってきます。

その考えて行くと、ドメインマスクがAltFileManager.localDomainMaskとFileManagerの直下にバラバラにドメインマスクがあるのは読みにくいです。
なぜなら、userDomainMask、localDomainMask、networkDomainMask、systemDomainMaskは4つで1つのビット演算をしようとしているからです。

ビットマスクで「2進数で4桁の数値を設定して1がある位を選択状態とみなす」と考えた時にバラバラのプロパティを定義して「userDomainMaskは2進数の1桁目を表します。localDomainMaskは2進数の2桁目を表します」とドキュメントに書くよりは、「SearchPathDomainMaskという構造体を定義し範囲の中でlocalDomainMask、userDomainMaskを定義」するほうがSearchPathDomainMaskというグループに区切ることができるので読みやすくなります。

userDomainMask、localDomainMask、networkDomainMask、systemDomainMaskは意味的にドメインマスクという同じグループに属しているので、それをまとめるSearchPathDomainMask構造体を定義しているのだと思います。

因みにObjective-Cの定義をもう一度確認しても、NSSearchPathDomainMaskの下に各ドメインマスクがあるという定義になっています。
NSSearchPathDomainMaskが各ドメインをまとめるものとして表現されています。

Objectie

1//NSPathUtilities.h 2typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) { 3 NSUserDomainMask = 1, // user's home directory --- place to install user's personal items (~) 4 NSLocalDomainMask = 2, // local to the current machine --- place to install items available to everyone on this machine (/Library) 5 NSNetworkDomainMask = 4, // publically available location in the local area network --- place to install items available on the network (/Network) 6 NSSystemDomainMask = 8, // provided by Apple, unmodifiable (/System) 7 NSAllDomainsMask = 0x0ffff // all domains: all of the above and future items 8}; 9

SwiftではSwift2までObjective-Cの定義をブリッチしていましたがSwift3では、ブリッチの方法を見直し、よりSwiftらしい書き方ができるように変更されています。

ということでSwift2での定義も確認してみました。

swift2

1public struct NSSearchPathDomainMask : OptionSetType { 2 public init(rawValue: UInt) 3 4 public static var UserDomainMask: NSSearchPathDomainMask { get } // user's home directory --- place to install user's personal items (~) 5 public static var LocalDomainMask: NSSearchPathDomainMask { get } // local to the current machine --- place to install items available to everyone on this machine (/Library) 6 public static var NetworkDomainMask: NSSearchPathDomainMask { get } // publically available location in the local area network --- place to install items available on the network (/Network) 7 public static var SystemDomainMask: NSSearchPathDomainMask { get } // provided by Apple, unmodifiable (/System) 8 public static var AllDomainsMask: NSSearchPathDomainMask { get } // all domains: all of the above and future items 9}

Swift2ではpublic struct NSSearchPathDomainMask : OptionSetTypeとグローバルな構造体としてNSSearchPathDomainMask(swift3でSearchPathDomainMaskに改名)が定義されてました。(どこにもextensionされていないグローバルな構造体です。)

NSSearchPathDomainMaskの下にUserDomainMaskなどの各ドメインマスクが定義されてます。
NSSearchPathDomainMask > UserDomainMaskのツリー構造になっています。

Swift3でよりよくリファクタリングしようとしたときにこのツリー構造は壊したくないです。

そしてどのように変更されたのかを見ると

FileManager > SearchPathDomainMask > UserDomainMask

というように、FileManagerをトップとしてその下にSearchPathDomainMask、その下にUserDomainMaskとする構造に変更されました。

考えてみれば、SearchPathDomainMaskはファイルを扱う時に使用するので、グローバルで定義するよりFileManagerの下にあった方がわかりやすいです。

ここで質問の答えをしたいと思います。
「extensionでプロパティを増やしたいならなぜわざわざ構造体を定義したのか?コンピュートプロパティなど他のやり方があるのになぜ?」という質問には
「昔からSearchPathDomainMask > UserDomainMaskのツリー構造でObjcもSwift2も定義されていたので、バラバラに定義するのは不合理。Swift3でSearchPathDomainMaskがFileManagerの配下になり、FileManager > SearchPathDomainMask > UserDomainMaskというツリー構造になった。わざわざ構造体を定義した理由はFileManagerというファイルを扱うものの下にSearchPathDomainMaskを定義することで、構造を明確化し可読性を上げるため」
とお答えします。

#追記 OptionSetの使い方
公式サイトにoptionsetの使い方が書かれていました。
https://developer.apple.com/reference/swift/optionset

ECサイトなどで購入者の商品配送方法をオプションセットで表す例です。

swift

1struct ShippingOptions: OptionSet { 2 let rawValue: Int 3 4 static let nextDay = ShippingOptions(rawValue: 1 << 0)//次の日 5 static let secondDay = ShippingOptions(rawValue: 1 << 1)//二日後 6 static let priority = ShippingOptions(rawValue: 1 << 2)//優先的に 7 static let standard = ShippingOptions(rawValue: 1 << 3)//標準 8 9 static let express: ShippingOptions = [.nextDay, .secondDay]//特急オプション 10 static let all: ShippingOptions = [.express, .priority, .standard]//全部入り 11}

nextDayなどオプションをstaticで保持して、型ShippingOptionsになっているところ、SearchPathDomainMaskと同じですね。プロパティが自身の型になっているところが。staticで型プロパティにしているからできるんだと思います。
OptionSetはこんなふうに使うんですね。
私も勉強になりました。

参考にしてください!

投稿2017/01/14 03:32

編集2017/01/14 06:45
SatoTakeshiX

総合スコア113

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こちらのリンク内の記事を読むことをお勧めしますが、読んでばかりいると、プログラムソースをかけなくなるので、
まず動かしてみることも必要かと思いました。

Swift2でのQita参考記事

Github_Xcode8.21でのソース

参考記事Swift3

プログラムを読む、プログラムを考える、プログラムを動かす、バランスですね。

投稿2017/01/13 13:01

Dbank

総合スコア120

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

プロトコルはそのままだと思いますが、
Int32の値は4バイトですから、最小値のバイト数を求める場合はInt8の1バイトだと思います。
public staticで宣言されている変数達の型はこの構造体自身の型になっている??
ことに関して、モデルの構造体ではこのようにIntを宣言して、rawValueを使用しています。
この宣言を自分の用途に応じて対応すればよろしいかと。

let rawValue: Int32のような形で。

struct ShippingOptions: OptionSet { → let rawValue: Int → let rawValue: Int32 にする。 static let nextDay = ShippingOptions(rawValue: 1 << 0) static let secondDay = ShippingOptions(rawValue: 1 << 1) static let priority = ShippingOptions(rawValue: 1 << 2) static let standard = ShippingOptions(rawValue: 1 << 3) static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] }

投稿2017/01/10 11:10

Dbank

総合スコア120

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirdd

2017/01/13 11:13 編集

コメントありがとうございます。 すみません、勉強不足で説明についていけませんでした。今回質問のuserDomainMaskとどう対応するのか初心者向けに説明頂けると嬉しいです。
guest

0

まず、SearchPathDomainMaskという構造体は、OptionSet型なのでしょうか??

プロトコルの実装です。

そして、public staticで宣言されている変数達の型はこの構造体自身の型になっている??

それが何か?
例えばInt32型の最小値:minプロパティはやっぱりInt32型です。

投稿2017/01/10 11:01

編集2017/01/10 11:01
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

日本語訳の Appleリファレンスにかなり詳しい詳細が記載されています。
Appleリファレンス

投稿2017/01/10 10:22

編集2017/01/10 10:23
Dbank

総合スコア120

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirdd

2017/01/13 11:09

リンクを頂きありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問