Posted by あしたさぬき.JP at ◆

 

この広告、メッセージは90日以上更新のないブログに表示されています。
新しい記事を書く事でこのメッセージが消せます。
  

2014年12月13日
Posted by 屋台ブルー at ◆ 2014年12月13日00:00 Comment(0)

Swiftで遊ぼう! - 155 - アプリを作ろう - 4 weak...

@IBActionの説明に入ろうと思っていましたが、「weak」の装飾子の理解も少しすすめないといけない。ちょっと以前のブログエントリーを振り返ってみる。

weakが出れば、unownedってキーワードと一緒に理解しなといけません。これらのキーワードはARC(Auto Reference Counting)の所で使われるから、ARCのエントリーを見てみよう。

Swiftで遊ぼう! - 51 また話は飛んで今日からARC
Swiftで遊ぼう! - 53 そして問題ふくみのARC
Swiftで遊ぼう! - 54 ARCの問題解決?
Swiftで遊ぼう! - 54 まだまだ続くARC
Swiftで遊ぼう! - 55 ARCの理解にも少々時間がかかります

この頃はかなり理解に苦しんでいました。ネストされたクラスインスタンスが消滅するときのメモリーリークの理解はできてもThe Swift Programming Language本の説明で使われているPersonクラスとApartmentクラスの話が抽象的で、実際のコーディングでどう利用するのかさっぱり想像できませんでした。

しかし、UIKitフレームワークを扱うことになり、「あ、実際のコーディングではこういうことだったのか」と妙に納得できました。まさに、UIButtonクラスとViewControllerクラスのネストされた関係が物語っていますね。

Main.storyboardにあるUIButtonクラスのインスタンス本体と、ViewControllerクラスインスタンスに存在するUIButtonクラスのプロパティは入れ子状態、その上、UIButtonプロパティは「nil」を取り得るオプショナル型なので、weakリファレンスとすべきというルールがありますね。nilを取り得ない場合は「unowned」リファレンスです。

ということで、メモリーリークを防ぐために「weak」を付けているということでしょう。

今日はここまで。
  


2014年09月05日
Posted by 屋台ブルー at ◆ 2014年09月05日00:00 Comment(0)

Swiftで遊ぼう! - 56 強力な循環参照を切る!第3弾!

swift_logo何度読み返したか... 何度読み返しても理解したと断言できない。プロパティが相互にかみ合った複雑な状況をそのまま理解できるほど、私の頭が柔軟じゃないことを思い知らされた。先行きに暗雲が垂れ込めてきた。まさにSwiftの勉強を続けらえるかどうかの瀬戸際に立たされている。時間をかけて理解しようとすべきか、更に先に進むべきか、悩むところだが、経験上、先に進んだ方がいいのだろう。ストロング・リファレンス・サイクルの回避法第3弾をもう少しだけ考えてから次に進もう。

class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}

教科書の説明をもう一度ちゃんと読もう。
「両者のプロパティが常に値を保持していて、初期化が一旦終了すると、「nil」を持つことは決してない互いのプロパティを保持するクラスの場合、一方のクラスに、「unowned」プロパティ、そして他方に「implicitly unwrapped optional」プロパティを組み合わせて利用する」、という説明を忠実に頭に入れるしかないだろう。

メインに使うクラスに、「implicitly unwrapped optional」プロパティを設定する。これはオプショナルタイプをアンラップするキーワードの「!」を「?」無しで使用する。強制的に中身をさらけ出すという働きがあり、イニシャライズが済んだ後に「nil」だった場合はランタイムエラーになるが、イニシャライズされるまで「nil」が割りふられている不思議な使い方をする。ようするにインスタンスを生成するまで「nil」が入っていると考えればいい。しかし、一旦インスタンスが生成されると、「nil」にはなりえないので、一般的なオプショナルな働き、Boolean型の扱いはできないということでしょう。更に言えば、インスタンス生成後のオプショナルな扱い(常に「!」をつけた振る舞い)はいらないということだろう。←説明がくどいけど、私にはなかなか理解できないんです。

var country = Country(name: "Japan", capitalName: "Tokyo")

上記のようにクラス型Countryのインスタンスcountryの生成宣言があったときの流れを考えてみよう。String型のnameが宣言され初期化が必要なことが示唆される。次にクラスCityのインスタンスcapitalCityが宣言されるが、これは「implicitly unwrapped optional」なので、取りあえず、初期化が終わるまで「nil 」が代入される。そして、次に初期化ステップのinit(){}に流れがすすむ。このクラス生成の引数に2つのString型が必要なことが理解できる。country宣言のところに、引数としてname: "Japan"とcapitalName: "Tokyo"が渡されている。nameはそのまま、countryのString型プロパティであるself.nameに渡されて、定数のnameの初期化は終了する。

次にこのイニシャライザーの中で、自分自身の持っているクラス型Cityのインスタンスに値を加えようとしている(実はこのステップ、capitalCityの初期化では無いということが重要!)。capitalCityが「implicitly unwrapped optional」のプロパティなので、既に「nil」が代入された形で存在しているんです。だから、このイニシャライザーの中で、selfを使って呼ぶことができるんです。もし「!」がついていなかったら、ここでエラーが生じます。ここで、「nil」の代わりに、City(name: capitalName, country: self)を使って具体的な値を持ったインスタンスに替えてやるんです。そして、この情報を下のクラス型Cityに渡されます。重要なのが、渡される情報から作られるインスタンスは、クラス型Countryのインスタンスの中だけで生きるということ。なので、クラス型Cityは、「unowned」プロパティを持たなければならないということですね。

class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}

そして、ここに渡ってきたcapitalNameでCity型インスタンス(名前無し)のプロパティnameを初期化して、Countryクラスはcountryと共通になるということで、最終的にクラス型Countryのインスタンスcountryの初期化ステップが終了ということですよね。こうすれば、確実にメモリーリークは回避できると。

完全納得!、といきたいところですが、
イマイチ理解できていない(T_T)  


2014年09月04日
Posted by 屋台ブルー at ◆ 2014年09月04日00:00 Comment(0)

Swiftで遊ぼう! - 55 ARCの理解にも少々時間がかかります

swift_logoまだメモリー解放させるARCの挙動に関して理解が足りない。前回話したPersonとApartmentの関係性は、「nil」をとりえるApartmentのプロパティtenentをweakレファレンスにすることで解決している。

「nil」になることがあり得ないプロパティの場合は、unownedリファレンスを使うという説明がされている。CustomerとCreditCardの関係で説明されている。

class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { println("\(name) is being deinitialized")}
}

class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { println("Card #\(number) is being deinitialized") }
}

Customerクラスはオプショナルなプロパティを持っているので、インスタンス生成時にオプショナル宣言をして使用する。しかし、CreditCardクラスは「nil」をとらない実体プロパティなので参照するときは実体をそのまま呼ぶということになる。

var yuji: Customer?
yuji = Customer(name: "Yuji Tai")

オプショナルなインスタンス生成は上記のようにする。これでnameにYuji Taiが代入される。これを利用するときは強制アンラップのキーワード「!」を使って、yuji!.nameとすれば、”Yuji Tai"が使える。当然、この段階で、yuji!.cardには「nil」が代入されている。

cardはCreditCardクラスのインスタンスなんで、このcardに実体を代入するのと同時に初期化しないといけませんよね。ということは以下のように

yuji!card = CreditCard(number: 1234_4565_7890_1237, customer: yuji!)

こうしてやれば、
yuji!.name
yuji!.card
というふうにして、ビックリマーク付きではあるが、それぞれのインスタンスが使用できる。

まあ、ここまでは何とか理解できる範囲であるが、ストロング・リファレンス・サイクルの回避法、第三弾!は私の理解力を遙かに超えることに!
単純明快に考えれば、このビックリマーク「!」無しで、オプショナルな形を作ること無く利用でき、ストロング・リファレンス・サイクルも回避するやり方を明日じっくり考えましょう。(^_^;)  


2014年09月03日
Posted by 屋台ブルー at ◆ 2014年09月03日00:00 Comment(0)

Swiftで遊ぼう! - 54 まだまだ続くARC

swift_logoデイニシャライザーが動いているのかどうか確認できないが、ストロング・リファレンス・サイクルに陥るとメモリーリークになるという考え方は理解できた。

じゃあこれを回避するためにどうすべきか?

デベロッパー連中がfacebookで話題にしている「Swiftの循環参照問題におけるunownedとweakの使い分けについて」で解説もあるが、「weakリファレンス」もしくは「unownedリファレンス」の使い分けということになる。

どう選択すべきかは、教科書に示されていて、「リファレンス値がnilを取る可能性があればweakリファレンスを選択して、nilになり得ず値をとり続ける場合はunqownedリファレンスにしろ」という単純明快な判断基準があるけど、、ベテランデベロッパーになればなる程この選択で悩むようだ。当然私にはわかりません(^^;)

昨日ストロング・リファレンス・サイクルの例文を考えてみよう。クラスPersonとクラスApartmentのプロパティを考えてみると、Apartmentのプロパティのtenantは必ずしも誰かがいるという訳ではないので、weakリファレンスの対象としていいだろう。ということで、

class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}

class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}

としていいのだろう。でも、これはクラスAparatmenの変数プロパティのtenantにweakというキーワードがついただけ、これでストロング・リファレンス・サイクルから回報されたかどうか確認する方法がないところに問題があると思う。メモリーリークに陥ったかどうか判断することができないのはどうしてなんでしょうね?  


2014年09月02日
Posted by 屋台ブルー at ◆ 2014年09月02日00:00 Comment(0)

Swiftで遊ぼう! - 54 ARCの問題解決?

swift_logoまだ、ここのブログを毎日チェックしている人はいないと思うので気づかれていないかもしれないが、ここは午前0時に更新されている。そうです。1週間ぐらいの記事を前もって書いているので、完全リアルタイムじゃないが、たまにはタイムラグ無しで話したいから、途中で新しい記事を差し込んでいる。今日のトピックはそれだ。

私がこのブログで展開しているプロジェクトは、頭が固くなったオヤジが一人前のSwiftプログラマになれるかどうかの実験である。ここで重要なのは「一人前」という言葉。私が解釈する「一人前」とは、人の書いたプログラムを読める能力だけではない。自分のアイディアをコーディングできる技術のこと。この技術を体得するために文法書にあたる教科書The Swift Programming Languageだけでは不十分だ。一般的なプログラミングの勉強がいる。こうして「一人前のSwift使い」になれたら、その経緯を書籍化もしたい。ブログを書き続けることでモチベーションを維持、そして復習で記憶の定着、さらに修正をすることで、問題点の再評価、これらを最後にまとめれば本ができるという流れだ。しかし、理解力の落ちた頭脳で続けるのはかなり辛い。若い頭の皆さん、プログラミング技術って英語がしゃべれるのと同じように必須能力になってるよ。今のうちにドップリと浸かって物にしてください。

さて、今日の記事は、昨日まで、疑問に思っていたplayground上で、deinitが動かない理由が分かりました。最近、stackoverflowというプログラミングの疑問に答えてくれるサービスに参加したのですが、そこで同様の質問をされていました。

Deinit method is never called - Swift playground
XcodeのPlaygroundsは、一般的なアプリのように常に動き続けているプログラムではないので、オブジェクトが作られるときにメモリーは確保され、コードに変更が加わるとメモリーチェックが生じる。この変更が加わったところでPlayground全体が再評価がされているため、これが生じた時、それまでに得られた結果はすべて破棄され、すべてのオブジェクトのメモリー割り当ても消える。このステップの出力を目で見て確認することができない。Playgroundsはメモリーマネージメントに関するテストをするツールとして不向きである。

この状況はβ6で確認されました。また今後リリースされる正式版で変更されるかもしれません。またその時は確認作業をします。  


2014年09月01日
Posted by 屋台ブルー at ◆ 2014年09月01日00:00 Comment(0)

Swiftで遊ぼう! - 53 そして問題ふくみのARC

swift_logo2014年9月2日追加:Xcode6β6のplaygroundsではデイニシャライザーのテストはうまくできない→「Swiftで遊ぼう! - 54 ARCの問題解決?
とうとう9月になってしまった。毎日ちょっとずつ勉強して夏休みの間にSwiftをマスターするぞ! なんて思っていましたが、現実はあまい(^^;) 全くの素人の50オヤジは、闇の中を彷徨っています。

5歳の娘が取り組んでいる公文算数のお手伝いを日課にしているけど、足し算、引き算、かけ算とゆっくり進むスピードは、自分のプログラミングの理解するそれと同じような気がする。娘が微分積分を理解する日は... やっぱりプログラミングも10年計画で取り組まないと甘いかな... なんて考えながら、今日のARCのステップをもう少し深く掘り下げていく。

ARCに問題が生じるのは、継承されるクラスが絡みあう時。2つのクラスのプロパティにそれぞれのクラスが呼ばれている時がややこしい。

class Person {
let name: String
init(name: String) {
self.name = name
}
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}

class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}

こういう2つのクラスを宣言する時、お互いが互いをオプショナルなプロパティとして作っている。こいういう場合、インスタンスを生成するときに、
var yuji: Person?
yuji = Person(name: "Yuji Tai")
てな感じで、オプショナルなインスタンス宣言をしなければならないのでしょうか?
当然、以下のように
var yuji = Person(name: "Yuji Tai")
上のようにしても問題なくインスタンス生成はできるけど、どうなんでしょうか?
オプショナル宣言でなければならない理由があるのでしょうか?

まあ、例文の次に進むとオプショナルでなければならないと思うがストロングリファレンスを作るという意味でオプショナルでなければならないんでしょうか? しかし、ストロングリファレンスサイクルを作りあげるためにオプショナルで宣言しないと、nilによるデイニシャライズが生じないので、やっぱりオプショナルにしないといけないんでしょうね。

この例文から考えると、ARCの効果がなくなるクラス宣言はオプショナル宣言したインスタンス作成というのが必要なんでしょう。

var number23: Apartment?
number23 = Apartment(number: 23)

yuji!.apartment = number23
number23!.tenant = yuji

yuji = nil
number23 = nil

しかしながら、このnilの代入を試みても、デイニシャライズが生じてないように思えます。これは昨日エントリーと同じ問題に思えるけど、何がいけないんでしょう。これでストロング・リファレンス・サイクルによるメモリーリークが生じているんでしょうか?

疑問だ。  


2014年08月31日
Posted by 屋台ブルー at ◆ 2014年08月31日00:00 Comment(0)

Swiftで遊ぼう! - 52 デ・イニシャライザー? なぜ動かない?

swift_logo2014年9月2日追加:Xcode6β6のplaygroundsではデイニシャライザーのテストはうまくできない→「Swiftで遊ぼう! - 54 ARCの問題解決?
ARCの勉強の前に、もう一つ勉強し忘れていた項目があった。デ・イニシャライザーだ。クラスはイニシャライズーによって初期化されるとメモリーは割りふられ、必要なくなると勝手にARCが働いてメモリーは解放されるそうですが、開放されない状況もあり、マニュアルでメモリーを解放する方法が用意されている。それがデイニシャライザーだ。

し・か・し! このデイニシャライザー、動かないんです。考えても理由が分からない!

ストラクチャー型のBankですが、プロパティは「static」付きで宣言されている。staticって、グローバルに使える定数、変数、関数などを生み出すことができるんですよね。その場合、staticを使ったその行に具体的な「値」の代入←初期化?をしないといけないようです。それから、static変数があると、インスタンス生成はなくなる。宣言そのものがグローバルな働きをするってことになるから、複数のインスタンスを持つと変です。ゲームで使うストラクチャ型のBankは当然、1つだけで十分、プレーヤーが何人でもBankは1つということで、staticを宣言ということでしょう。staticの使い方の1つですね。

struct Bank {
static var coinsInBank = 10_000
さて、グローバルに使える変数「coinsInBank」を宣言しているが、この場合、必ずここで初期化をしなきゃいけないみたいです。←本当でしょうか?
例えば、次のイニシャライズステップに組み込んで初期化できるのか実験してみましたができませんでした。
static var coinsInBank: Int
init() {
coinsInBank = 10_000
}
上記はエラーになりますよ。グローバルな値なので、インスタンス生成時に働くイニシャライザーの出番が無いですよね。無いということは、イニシャライズされないのでエラーになるんでしょうか?

static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
実は関数「vendCoins」も少し理解するのに時間がかかった。どうしてかというと、「min(A,B)」というメソッド?が教科書に載ってなかったからです。同じタイプの数字を2つ比べて小さい方を返すメソッドなんですが、じゃあminの逆で大きい方を返すのは「max」? 
この関数は、金額を指定して銀行から借りるメソッドです。指定した金額が銀行にあれば(coinsInBankの数が大きければ)、そのお金がそのまま手に入れられるが、銀行のお金が少なければ、銀行に残っている残金をすべて手に入れます。
static func receiveCoins(coins: Int) {
coinsInBank += coins
}
この関数は銀行に返す関数で、銀行の残金額が増えます。
}

構造体のBankはシンプルです。残金(coinsInBank)と借りる時に使う関数(vendCoins)と返す関数(receiveCoins)の3つのプロパティだけで、すべてグローバルなstatic変通と関数だ。

そして、次に人数不定のプレーヤーはクラス宣言となるが、構造体と似ていて、財布にある金(coinsInPuuse)と銀行から借りる時に使う関数「withCoins」の2つのプロパティが組み込まれていますね。ここで新しいのは、クラスが消滅した時のステップ、デイニシャライザがあって持っていたコインを全て銀行に返すステップだ。

class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func withCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
}
}

このプレーヤはクラスなんで、必ずインスタンスを生成しないと使えないため以下のインスタンス宣言をする。
var playerOne: Player? = Player(coins: 100)
println("A new player ha joined the game with \(playerOne!.coinsInPurse) coins")
println("There are now \(Bank.coinsInBank) coins in the Bank")

//A new player ha joined the game with 100 coins
//There are now 9900 coins in the Bank

playerOne!.withCoins(2_000)
println("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse)")
println("The bank now only has \(Bank.coinsInBank) coins")

//PlayerOne won 2000 coins & now has 2100
//The bank now only has 7900 coins

とここまでは思い通りに動いている。
じゃあ、デイニシャライザーということで、次のステップに進むと...

playerOne = nil
println("PlayerOne has left the game")
println("The bank now has \(Bank.coinsInBank) coins")

//PlayerOne has left the game
//The bank now has 7900 coins

お金が銀行に戻っていない(T_T)  


2014年08月30日
Posted by 屋台ブルー at ◆ 2014年08月30日00:00 Comment(0)

Swiftで遊ぼう! - 51 また話は飛んで今日からARC

swift_logoアプリ作ってたと思ったら途中で止めて、復習としてジェネリックの理解を深めようとしていた矢先、またまた興味は別のところに(^^;) 最近参加したFacebookグループの「Swift_developers japan」のエントリーに、「Swiftのクロージャにおける循環参照問題でunownedとweakの使い分けがわからない」があった。「unowned?、weak? はて、聞いたことがないキーワードだ!」ということで、教科書を調べてみると、ARC(Automatic Reference Counting:自動リファレンス計算?)という章に出てくる。ARCの冒頭の説明が以下のようになっていました。
SwiftはAutomatic Reference Countin(ARC)を使用して、アプリケーションが使用するメモリーを監視制御する。ほとんどの場合で、Swiftのメモリーマネージメントは「単に機能する(just works)」ということを意味する。デベロッパーはメモリー管理を考える必要はなく、いらなくなったクラスインスタンスが保持していたメモリーはARCによって自動的に開放される。

なんだ、自動でしてくれるなら、この章は後回しということで、読んでいなかった部分だったので、今の知識をもってしっかり読み解いていくことにしたのですが、この冒頭の文の中に重要なキーワードが2つあります。一つ目が「単に機能する(just works)」だった。最初に理解した時に、メモリーマネージメントを自動的にするのなら意識しなくてもいいのかと思いましたが、意識しないでいい内容なら説明する必要がないでしょう。この「単に機能する」とは、勝手に処理してしまうので、意識しないとメモリー管理が無茶苦茶になりますよ。という意味です。それは、内容を読んで理解できました。メモリーリークという言葉を以前に聞いたことがありますが、メモリーリークが生じる理由が理解できました。また2つ目のキーワードが、メモリー管理が必要なのは、継承機能がありリファレンス値を扱う「クラス」型に限るということです。という事は、例文はすべてクラスを使わないとできないということになりますね。

プログラミングを理解する上で、入れ子状態(Nest)が多重に生じてしまうと、理解しにくくなります。私のように理解力の乏しいオヤジになるとなおさらです。クラスは継承できて、その上、プロパティに他のクラスを持つことができるので、互いに異なるクラスがそれぞれのクラスをプロパティを持ってしまうとややこしくなるようです。しかし、実際そのような状況はよくあるようです。教科書の例文、めちゃ重要なんだということを再認識しました。

さて、ARCの本題に入る前に、デ・イニシャライザーの説明を明日からしよう。