Change before you have to.

androidアプリ開発、iosアプリ開発、rails、deep learning .etc.を使った社会実験。

無性に機械学習がしたくてdeep learningをアプリに実装してみた話③

前回寄り道をしてしまいましたが、

今回は実装してみたシリーズ第三弾です。

 

第二弾では、ニューラルネットワークの中間層から出力層への接続荷重の更新をやりましたが、

今回は宣言したように、ニューラルネットワークにおける入力層から中間層への接続荷重の更新を行います。

 

接続荷重の更新は前回紹介したように、出力信号と教師信号の誤差Eの自乗が最低値となるように更新すべきなので、

 

 { \displaystyle \frac{\partial E}{\partial W_{i, j}} = 0}

 

となる{ \displaystyle W_{i,j}}になるように更新していきます。

 

今回は評価関数Eを以下のように分解します。

 

 

{ \displaystyle \frac{\partial E}{\partial W_{i, j}} = \sum_{{k'}=0}^{max_k} (\frac{\partial E}{\partial output_{k'}} \frac{\partial output_{k'}}{u_{k'}} \frac{\partial u_{k'}}{\partial hidden_j}) \frac{\partial hidden_j}{\partial u_j} \frac{\partial u_j}{\partial W_{i,j}} }

 

ここで{u_k}は出力層の細胞kの内部状態、{u_j}は中間層jの細胞の内部状態としています。

なぜこのような形になっているのか...それは微分しやすくするためです。( ! )

 

右辺の積は以下のように、 

{ \displaystyle \frac{\partial u_{k}}{\partial h_{j}} = \frac{\partial }{\partial h_j} \sum_{j'=0}^{{max}_j} hidden_{j'} \cdot W_{j',k} = W_{j,k} }

 

{ \displaystyle \frac{\partial hidden_j}{\partial u_j} = \frac{\partial }{\partial u_j} \frac{1}{1+ e^{-au_j} } =  \frac{-ae^{-au_j}}{1+e^{-au_j}} = a \cdot (1-output_j) \cdot output_j }

 

{ \displaystyle \frac{\partial u_{j}}{\partial W_{i,j}} = \frac{\partial }{\partial W_{i,j}} \sum_{i'=0}^{{max}_i} input_{i'} \cdot W_{i',j} = input_{i} } 

 

 となるので、結局、

 { \displaystyle ΔW_{i,j} = -δ \left\{ \sum_{j'=0}^{{max}_j} (output_k-supervisor_k) \cdot output_k \cdot (1-output_k) \cdot W_{h,k} \right\} hidden_j \cdot (1-hidden_j) \cdot input_i}

 

が最終形になります。

 

このΔWijを入力層のi番目のニューロンから中間層j番目のニューロンへの接続荷重に加えてあげれば良いです。

 

 

こうして、前回の中間層から出力層への接続荷重と、

今回の入力層から中間層への接続荷重に対して
それぞれ変化量ΔWj,k、ΔWi,jを計算し、加えてあげることで誤差Eが低下します。
すなわち、学習が進む、ということになります。

計算過程は偏微分と全微分の大学一年生で習う数学の公式を使って

ひたすらぐるぐる計算しているのですが、
これらの計算を覚える必要はなくて、

最後の結果だけを適用すれば良いです。

 

接続荷重の更新の中で、δという文字が出てきますが、

これは0〜1の間の小数で表される学習の効率性を示します。


効率性だからといって大きな値にしてはいけません。
ニューラルネットの接続荷重は初期状態においてランダムで、

いわば赤ちゃんが意味のある言葉を喋られないように、
初期の出力ニューロンは無意味な値を出力します。
学習が進むにつれてこのランダムな出力は試行錯誤を繰り返しながら、

接続荷重が雑然とした状態から徐々に自己組織的に正しい(?)値に近づき

出力ニューロンが教えられた通りの出力を示します。


この試行錯誤の部分において、どれだけ冒険していいか、

また冒険した結果、どれだけ自分自身の接続荷重を更新していいかを

決める指標にもなるため、あまりにも大きい値をとってしまうと

接続荷重の変化量がふりきれてしまい、

思うように学習が進まないということがあります。

また一方で、あまりにも小さい値にしてしまうと、学習の進捗が遅くなってしまいます。 

 

ニューロン数や(中間層を増やす場合は)層数によって適当な値は異なりますが 

ニューラルネットワークには他にもシグモイド関数の勾配(式中ではa)など

無数のパラメータが存在しています。

実際の脳ではこれらのパラメータでさえ、接続荷重と同様に学習して変化しているとも言われています。

これら無数のパラメータの調整が学習の難しさにも繋がっているのですが、

ニューラルネットワークの規模や形態によって適切な値に変更してあげる必要があります。

 

次回では、実際に実装した結果を示したいと思います。

また上記パラメータ調整による学習スピードの変化についても調べる予定です。

 

最近上場したテック系ベンチャーをまとめてみた

見たようなタイトルですが、自作ですw

 

最近の円安株高がベンチャー界隈にも波及したのか

IPOとかPOとか少し賑やかになってきました。

 

というのも、
12月12日にクラウドワークスが
12月18日にgumiが上場することが決定しました。
クラウドワークスについてはマザーズに決まりましたが、
 gumiについて11月16日現在まだ上場先が決定していません。

 

関係者の皆様おめでとうございます。

 

テンポよく連続してIPOが発表されるので
果たしてこの流れが続くかどうかということ、
各上場会社について調べてみようと思いました。


ついでに、少し前(6月24日)にマザーズ上場したフリークアウトとか
gumiと同業のガンホーとかコロプラと比較してみました。


<利益率>
まず、利益率について最も一般的な同社の純資産に対する最終利益の割合を比較してみます。

f:id:ichonol:20141116184222p:plain

次にこの純資産利益率を
売上高利益率、総資産回転率、財務レバレッジの3指標に分類します。※デュポン分解の意味については省略

 

まずは利益指標について

【売上高利益率について】

 

f:id:ichonol:20141116184225p:plain

クラウドワークスはそもそも赤字上場なので、目立ちますねw

 

 

次に効率性指標です。

【総資産回転率について】

※総資産が事業のために投下された総資産をどれだけ有効活用しているか示す指標で
ここでは売上高の何倍(回転)するかで計算しています。

f:id:ichonol:20141116184232p:plain

 

 

最後に

【財務レバレッジについて】

自己の資本である純資産に対して、どれだけの資産を事業に投下しているかを示していて外部借入など(レバレッジ=てこ)を活用しているかの指標になります。

 

f:id:ichonol:20141116184247p:plain

 

財務レバレッジはリスク水準を超えるような大きさではないのですが、
負債の内訳を見るために短期と長期の安全性指標を軽く見てみます。

 

【短期的な安全性について】

f:id:ichonol:20141116184252p:plain

 

【長期的な安全性について】

f:id:ichonol:20141116184256p:plain

 

 

ーー
各社見てみましたが、
共通して言えるのは売上が立っている会社もそうでない会社も
外部資金に対するレバレッジを効かせすぎることなく
自己資金を有効に活用しているという点では、健全な運営体制が垣間見えました

 

gumiはクラウドワークスと対極的に資産の有効活用ができていて

フリークアウト、ガンホー、コロプラが1倍程度であるのに対して
総資産の2倍の売上高を上げています。


一方で、開発費用が嵩んでいる点は否めず、今後新たなタイトルの開発費用については課題が残るところです。

 

 

クラウドワークスについても専業の利益による黒字化が待たれるところですが、
これはPOによって得られた資金を人員拡充のために用いて営業費用が嵩んでいるためで
回転率が低いように、今後こうした”投資”活動が営業収益に結びついていけば黒字化を達成することは容易であるように思います。
今後上場を機に強豪だったランサーズに対しても差が広がっていく可能性もあります。
※個人的に両社をよく活用しているので、クラウドソーシング分野の強み弱みに対する棲み分けができていくのではないかと思いますが。

 

 

また業界の健全性担保のためのクラウドソーシング協会を設立したり、

政府、地方自治体からも受注していることからも

順調にロビー活動を展開しているようで、

これから上場により得られる自己資本を投じて

事業買収やパートナーシップ契約を進めていくことによりさらなる飛躍を遂げることでしょう。

 

 今後もテクノロジーベンチャーが発展することを祈念しています。 

 

無性に機械学習がしたくてdeep learningをアプリに実装してみた話②

前回は脳とニューラルネットの関係性について述べました。

 

今回はもう少し踏み込んだアルゴリズムの話をしようと思います。

 

そもそもニューラルネットワーク

脳の構造を模倣した構造である一方で、

れっきとした数理統計モデルの一種で、数学バリバリ使います。

特に学習する際のバックプロパゲーションに関しては

微分偏微分の知識が前提になっています。

けど、さんすう嫌いの方でも読めるように式を飛ばしても読めるよう工夫して書こうと思います。

 

ニューラルネットは大きく推測行動(feed-forward)と学習(learning)の2つのアクションがあります。

 

【推測行動】

視覚システムに限って言うと、人の脳が眼球内にある光受容体の視細胞から電磁波(光)を電気信号に変換し、脳内の特定のニューロンを刺激して頻繁に発火させながら信号を伝播し、最後に小脳、海馬や大脳辺縁系等の特定のニューロンを刺激して今見ている対象物を認識するように、

ニューラルネットも入力となる信号Iに対して脳内で処理し、判断するための出力ニューロンを定義してあげます。


まず、入力層は入力信号をそのまま出力し、

{ \displaystyle input_i}と表現することにします。

 

そうすると 中間層については

{ \displaystyle hidden_{p,q} = f(\small\sum_{q'=0}^{q'_{max}} hidden_{p-1,q'} * W_{q,q'}^{p,p-1} })

 となります。

 

但し、

{ \displaystyle hidden_{p,q}}はp列q番目の中間層ニューロンの出力値

{ \displaystyle W_{q,q'}^{p,p-1}}はp-1列q'番目ニューロンからp列q番目ニューロンへの接続加重

 関数fは{\displaystyle f(x)=\frac1{1+e^{-ax}}}であり、一般にシグモイド関数と呼ばれ、関数の形状から明らかですが原点を通過して右肩上がりの曲線を描き、aが大きくなる程その傾きがスティープ化します。

のちにバックプロパゲーションを説明する部分で出てきますが、シグモイド関数微分可能であること以上に、微分すると自分自体の式が現れるという特徴が重要です。 

{\displaystyle \frac{\partial f(x)}{\partial x}=\frac{\partial }{\partial x} \frac{1}{1+e^{-ax}}=\frac{e^{-ax}}{(1+e^{-ax})^2}=f(x)(1-f(x)) }

ちなみに同様の性質を持つ関数として

{\displaystyle tanh(ax)=\frac{1-e^{-2ax}}{1+e^{-2ax}}}

という双曲線関数も使用することができます。

 

要は入力信号をシャンパン、ニューロンをグラスに例えると

ニューラルネットワークは大小様々なグラスが積み重なったシャンパングラスのようなもので、

上からシャンパンを注ぐと、各グラスはその容積を満たすまではこぼさないのですが、

グラスの容積を超えるとシャンパンは下層のグラスに伝っていきます。

 

 

 

一般にパラメータaの大きくなる程ニューロンが活性化し易くなるので、学習スピードに影響しますが、チューニングを誤ると過学習や学習失敗(評価関数の発散)が起こり得ます。

 

ちなみにp=0の時、

{ \displaystyle hidden_{0,q} = f(\small\sum_{i=0}^{i_{max}} input_i * W_{q,i}^{0})}
であり、{ \displaystyle W_{q,i}^{0} }は入力ニューロンiから最低位層の中間層ニューロンqへの接続加重です。

 

出力層について

{ \displaystyle output_k = f( \small\sum_{q=0}^{q_{max}} hidden_{p_{max},q} * W_{k,q})}

 となります。

 

input→hidden→outputと順々に信号が伝播しますが、

接続加重Wは最初は「ランダム」なので、最初はデタラメな出力を吐き出します。

 

 

【評価と学習行動】

 そうして、ある一定期間注がれた後の

最下層の注がれているグラス(出力層のニューロン)の組み合わせが

最適になるように「評価」して各グラスの容量を変えていく作業が「学習」になります。

一般に出力層から中間層を経由して入力層へと接続荷重を更新していくので、バックプロパゲーション(backpropagation)、もしくは日本語で逆誤差伝播法と呼ばれます。

 ※簡単のため、今回は出力層と一番最後の中間層の間の学習のみ説明します。

 

 まず評価は出力値の認識がどれだけ正しいかという情報を数字に変換します。

 

今回は答えを教えないで学習するので、

出力ニューロンの出した回答(認識)が正解ならば報酬を、

不正解ならば罰(panishment)を与えるようにします。

 

 

通常、脳内ではシナプスが発火すればするほどシナプス(あるニューロンの軸索から別のニューロン樹状突起へ結合されている部分)が強くなり、信号を通しやすくします(Hebb則)。

※厳密にはHebbs則とは細胞Aの軸索が細胞Bを発火させるのに十分近くにあり、繰り返しあるいは絶え間なくその発火に参加するとき、いくつかの成長過程あるいは代謝変化が一方あるいは両方の細胞に起こり、細胞Bを発火させる細胞の1つとして細胞Aの効率が増加する、ことを言います。

 

ニューラルネットワークではこのようなシナプス結合のように、
ニューロンニューロンを結合する結合荷重を用意して、
正しい結果が得られれば結合荷重を強くすることで信号が伝わりやすくなるようにすることでHebb則擬きを実現しています。

 

 

先ほど正しい出力が得られれば報酬を、誤った出力ならば罰を、

という話だったので、こうした報酬と罰をそれぞれ+cδと−δ(ただし、{ \displaystyle c \in R})としてその報酬額をEと置くと、

Eは各結合荷重に対して連続な上への凸関数になる(微分可能なシグモイド関数を使っているメリット!)ので、例えばj番目の中間層ニューロンからk番目の出力ニューロンへの結合荷重の更新{ \displaystyle ΔW_{j,k}}は全微分を使うと

{ \displaystyle ΔW_{k,j} = \frac{\partial E}{\partial W_{j,k}} ΔE }

であり、Eは出力ニューロンoutput、出力ニューロンの入力値u、結合荷重wの変微分形式で表現してあげると

{ \displaystyle ΔW_{k,j} = \frac{\partial E}{\partial W_{j,k}} ΔE }

   { \displaystyle  = \frac{\partial E}{\partial output_{k}} \frac{\partial output_{k}}{\partial u_{k}} \frac{\partial u_{k}}{\partial W_{j,k}}ΔE }

であり、右辺は各々

{ \displaystyle \frac{\partial output_{k}}{\partial u_{k}} = \frac{\partial }{\partial u_k} \frac{1}{1+ e^{-au_k} } =  \frac{-ae^{-au_k}}{1+e^{-au_k}} = a \cdot (1-output_k) \cdot output_k }

 { \displaystyle \frac{\partial u_{k}}{\partial W_{j,k}} = \frac{\partial }{\partial W_{j,k}} \sum_{j'=0}^{max_j} hidden_{j'} \cdot W_{j',k} }

だから、結局

 

{ \displaystyle ΔW_{j,k} = -δ \cdot output_k \cdot (1-output_k) \cdot hidden_j \cdot δ}

 

となります。

 

 

こうして、j番目中間層ニューロンからk番目出力層ニューロンへの接続荷重は何度も何度もこの値を加えていくことで正しい出力が得られることになります。

 

tex形式で書くのが辛いので今日はここまで。

 

次回は今回の中間層-出力層間の学習結果をベースにして、複数中間層間の接続荷重の計算、入力層から中間層への接続荷重の計算(今回より若干複雑になります)をして、

実際の学習をobjective-cでコーディングした上で、3層構造ニューラルネットパーセプトロンと呼びます)の問題点を説明した後、

いよいよdeep learningのためのrestricted boltzmann machine、auto-encoding、deep boltzmann machineを行っていきます。

 

 

 

参考 

 ヘッブの法則(ヘッブのほうそく)は、脳のシナプス可塑性についての法則である。ヘッブ則、ヘブ則とも呼ばれる。心理学者のドナルド・ヘッブによって提唱された。ニューロン間の接合部であるシナプスにおいて、シナプスニューロンの繰り返し発火によってシナプスニューロンに発火が起こると、そのシナプスの伝達効率が増強される。また逆に、発火が長期間起こらないと、そのシナプスの伝達効率は減退するというもの。(出典:ヘッブの法則 - Wikipedia)

 

シグモイド関数の説明中にある、閾値を超えるような信号強度が入ってきた場合に発火するような単純な仕組みではありませんので、そういう点は実際の脳の構造と異なり注意が必要ですね。

無性に機械学習がしたくてdeep learningをアプリに実装してみた話①

本日はいつもと違ってアプリの開発方法が主題ではない話を書こうと思います。

 

まず、最も一般に認識されているニューラルネットワークと脳のメカニズムは根本から異なるということを述べておきます。

従来、前者は後者の構造の一部の特徴のみを用いて静的、もしくは動的なパターン認識を行っていると言えます。

もっと踏み込んで言うと、これまでニューラルネットワークでは表面的なパターン認識しかできなかったと言えます。

分かり易い例で言うと、人間のある動作を見て、ロボットがコップをこぼさないようにしていると認識しているか、ある一定の軌道を描くためにモータの出力トルクを時系列で真似するという事象は大きく異なります。

それを汎化能力の差と見るか、根本的な学習構造の差と見るか

 

最近(といっても半年以上前)、グーグルがdeepmindという人工知能テクノロジー企業を買収したことで話題になりました。

 

学生時代に小脳研究、社会人になってから銀行で金融工学系の研究職をしていた際、ニューラルネットワーク遺伝的アルゴリズムに関する業務に従事していたことがありました。

 

このニューラルネットワークとは

脳の中にあるニューロンと呼ばれる神経細胞を網の目上に張り巡らしたネットワークを模したものになります。

単純な三層構造のパーセプトロンと呼ばれる構造のものから

出力層のニューロンを入力層に接続して時系列学習が可能なリカレントニューラルネットワーク

最近話題のより多層構造からなるdeep neural network(DNN)と呼ばれるものまで

実に様々な形態が存在し、その学習方法も多種多様です。

 

単純なニューラルネットは普通、こういう入力(ベクトル)に対してはこういう出力(ベクトル)を出しなさい、というパターンを複数学習することができる、というものですが、

今回やりたいのは、例えばこれは人間ですという事実を教えないで、様々な写真に対して人間の画像を識別させる、というところをゴールに設定します。

これを延長させる(のは難しいですが)顔認証や物体識別も可能になると思っています。

 

専門用語で言うと、教師あり学習ではなく、強化学習を行います。

強化学習とは教師なし学習の一種で、簡単に言うとある種のアメとムチを使い分けて学習させることです。

子供を教育する時、間違えたら怒って、正解したら褒めるということを繰り返すと、

なんとなく善悪の判断がついて、習ってないけどこのパターンは恐らくこっちが正しいということが分かる、というようなイメージです。

つまりニューラルネットワークが自ら特徴を捉え、学んでない未知の出来事に対しても正しく反応することができるということです。

 

あと、どのような形態のニューラルネットワークを使うか、、ですが、

将来の拡張性を持たせるため、今話題のdeep learningの一種である

deep neural network(DNN)を採用したいのですが、

その前に簡単なニューラルネットワークを使おうと思います。


次回に続きます。

ネットショップ開いたら生活費稼げた話

今回はiOS開発日記ではないです。

お金がなさすぎて本格的にヤバくなってきたので
何かで稼がなくてはいけなくなりました。

それで、目を付けたのがネットショップで物を売るという手段です。

ネットショップと言っても今の時代は簡単にできるツールがたくさんあって、
今回はその中でもこちらツールを使ってみることにしました。


まず登録にはメールアドレスが必要なので
アドレスを持っていない人はgmailを登録します。
スマホがなくても作れますが、今回はスマホありきで作ります。

メールアドレスを持っている人は読み飛ばしてください。

まず検索窓にgmailと打って検索して下さい。
そしたら以下のページに行くと思います。
https://www.gmail.com/intl/ja/mail/help/about.html


右上のアカウント作成ボタンを押したら
次のページで、作成したいメールアドレス、名前、ユーザー名、(gmailログイン用)パスワードや生年月日等を入力して下さい。
生年月日や性別等は適当で大丈夫です。

でも携帯電話のところだけ自分が持っているスマホの電話番号を正確に入力して下さい。
そうしないとそのスマホに認証用のコード(6桁くらいの数字の羅列)が送られてきません。
電話番号を入力したらスマホに認証用コードが送られてきます。
時間にして大体長くても数分以内に送られてきます。
送られてこない場合は再度電話番号を確認してもう一度再送してもらうようにしましょう。


次にその下のテキストを入力という箇所は画像内に見える文字を入力して下さい。
大体、4桁程度の数字だと思います。

最後にgoogle利用規約とプライバシーポリシーに同意しますと記載があるところのチェックボタン(左の四角い箱)をクリックしてチェックして下さい。

運良く?メールアドレスを取得したら次に進んで下さい。

こちらのリンクで、右側にあるショップURL、メールアドレス、パスワードを入力します。



ショップURLは適当なもので良いですが、覚え易いものにしたほうがいいです。
メールアドレスはさきほど取得したものを使います。既に持っている人はそれを使って下さい。
パスワードも覚えやすいものを考えて適当に入力して下さい。

最後に利用規約のチェックをクリックしてオンにしたら、
右下にある緑色の「新しくショップを開く」というボタンをクリックして下さい。


これで、ようやくショップを開くことができました。もう、あなたはショップオーナーです。
メールアドレスを既に持っている人で早い人だと3カ所入力するだけなので
1分もかからずにネットショップを作成することができると思います。


次は作成したショップを公開します。
手順は以下の3つです。
①メール認証:
さっき作成したアドレスの受信ボックスを見るとBASEからメールが届いていますので
そのメールの中にあるリンクをクリックするだけです。
クリックした後に表示されるページに「メールアドレスの認証が完了しました」というメッセージが表示されていれば完了です。

②「特定商取引に関する表記」を登録します。
登録するためには、BASEのダッシュボードから”「特定商取引に関する表記」の登録が完了しておりません”というメッセージをクリックすると
”ショップ運営者様には「特定商取引法に関する表記」の記入をお願いしておりますので、こちらより行ってください”
というメッセージが表示されるので、緑色になっている”こちら”というリンクをクリックして、
遷移したページ先では、自分に関する情報を正確に入力しましょう。
お店で物を売る人が自分の身分を詐称していたら怖いですもんね。

③決済方法の設定
このページは自分のお店を物を買った人がどうやってお金を払うかを決めるページです。
例えばコンビニでお金を払う場合はコンビニ決済の利用申請するというボタンを押せば良いし、
送付された時に代金を支払う方法が良いなら代金引換という項目のチェックボタンを押せば良いです。
もちろん銀行振込も可能です。


以上でショップが公開されます。

次は商品を登録します。
必要最低限、設定が必要な箇所は以下の5カ所です。
・商品名:適当に入力します。
・価格:100円とか適当に入力します。
・商品説明:一言、商品についての説明を入力します。
・数量:販売できる量を入力します。
・商品画像:適当に画像を登録します。

商品画像がない場合は写真を撮影して、PCに取り込んでからその写真を選択します。
登録方法は「画像を追加する」というボタンをクリックすると
ファイルを選択する画面が現れますので、そこで登録したい写真を選択します。

最後に一番下にある右側の「保存する」というボタンを押せば商品が登録できます。

商品はいくつでも登録できるので、
好きなだけ登録して下さい!

以上でショップの登録と公開、商品登録の全てが完了しました。


え?売れるような商品がないですって?
それでも大丈夫!
登録できる商品がない人でもスマホケースやTシャツに自分で作った画像や写真を印刷して販売することも出来ます!
その方法は次回!

UITableViewについて(おもにdelegateメソッド)

今日は最近よく使うUITableViewについて書こうと思います。

 

https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UITableView_Class/index.html


UITableView Class Reference

 

UITableView(以下テーブルビュー)についてはobjective-cの中で最も良く使うコンポーネントではないかと思います。

ビューと名前がついていることからもUIViewのサブクラスのビューです。

このビューはセル(UITableViewCell)と呼ばれるインターフェース部品を要素としたテーブルです(そのまま^^;

 

一般にテーブルビューを作成するには二つの方法があります。

①UITableViewControllerを継承したViewControllerを作成する

②ViewControllerの中でUITableViewを保有する

 

今回はより汎用性の高いViewControllerの中でテーブルビューを保有する方法②について説明しようと思います。

 

具体的な方法は以下の通りです。

①headerファイルの宣言部でインターフェースを実装していることを宣言する

これはテーブルビューのデリゲートメソッド(セルの内容や個数の定義、変更を行るメソッド)を認識するためです。

@interface XXXViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>

ここは他のデリゲートメソッドを使用する時といっしょです。

 

②テーブルビューを定義

使い方によってグローバルに定義する場合とローカルに定義する場合がありますが、今回は必要最低限ということでローカルに定義します。

その場合、例えばviewDidLoadの中で

UITableView *myTable = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];

 

等と宣言、定義してあげます。

第一引数は表示するテーブルビューのサイズです。

この場合、画面いっぱいに表示しています。

台に引数はテーブルのスタイルで、UITableViewStylePlainと

UITableViewStyleGroupedを選びます。

例のように後者を選んだ場合はセクションと呼ばれるセルのグループ毎に表示します(具体的にはスクロールしてもヘッダーと呼ばれるビューが常に表示されるようになる)

どのセルがどのグループに属するかも定義できます。

 

③テーブルビューをaddSubview

②で作成したテーブルビューを表示させます。

例えば、

[self.view addSubview : myTable]

とすればself.viewに配置されます。

 

④宣言したテーブルビューをデリゲートメソッドに関連づける

①で関連づけたインターフェースのデリゲートメソッドと関連づけます。

myTable.dataSource = self;
myTable.delegate = self;

 これだけです。それぞれのインターフェースと対応しています。

 

⑤デリゲートメソッドの定義

以下のようにたくさんあります。

 A.dataSource

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

セクション内のセルの個数を返します。

必ず定義しなければなりません。

 


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

ここで具体的な内容を記述したセルを返します。

必ず定義しなければなりません。

ここでの注意としてセルに部品(ボタンやラベル等)を配置する場合は

セルに直接addSubviewするのではなく、

セルのcontentViewに対してaddSubviewするところです。

例えば

[cell.contentView addSubview: btn];

のように。

-(NSInteger)numberOfSectionsInTableView:
(UITableView *)tableView 
セクションの個数を返します。

必須ではありません。


-(NSString *)tableView:
(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
セクションのヘッダータイトルを返すように実装する

必須ではありません。


-(NSString *)tableView:
(UITableView *)tableView
titleForFooterInSection:(NSInteger)section 
セクションのフッタータイトルを返すように実装する

必須ではありません。


-(void)tableView:
(UITableView*)tableView commitEditingStyle:
(UITableViewCellEditingStyle)
editingStyle forRowAtIndexPath:
(NSIndexPath*)indexPath

編集モード時で、Delete、Insertされた時に呼び出されます。

 

※Delete時はcommitEditingStyleにUITableViewCellEditingStyleDeleteが渡されます
※Insert時はcommitEditingStyleにUITableViewCellEditingStyleInsertが渡されます。

-(void)tableView:
(UITableView*)tableView
moveRowAtIndexPath:(NSIndexPath*)
fromIndexPath
toIndexPath:(NSIndexPath*)toIndexPath

編集モード時で、セルが移動された時に呼び出されます。

 

※fromIndexPathに移動前のNSIndexPath、toIndexPathに移動後のNSIndexPathが渡ってくる。

 

B.delegate

-(CGFloat)tableView:
(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath

セルの高さを浮動小数型返します。

必須ではありません。

-(CGFloat)tableView:(UITableView *)tableView
heightForHeaderInSection:(NSInteger)section

同様にセクションヘッダーの高さを返します。


-(CGFloat)tableView:(UITableView *)tableView
heightForFooterInSection:(NSInteger)section

セクションフッターの高さを返します。


-(void)tableView:
(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath

セル毎にタップ時の反応を記述します。

セルをボタンの代わりのようにも出来ます。



-(void)tableView:(UITableView *)tableView
accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath

アクセサリボタンと呼ばれる部品をタップした時の反応を記述します。

 

これで一通り書いたかと思います。

忘れているものもあるかもしれないのですが。。^^;

 

個人的にtableView: cellForRowAtIndexPath:でセルを再利用する時が少し面倒だったかもしれませんが、ここら辺はiOS6からiOS7になるときに大幅に楽になったようです。

 

あと、iOS7からiOS8になってからセルのサブビュー構造が変わったことがくせ者でした。

例えば、[cell.contentView addsubview:btn1]とした時、

iOS7:UITabeViewCell->UITableViewCellContentView->UITableViewCellScrollView->btn1

となっていたのが

iOS8:UITabeViewCell->UITableViewCellContentView->btn1

 というふうになり、構造が変わっていました。

 

セルを動的に部品管理(例えば個別セルに設置した部品に対してremoveFromSuperview)する時は上記を気をつけなくてはいけないようです。

次回はUICollectionViewについて書いてみようと思います。

objective-cの循環参照について

 

 

前回は「ARCでメモリリークを起こさないために」というタイトルで記事を書きました。

 

今回は「循環参照」について書こうと思います。

 

循環参照とは二つのクラスの間でお互いをインポートしてしまうことです。

 

こんな感じです。

 

まずクラスAでクラスBを使用するためにインポートします

#import "ClassB.h"
@interface ClassA {
  ClassB variableB;
}
@end

 

今度は同様にクラスBでクラスAを使用するためにインポートします。

#import "ClassA.h"
@interface ClassB {
  ClassA variableA;
}
@end

 

 すると何がおこるかと言うと

このプログラムを理解しようとするコンパイラ

 

まずクラスAを理解しようとして、その中にあるクラスBを読みにいきます。

しかしクラスBにもクラスAが呼ばれているので、クラスAを読みにいき、・・・

この繰り返しをしてしまうので、実際には循環参照をしている時点でエラーになります。

 

この解決策として

まず、こんな循環参照になるような設計をしない!ということが大前提ですが、

それをいってしまうと根も葉もないので

以下のようにしてエラーを回避します。

 

まずクラスAは

#import "ClassA.h"
@class ClassA;
@interface ClassB {
  ClassA variableA;
}
@end

 

 として、同様にクラスBも以下のようにします。

 

#import "ClassB.h"
@class ClassB;
@interface ClassA {
  ClassB variableB;
}
@end

 

 

 

つまり、

@class ClassX

としてクラス宣言してやることで循環参照によるエラーを回避できます。