<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8445416630012754496</id><updated>2011-11-19T16:39:37.580+09:00</updated><category term='ruby'/><category term='1. GAEをプログラムする'/><category term='1. 自由なプログラマの規律'/><category term='1. 物読みファイル'/><category term='emacs'/><category term='拡張された読書体験'/><category term='2. デジタルの遺伝子'/><category term='デザイン'/><category term='bloggerglass'/><category term='2. LOGREPO (構築編)'/><category term='ipad'/><category term='キーボード'/><category term='9. about'/><category term='2. 検索のある生活'/><category term='macruby'/><category term='ports'/><category term='ユーザ体験'/><category term='ebook'/><category term='appletv'/><category term='cocoa'/><category term='ライフログ'/><category term='iphone'/><category term='xcode'/><category term='2. アナログとデジタルの界面'/><category term='git'/><category term='python'/><category term='ipod'/><category term='trackpad'/><category term='imac'/><category term='gdata'/><category term='zsh'/><category term='macbook'/><category term='1. 理想のノート(アプリ編)'/><category term='文房具'/><category term='2. ネットの向こう側'/><category term='mini'/><category term='2. アプリの断片'/><category term='redmine'/><category term='cocoatouch'/><category term='数学'/><category term='itunes'/><category term='1. Macをプログラムする'/><category term='3. twitterより'/><category term='scansnap'/><category term='ヒレガス本'/><title type='text'>lifeLOG + REPOsitory</title><subtitle type='html'>身の周りで起きること、起こすことの記録、それが lifelog。
自分で作るモノの置き場所、それが repository。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default?start-index=101&amp;max-results=100'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>375</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3784949745548079194</id><published>2011-11-19T16:39:00.001+09:00</published><updated>2011-11-19T16:39:37.677+09:00</updated><title type='text'>twitter より (2011-11-18)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/137379841656885249"&gt;13:01&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;納得した！ → "@&lt;a href="http://twitter.com/yoshikaw"&gt;yoshikaw&lt;/a&gt;: "「一番いいおすすめを頼む」 〜5分でわかるレコメンドエンジンの基礎〜 (Gunma.web &lt;a href="http://search.twitter.com/search?q=%233"&gt;#3&lt;/a&gt; 2010/12/11)" &lt;a href="http://t.co/PEtTmx6N""&gt;http://t.co/PEtTmx6N"&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3784949745548079194?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3784949745548079194/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/11/twitter-2011-11-18.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3784949745548079194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3784949745548079194'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/11/twitter-2011-11-18.html' title='twitter より (2011-11-18)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7062973208499692941</id><published>2011-11-08T01:03:00.001+09:00</published><updated>2011-11-08T01:03:17.117+09:00</updated><title type='text'>twitter より (2011-11-07)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/133366646801825792"&gt;11:14&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Ruby 1.9.3 を Lion (+ xcode) でビルドする際の注意点が載っている。 → &lt;a href="http://t.co/YC9ch0OT"&gt;http://t.co/YC9ch0OT&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7062973208499692941?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7062973208499692941/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/11/twitter-2011-11-07.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7062973208499692941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7062973208499692941'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/11/twitter-2011-11-07.html' title='twitter より (2011-11-07)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5111013917537255469</id><published>2011-10-28T08:07:00.001+09:00</published><updated>2011-10-28T08:11:16.895+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='imac'/><category scheme='http://www.blogger.com/atom/ns#' term='macbook'/><title type='text'>twitter より (2011-10-26)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/129019505635631105"&gt;11:20&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;MobileMe 時代には複数のMac間でMail.app上のローカルなメモが同期できていたのに、iCloudに移行してからそれができなくなった。もちろん、iCloud上にメモを作れば同期する。とはいえ、こういう仕様変更にはイライラさせられる。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/129022234223316992"&gt;11:31&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;やっぱりバグだったのかもな。ローカルなメモが同期していたのは。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/129029956457086976"&gt;12:01&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iMac が Lion になって以来、スクロールバーが常時表示されないのはユーザ体験の観点から言ってどうかな、と思ってきた。が、これってデバイス(というか、Mac の機種)によって変わるのな。MacBook (Early 2008) を Lion に移行させて初めてわかった。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/129030790834159617"&gt;12:05&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;ちなみに、スクロールバーの表示方式は「システム環境設定」&amp;gt;「一般」&amp;gt;「スクロールバーの表示」で切り替えられる。デフォルトは「入力デバイスに基づいて自動的に表示」。当然のことだが、常に表示させるとコンテンツの表示エリアが小さくなる。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-5111013917537255469?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/5111013917537255469/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/10/twitter-2011-10-26.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5111013917537255469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5111013917537255469'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/10/twitter-2011-10-26.html' title='twitter より (2011-10-26)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-8180344782276089109</id><published>2011-10-21T13:43:00.002+09:00</published><updated>2011-10-28T08:09:59.569+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='imac'/><title type='text'>Lion アップデート (10.7.1 → 10.7.2)</title><content type='html'>&lt;table class="chart"&gt;
&lt;caption&gt;Lion (client) 10.7.2 update&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iMac (Mid 2010)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;
&lt;a href="https://picasaweb.google.com/lh/photo/RpfLv9gLT8FWS5FZmrguFg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-ZHxxAC6Lrls/TqD25dV_ywI/AAAAAAAABxM/gczwu5nYWbM/s640/lion_client-10.7.2-update.png" height="640" width="285" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/Hr2mTYVb3wRpHI6hXL5IMw?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-Wlie5eIJbUw/TqD26KlwXVI/AAAAAAAABxU/x_SHwU8m9FA/s800/lion_client-10.7.2-about.png" height="379" width="307" /&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;アップデートは 2011-10-19 に実行。再起動後に iCloud への以降をうながすウィンドウが出てきたが、iOS デバイスのアップデートを放ってあるので、iCloud への以降もしばらく保留。MacBook も Snow Leopard のままになっているし。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2011/08/lion-10.html"&gt;Lion アップデート (10.7 → 10.7.1)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-8180344782276089109?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/8180344782276089109/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/10/lion-1071-1072.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8180344782276089109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8180344782276089109'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/10/lion-1071-1072.html' title='Lion アップデート (10.7.1 → 10.7.2)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-ZHxxAC6Lrls/TqD25dV_ywI/AAAAAAAABxM/gczwu5nYWbM/s72-c/lion_client-10.7.2-update.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1470764431199429590</id><published>2011-08-26T18:19:00.001+09:00</published><updated>2011-08-26T18:19:33.214+09:00</updated><title type='text'>twitter より (2011-08-25)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/106667692538331137"&gt;19:02&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;そうか。ジョブズが CEO 辞任したのか。彼のスピーチが見られなくなるとしたら、ちょっと残念かも。→ &lt;a href="http://t.co/fsZWF6F"&gt;http://t.co/fsZWF6F&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/106668203303907328"&gt;19:04&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;10年後に残っているのは何なのかな?Windows? Mac? それとも iPhone? どれも忘れられているんだろうな。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/106669341482827776"&gt;19:08&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;ソフトウェアっていうのは消してしまえるんだよねえ。物理的な実体をともなったサーバ設備を削除するのは簡単なことじゃないけど、仮想サーバなら管理者パスワードを知っていれば それができる。コワいわ。→ &lt;a href="http://t.co/MxeL7D9"&gt;http://t.co/MxeL7D9&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/106671093552644097"&gt;19:15&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;世界の仮想化(ソフトウェアへの移行)が進んだ状態では、パスワードによる直接的な権限認証方式は旧式に過ぎるのだろう。個人の認証ならパスワード(本人だけが知っている)でも良いとして、権限の付与や移譲はそれとは別の方式を採用すべきってことか。やはり確実なのは物理的な鍵か(・ω・)？&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1470764431199429590?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1470764431199429590/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/08/twitter-2011-08-25.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1470764431199429590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1470764431199429590'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/08/twitter-2011-08-25.html' title='twitter より (2011-08-25)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7497337307886797417</id><published>2011-08-20T12:32:00.000+09:00</published><updated>2011-10-28T08:10:26.043+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='imac'/><title type='text'>Lion アップデート (10.7 → 10.7.1)</title><content type='html'>&lt;table class="chart"&gt;&lt;caption&gt;Lion (client) 10.7.1 update&lt;/caption&gt; &lt;tbody&gt;
&lt;tr&gt;   &lt;th&gt;iMac (Mid 2010)&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/KrdgTR-AHdCWkSmCHf72yw?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-CaVmp_fRPj0/Tk8ny_IP_HI/AAAAAAAABw4/r0UEI9d2rCo/s400/lion_client-10.7.1-update.png" height="400" width="296" /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/Fu4H9LtCnvRQUwb8_bH-Sw?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-8DkHsamLCAo/Tk8nzRkypKI/AAAAAAAABw4/oZmAQFmdkUg/s800/lion_client-10.7.1-about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt; &lt;/table&gt;&lt;br /&gt;
&lt;h4&gt;関連記事&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2011/03/snow-leopard-1066-1067.html"&gt;Snow Leopard アップデート (10.6.6 → 10.6.7)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7497337307886797417?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7497337307886797417/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/08/lion-10.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7497337307886797417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7497337307886797417'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/08/lion-10.html' title='Lion アップデート (10.7 → 10.7.1)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/-CaVmp_fRPj0/Tk8ny_IP_HI/AAAAAAAABw4/r0UEI9d2rCo/s72-c/lion_client-10.7.1-update.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7283590922597136828</id><published>2011-08-11T13:49:00.001+09:00</published><updated>2011-08-11T13:49:42.905+09:00</updated><title type='text'>twitter より (2011-08-10)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/101095143712624640"&gt;09:58&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;すごいなあ。一度つぶれかけてたことが嘘のようだわ。 → アップル、時価総額で米国一に　エクソンモービル抜く ( &lt;a href="http://t.co/gL38GTQ"&gt;http://t.co/gL38GTQ&lt;/a&gt; )&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/101096413487833088"&gt;10:03&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;とはいえ、盛者必衰は世のならい。やがては凋落していくんだろう。ただ、iPhone も Mac も今更他の機器に乗り換えたくない。あと 5 年くらいはがんばってほしいかな。きっとその頃にはまるっきり違うモノが出ている。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7283590922597136828?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7283590922597136828/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/08/twitter-2011-08-10.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7283590922597136828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7283590922597136828'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/08/twitter-2011-08-10.html' title='twitter より (2011-08-10)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-9184102072738717876</id><published>2011-07-30T11:44:00.001+09:00</published><updated>2011-07-30T11:44:59.903+09:00</updated><title type='text'>twitter より (2011-07-29)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/96808812866052096"&gt;14:06&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;やっとわかった。なんで「システム環境設定」から消しちゃったんだろうな。 → Lionで操作スペースにソフトを割りあてる方法 ( &lt;a href="http://t.co/S3rMPH8"&gt;http://t.co/S3rMPH8&lt;/a&gt; )&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-9184102072738717876?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/9184102072738717876/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-29.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/9184102072738717876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/9184102072738717876'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-29.html' title='twitter より (2011-07-29)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6162925612098563998</id><published>2011-07-27T06:16:00.001+09:00</published><updated>2011-07-27T06:16:39.172+09:00</updated><title type='text'>twitter より (2011-07-26)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/95695609775915008"&gt;12:23&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;GAE だと Go でも Web アプリが作れるのか。→ &lt;a href="http://t.co/IsyVK4H"&gt;http://t.co/IsyVK4H&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6162925612098563998?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6162925612098563998/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-26.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6162925612098563998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6162925612098563998'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-26.html' title='twitter より (2011-07-26)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4901487402474216627</id><published>2011-07-22T11:54:00.001+09:00</published><updated>2011-07-22T11:54:02.428+09:00</updated><title type='text'>twitter より (2011-07-21)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/93722821427085312"&gt;01:43&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;とりあえず imac を Lion に上書きアップデートしてみる。ただいまダウンロード中...&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/93729514911318016"&gt;02:10&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Lion へのアップデート完了(iMac)。AppStore でポチってから Lion で起動するまで 30 分かからなかった。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/93785136839139328"&gt;05:51&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Lion になって、Safari の「進む」「戻る」ボタンの色が薄くなった気がする。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/93790137313529856"&gt;06:11&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Lion のサーバ版は ¥4,300.- App Store のトップには並んでいないけど、検索すれば見つかる。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/93842546752634880"&gt;09:39&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Air もそのうち終わるかも。プログラミングしないならキーボードつきの iPad とあまり変わらんものね。 → アップル、ホワイト「MacBook」をひっそりと提供終了 ( &lt;a href="http://t.co/REGofZo"&gt;http://t.co/REGofZo&lt;/a&gt; )&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4901487402474216627?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4901487402474216627/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-21.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4901487402474216627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4901487402474216627'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/07/twitter-2011-07-21.html' title='twitter より (2011-07-21)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7500469912095002128</id><published>2011-06-10T12:22:00.001+09:00</published><updated>2011-06-10T12:22:53.133+09:00</updated><title type='text'>twitter より (2011-06-09)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78667170225201152"&gt;12:38&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;なんだ！今日(2011-06-09)の Google のロゴは音が鳴るぞ。ギターになっていて、マウスで弦をはじくと音が鳴る。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7500469912095002128?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7500469912095002128/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-09.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7500469912095002128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7500469912095002128'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-09.html' title='twitter より (2011-06-09)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2985369878244043621</id><published>2011-06-09T06:23:00.001+09:00</published><updated>2011-06-09T06:23:27.456+09:00</updated><title type='text'>twitter より (2011-06-08)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78366575547269122"&gt;16:43&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;先日(6/6)の WWDC keynote がポッドキャストで配信されていた。いま、見終わったところ。Lion の紹介がある一方で、iOS 5 と言い、iCloud と言い、総じて「脱PC」をメッセージとして強く感じた。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78367802725117952"&gt;16:48&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Lion で興味を引かれたのは Autosave と Versions。明示的な「セーブ」操作をなくすのは「ユーザビリティ」上の大きな挑戦だと思うが、版管理(Versions)と組み合わせることでその飛躍を解消している。ただしユーザが受け入れるかどうかは未知数だけど。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78368514523676672"&gt;16:51&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Versions はデモを見る限りでは、アプリごとの Time Machine だ。アプリ側が意識することなく(つまり、そのためのコードを追加することなく)、このサービスを使えるのか、さらに旧版を保持するためのストレージがどこなのか、などはデモからはわからなかった。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78369574499782656"&gt;16:55&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Lion についてもう 1 点、特筆すべきは、Mac App Store のみで提供されるってことだろう。4GB 程度で HD の映画 1 本と同じぐらいとのこと。$29.99 で 7 月に発売。あと 1 ヶ月か 1.5 ヶ月ぐらいか。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78370087261835265"&gt;16:57&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iOS 5 の目玉は PC Free だろう。購入して Setup するときに iTunes につなぐ必要がなく、Software update もl OTA でできるようになる。iCloud とあわせれば Mac (あるいは PC) はもういらない、と。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78370467580350464"&gt;16:59&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;まあ、Mac (や PC) が不要と言うよりは、Mac (や PC) が iPhone や iPad と同等になった、と見るべきか。もう、付属品(アクセサリー)じゃないのだ、と。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78371128623632384"&gt;17:01&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iCloud は Mobile Me の拡大後継サービス。対応アプリが増えて、無料になる。Mobile Me はどうなるんだ、と思っていたら、すでに移行(とサービス終了)の案内が出ていた。 → &lt;a href="http://t.co/wPm76Tm"&gt;http://t.co/wPm76Tm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78372181515575296"&gt;17:05&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Apple に(安定した)クラウドサービスなんて提供できるのか、と思っていたら keynote の終わりで、新しいデータセンター作ったぞ、と自慢していたので何とかなるのかも。まあ、サービス開始当初は混乱するかもしれないけどな。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/78373119831707648"&gt;17:09&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;今回の keynote は全体的に地味な印象を受けた。これはハードウェアの発表がなかったからだと思う。次の iPhone はいつなんだろう。待ってるんだよ。今更、白モデルを買う気になれないんだから。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2985369878244043621?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2985369878244043621/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-08.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2985369878244043621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2985369878244043621'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-08.html' title='twitter より (2011-06-08)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1147595741987094567</id><published>2011-06-05T06:24:00.001+09:00</published><updated>2011-06-05T06:24:23.080+09:00</updated><title type='text'>twitter より (2011-06-04)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/76790869562363904"&gt;08:22&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;今さらなんだけど、Google Chrome のアイコンを見ていて、Google のロゴに使われている色が四色だと気付いた。一方、Google は6文字なので、青と赤が2度使われている。なんで四色なんだろう？&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1147595741987094567?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1147595741987094567/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-04.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1147595741987094567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1147595741987094567'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/06/twitter-2011-06-04.html' title='twitter より (2011-06-04)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5867413170305034618</id><published>2011-05-31T06:25:00.001+09:00</published><updated>2011-05-31T06:25:14.977+09:00</updated><title type='text'>twitter より (2011-05-30)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/74998171939520513"&gt;09:38&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;昔の知り合いと(すごく)久し振りに話すと、自分が歳をとったなあ、と実感する。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-5867413170305034618?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/5867413170305034618/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-30.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5867413170305034618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5867413170305034618'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-30.html' title='twitter より (2011-05-30)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-8647609671969350795</id><published>2011-05-17T12:07:00.001+09:00</published><updated>2011-05-17T12:07:33.756+09:00</updated><title type='text'>twitter より (2011-05-16)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/69823354760204289"&gt;02:55&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;近いうちに「解析概論」などが iPad で読めるようになるのか。 → &lt;a href="http://t.co/UnnLnvn"&gt;http://t.co/UnnLnvn&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-8647609671969350795?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/8647609671969350795/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-16.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8647609671969350795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8647609671969350795'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-16.html' title='twitter より (2011-05-16)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2618564932149875412</id><published>2011-05-16T21:13:00.001+09:00</published><updated>2011-05-16T21:13:25.982+09:00</updated><title type='text'>twitter より (2011-05-15)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/69569474851901440"&gt;10:07&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iOS デバイスたちのアップデート、いちいち Mac につないでやるのがメンドウ。セルフアップデート可能になって欲しいわ。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2618564932149875412?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2618564932149875412/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-15.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2618564932149875412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2618564932149875412'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/05/twitter-2011-05-15.html' title='twitter より (2011-05-15)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-8162042453093539557</id><published>2011-04-28T06:16:00.001+09:00</published><updated>2011-04-28T06:16:20.136+09:00</updated><title type='text'>twitter より (2011-04-27)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/63177530357710848"&gt;18:47&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;ER15 が届いた。これがファイナルだとのこと。長く続いたもんだ。14 か 13 あたりから見返さないとストーリーが思い出せないわ。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-8162042453093539557?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/8162042453093539557/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/04/twitter-2011-04-27.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8162042453093539557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8162042453093539557'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/04/twitter-2011-04-27.html' title='twitter より (2011-04-27)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2562714318538335330</id><published>2011-04-01T06:22:00.001+09:00</published><updated>2011-04-01T06:22:18.912+09:00</updated><title type='text'>twitter より (2011-03-31)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/53263790371905536"&gt;10:14&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;xcode もついに有料化か。なんでこんなことするのかねえ。&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2562714318538335330?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2562714318538335330/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/04/twitter-2011-03-31.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2562714318538335330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2562714318538335330'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/04/twitter-2011-03-31.html' title='twitter より (2011-03-31)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6003006166072638103</id><published>2011-03-31T15:27:00.002+09:00</published><updated>2011-03-31T15:27:23.193+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mini'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='imac'/><category scheme='http://www.blogger.com/atom/ns#' term='macbook'/><title type='text'>Snow Leopard アップデート (10.6.6 → 10.6.7)</title><content type='html'>&lt;p&gt;少し遅くなったが、うちの Mac たちの Mac OS X (Snow Leopard) をアップデートした。&lt;a href="http://logrepo.blogspot.com/2011/01/snow-leopard-1065-1066.html"&gt;前回(→ 10.6.6)&lt;/a&gt;から約 2.5 ヶ月。&lt;/p&gt;

&lt;h4&gt;iMac &amp;amp; MacBook&lt;/h4&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard (client) 10.6.7 update&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iMac (Mid 2010)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/DNHw0W2L6DMpP0dh7PIVrg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/_wobSevGXOh4/TZQZO9p9OXI/AAAAAAAABvM/bB29s_OsZGs/s800/snowleopard_client-10.6.7-update.png" height="630" width="512" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;サイズは 313.2MB。10.6.6 の2倍以上で、10.6.5 のときの半分。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;MacBook (Early 2008)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/C1KzBHguLgfE7Hmj21dq-A?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/_wobSevGXOh4/TZQZPrnN5RI/AAAAAAAABvQ/GTAtzd5VjkM/s800/snowleopard_client%28macbook%29_10.6.7-update.png" height="630" width="512" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;iMac と同じ 313.2MB。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;About パネルは以下のようになった。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard (client) 10.6.7 About dialog&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iMac (Mid 2010)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/VIo8rCc6OzLHKx3f_xAFJg?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/_wobSevGXOh4/TZQZQfUuTCI/AAAAAAAABvU/prEBiybQbE8/s800/snowleopard_client-10.6.7-about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;MacBook (Early 2008)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/ntct9A-GLzhl2AJYpVydCg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/_wobSevGXOh4/TZQZRle2JOI/AAAAAAAABvg/GR0kOlIPa0c/s800/snowleopard_client%28macbook%29_10.6.7-about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;Mac mini Server&lt;/h4&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard Server 10.6.7 update&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;Mac mini Server (Late 2009)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/B4Bs8zws8dbMyw6Fzkvikg?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/_wobSevGXOh4/TZQZQj6PfNI/AAAAAAAABvY/OKFqDOVmrXg/s800/snowleopard_server-10.6.7-update.png" height="729" width="512" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;サーバ版は 381.8MB。クライアントよりも少し大きめ。しばらくアップデートをさぼっていたので、OS 以外にもいろいろと並んでいる。Xcode アップデートがここに並ぶのは珍しい(ひょっとしたら、初めて見たかも)。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;以下は、Mac mini Server の About パネル。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard Server 10.6.7 About dialog&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;Mac mini Server (Late 2009)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="https://picasaweb.google.com/lh/photo/rWmDKjLSVc3dvLKBaB2JCA?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/_wobSevGXOh4/TZQZRMF6MYI/AAAAAAAABvc/zOkfm1ENVMM/s800/snowleopard_server-10.6.7-about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;カーネルバージョン&lt;/h4&gt;

&lt;p&gt;ついでにカーネルバージョンも記録しておく。&lt;/p&gt;

&lt;pre class="terminal"&gt;
[mini] mnbiwa% uname -a                                                   [~]
Darwin xanadu.private 10.7.0 Darwin Kernel Version 10.7.0: \
Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386
&lt;/pre&gt;

&lt;pre class="terminal"&gt;
[imac] mnbi% uname -a                                                     [~]
Darwin avalon.private 10.7.0 Darwin Kernel Version 10.7.0: \
Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386
&lt;/pre&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://internet.watch.impress.co.jp/docs/news/20110322_434354.html"&gt;「Mac OS X 10.6.7」公開、セキュリティ修正を含むアップデート&lt;/a&gt; (INTERNET Watch)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2011/01/snow-leopard-1065-1066.html"&gt;Snow Leopard アップデート (10.6.5 → 10.6.6)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/snow-leopard-1064-1065.html"&gt;Snow Leopard アップデート (10.6.4 → 10.6.5)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/11/snow-leopard-1062.html"&gt;Snow Leopard アップデート (→ 10.6.2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/imac.html"&gt;iMac、届いた！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2009/08/snow-leopardmacbook.html"&gt;Snow Leopard、MacBook にインストールした。&lt;/a&gt; (Macをプログラムする)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2009/08/snow-leopard.html"&gt;Snow Leopard、届いた。&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6003006166072638103?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6003006166072638103/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/03/snow-leopard-1066-1067.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6003006166072638103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6003006166072638103'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/03/snow-leopard-1066-1067.html' title='Snow Leopard アップデート (10.6.6 → 10.6.7)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/_wobSevGXOh4/TZQZO9p9OXI/AAAAAAAABvM/bB29s_OsZGs/s72-c/snowleopard_client-10.6.7-update.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5128175445174751935</id><published>2011-01-21T17:08:00.002+09:00</published><updated>2011-01-24T01:00:58.537+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><title type='text'>twitter より (2011-01-20)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/28024944721928192"&gt;18:44&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;プログラム(コード)よりも、コンテンツを売り物にする傾向は加速するばかりだ。ふと思ったんだけど、自分の撮った写真で時計アプリを作れたらウレシイのかね？ ウレシイのならそういうサービスが出てきてもおかしくないな。→ &lt;a href="http://t.co/YEpyJWK"&gt;http://t.co/YEpyJWK&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/28029090258755584"&gt;19:00&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;2月末か。日本でも同時期だとしても時期的にビミョーだな。→ &lt;a href="http://t.co/AKtZ6Rv"&gt;http://t.co/AKtZ6Rv&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-5128175445174751935?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/5128175445174751935/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/01/twitter-2011-01-20.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5128175445174751935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5128175445174751935'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/01/twitter-2011-01-20.html' title='twitter より (2011-01-20)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1607851802089030046</id><published>2011-01-20T20:32:00.002+09:00</published><updated>2011-01-20T20:32:25.030+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. 物読みファイル'/><category scheme='http://www.blogger.com/atom/ns#' term='数学'/><title type='text'>数学で犯罪を解決する ([著]キース・デブリン, ゲーリー・ローデン, [版]ダイヤモンド社)</title><content type='html'>&lt;h4&gt;数学なしには暮らせない&lt;/h4&gt;

&lt;p&gt;「数学なんて、日常生活で何の役に立つんだよ」と問われれば、「われわれは数学なしには暮らせない。知らないだけで、いつだって数学を使っているんだ」とチャーリーなら答えるだろう。チャーリーとは海外ドラマ「ナンバーズ」の主人公で、数学の天才。彼は著名な応用数学者にして、FBI の犯罪捜査に関するアドバイザーだ。&lt;/p&gt;

&lt;p&gt;なんだ、フィクションの話か、と思われるかもしれない。もちろんドラマ自体はフィクションだが、登場する数学(のかけら)はフィクションではないし、実際に犯罪捜査に応用されている事実もあるようだ。&lt;/p&gt;

&lt;p&gt;キース・デブリンによる「数学で犯罪を解決する」は、「ナンバーズ」で使われている数学を説明したものだ。これを読めば「ナンバーズ」自体はフィクションであっても、数学が犯罪捜査に役立つ(役立っている)ことがわかる。&lt;/p&gt;

&lt;h4&gt;数学は魔術？&lt;/h4&gt;

&lt;p&gt;ドラマの中で FBI の捜査官たちが求めるのは、数学を適用することで得られた分析の結果だ。犯人が誰なのか、どこに潜んでいるのか、どこを通って逃げようとするのか。つまりは、犯人を捕えるための決め手となる情報だ。それがどうやって導かれたものかは気にしない。劇中の捜査官たちは数学者(チャーリー)の説明を聞いて「魔術(ブードゥー)だ」とつぶやく。&lt;/p&gt;

&lt;p&gt;シーズン 2 のあるエピソードでは、FBI が霊能力者を協力者として使う様子が描かれる。チャーリーはそれに強く反発するが、(チャーリーの兄をふくめた)捜査官たちにとっては数学者も霊能力者も同じに見えているのだ。どちらも彼らには理解できない仕組みで隠された情報を暴く。&lt;/p&gt;

&lt;p&gt;充分に発達した科学技術は魔術と見分けがつかない、と言ったのはクラーク(→「&lt;a href="http://ja.wikipedia.org/wiki/クラークの三法則"&gt;クラークの三法則&lt;/a&gt;」)だが、数学はその最たるものかもしれない。&lt;/p&gt;

&lt;h4&gt;数学とはパターンを見つける方法&lt;/h4&gt;

&lt;p&gt;なぜ、数学を用いることで人の行動(犯罪者の逃走経路とか潜伏場所とか)を予測したり、言い当てることができるのだろう？&lt;/p&gt;

&lt;p&gt;「画像エンハンス」(Chapter 5) のように犯罪捜査(と裁判)の直接的な証拠となる応用例もあるが、「ナンバーズ」で用いられる数学の多くは人の行動を分析し予測するものだ。典型的なのはドラマの第1話の連続殺人犯の活動拠点(自宅と勤務先)の場所を絞り込む手法だろう(Chapter 1)。&lt;/p&gt;

&lt;p&gt;このエピソードでは、スプリンクラーから飛び出す水滴が落ちた場所からスプリンクラーの位置をつきとめる、という例え話で説明されている。同様に、複数の犯行現場から犯人の活動拠点をつきとめられる、というのがチャーリーの主張だ。物理法則だけにしたがう水滴と、自らの意思を持つ人の行動を同列に扱うことができるのはなぜだろう？&lt;/p&gt;

&lt;p&gt;その根拠となるのは人の行動には(たとえ本人がランダムに見せかけようとしたとしても)パターンが現れる、というものだ。加えて、人は「ランダム＝バラバラ」という偏見を持っている。犯行を重ねれば重ねるほどその場所は拠点の中心として同心円状に散らばっていく(実際の地理では地形や建物の状況、交通事情もあり円にはならない)。&lt;/p&gt;

&lt;p&gt;完全にランダムなデータからはどんなパターンも読み取ることはできない。けれど、人の行動は(そこに意思がからむ以上)完全なランダムにはなりえない。そこには必ずパターンが生まれる。そして数学はパターンを見つけることに長けた人々が築き上げてきた体系だ。パターンを見つけ、記述するための道具がそこにはある。&lt;/p&gt;

&lt;h4&gt;数学を知らなくても暮らせる&lt;/h4&gt;

&lt;p&gt;たしかにチャーリーの言うように、現代の生活では様々な面で数学が使われている。けれど、その恩恵を享受するだけなら数学を知る必要はない。インターネットで買い物をするのに暗号の仕組みを知る必要はない。微分方程式が解けなくたってクルマの運転に支障はない。これは数学に限らず、人類がこれまで蓄積してきたあらゆる知識体系について言えることだ。iPhone のアプリを使うのにプログラミングの技術が必要ないのも同じ理屈だ。&lt;/p&gt;

&lt;p&gt;もちろん、日常の生活の中でもパターンは生まれる。自分と自分の周囲のできごとを分析することでパターンを見つけ、将来に起きることを予測できるはずだ。しかし、そうする人は少ない。というか、そんな人はほとんどいないだろう。なぜなら、コストに見合わないから。データを収集し、分析し、パターンを見つけ、将来を予測する。どのステップにもコストがかかる。個人の生活では、そのコストに見合うだけの予測が必要になる場面はほとんどない。&lt;/p&gt;

&lt;p&gt;(チャーリーのような)数学の天才は別として、「数学が役に立つ」ではなくて「(自分で)数学を知っていることが役に立つ」場面というのは、日々の暮らしの中では見つけられないだろう。数学はオモシロイんだけどな。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/447800420X/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51bO5CLNotL._SL160_.jpg" alt="数学で犯罪を解決する" title="数学で犯罪を解決する" width="110" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/447800420X/mnbi-22/ref=nosim" target="_blank"&gt;数学で犯罪を解決する&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;キース・デブリン, ゲーリー・ローデン&lt;br /&gt;ダイヤモンド社 ( 2008-04-11 )&lt;br /&gt;ISBN: 9784478004203&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=447800420X" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;「ナンバーズ」は DVD で観ることもできる。以下は「数学で犯罪を解決する」に数学的あらすじが解説されているシーズン 3 までのもの。シーズンを重ねるごとに数学風味が薄れてくるように思う。ま、それは仕方ないことか。&lt;/p&gt;

&lt;div class="mm-small" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B001VCBZQE/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/512BVbynIfL._SL75_.jpg" alt="ナンバーズ 天才数学者の事件ファイル シーズン1" title="ナンバーズ 天才数学者の事件ファイル シーズン1" width="60" height="75" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:10px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B001VCBZQE/mnbi-22/ref=nosim" target="_blank"&gt;ナンバーズ 天才数学者の事件ファイル シーズン1&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;ロブ・モロー, デビッド・クラムホルツ, ジャド・ハーシュ, アリミ・バラード, サブリナ・ロイド / パラマウント ホーム エンタテインメント ジャパン ( 2009-06-12 )&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=B001VCBZQE" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-small" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B0029S98TU/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/515c7SO1v1L._SL75_.jpg" alt="ナンバーズ 天才数学者の事件ファイル シーズン2 コンプリートDVD-BOX" title="ナンバーズ 天才数学者の事件ファイル シーズン2 コンプリートDVD-BOX" width="60" height="75" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:10px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B0029S98TU/mnbi-22/ref=nosim" target="_blank"&gt;ナンバーズ 天才数学者の事件ファイル シーズン2 コンプリートDVD-BOX&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;ロブ・モロー, デビッド・クラムホルツ, ジャド・ハーシュ, アリミ・バラード, サブリナ・ロイド / パラマウント ホーム エンタテインメント ジャパン ( 2009-08-07 )&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=B0029S98TU" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-small" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B003BOFGDS/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51Q1S8Meo4L._SL75_.jpg" alt="ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 1" title="ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 1" width="60" height="75" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:10px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B003BOFGDS/mnbi-22/ref=nosim" target="_blank"&gt;ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 1&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;ロブ・モロー, デビッド・クラムホルツ, ジャド・ハーシュ, アリミ・バラード, ナビ・ラワット / パラマウント ホーム エンタテインメント ジャパン ( 2010-06-11 )&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=B003BOFGDS" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-small" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B003H9PJDY/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51-Mv9aC0eL._SL75_.jpg" alt="ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 2" title="ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 2" width="60" height="75" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:10px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/B003H9PJDY/mnbi-22/ref=nosim" target="_blank"&gt;ナンバーズ 天才数学者の事件ファイル シーズン3 コンプリートDVD-BOX Part 2&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;ロブ・モロー, デビッド・クラムホルツ, ジャド・ハーシュ, アリミ・バラード, ナビ・ラワット / パラマウント ホーム エンタテインメント ジャパン ( 2010-07-09 )&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=B003H9PJDY" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://ja.wikipedia.org/wiki/クラークの三法則"&gt;クラークの三法則&lt;/a&gt; (Wikipedia:ja)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1607851802089030046?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1607851802089030046/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/01/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1607851802089030046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1607851802089030046'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/01/blog-post.html' title='数学で犯罪を解決する ([著]キース・デブリン, ゲーリー・ローデン, [版]ダイヤモンド社)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4070778512162491367</id><published>2011-01-17T21:27:00.002+09:00</published><updated>2011-01-17T21:27:40.946+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>オフライン機能の実現 #6 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;GData API 経由で取得したフィードデータをローカルに保存できるようになり、すっかりオフライン機能が完成したと思い込んでいたが(→「&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成 - オフライン機能の実現 #5&lt;/a&gt;」)、実はまだ未完成だった。というのも、フィードを保存するだけでは、テキスト以外のリソースは保存されないからだ。フィードには記事のテキストしかふくまれていないから、たとえばスクリーンショット画像を表示するにはやはりネットにアクセスしなければならない。&lt;/p&gt;

&lt;p&gt;このブログで使っているテキスト以外のリソースは主に画像とスクリプトだ。画像はスクリーンショット等で使っているし、スクリプトは &lt;a href="http://logrepo.blogspot.com/2010/08/gist.html"&gt;Gist でコードを貼り付ける&lt;/a&gt;のに使っている。&lt;/p&gt;

&lt;p&gt;さて、この問題、どう解決したものだろう？&lt;/p&gt;

&lt;h4&gt;解決の方針&lt;/h4&gt;

&lt;p&gt;まず必要になるのは、リソース(画像等)をローカルストーレジの所定の場所に保存する仕組みだ。所定の場所はフィードと同様 &lt;span class="path"&gt;~/Library/BloggerGlass/&amp;lt;account&amp;gt;/&amp;lt;Blog ID&amp;gt;&lt;/span&gt; とする。ここにリソースのタイプごとにディレクトリを作って保存する。画像なら &lt;span class="path"&gt;images&lt;/span&gt;、スクリプトなら &lt;span class="path"&gt;scripts&lt;/span&gt; となる。このとき、ファイル名はリソースの URL から生成したハッシュ値を用いる。&lt;/p&gt;

&lt;p&gt;次に必要になるのは、フィードのテキストから対象となるリソースの URL を抜き出す仕組み。さらには、その URL をローカルなファイルへのものに置き換える仕組みも必要だ。これは、すでに&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html"&gt;内部リンクの置き換えの実現&lt;/a&gt;で似たような処理を作り込んでいる。ここにはいくつか工夫の余地がありそうだが、動かすだけなら素直な(ただし愚直な)方法で実現できる。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/オフライン機能の実現-2-macbloggerglass.html"&gt;オフライン機能の実現 #2 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html"&gt;内部リンクの置き換えを実現する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/gist.html"&gt;Gist を使って記事にコードを埋め込む&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4070778512162491367?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4070778512162491367/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/01/6-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4070778512162491367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4070778512162491367'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/01/6-macbloggerglass.html' title='オフライン機能の実現 #6 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2385274234919694986</id><published>2011-01-16T19:56:00.003+09:00</published><updated>2011-01-16T20:03:48.856+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mini'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='imac'/><category scheme='http://www.blogger.com/atom/ns#' term='macbook'/><title type='text'>Snow Leopard アップデート (10.6.5 → 10.6.6)</title><content type='html'>&lt;p&gt;Mac 用の App Store のスタートにあわせて、Mac OS X (Snow Leopard) のアップデートがリリースされた。10.6.5 から約 2 ヶ月と短い間隔のアップデートだが、これは App Store 対応のアップデートだから(だと思う)。&lt;/p&gt;

&lt;h4&gt;iMac &amp;amp; MacBook&lt;/h4&gt;

&lt;p&gt;iMac と MacBook (クライアント版)の場合。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard (client) 10.6.6 update&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iMac (Mid 2010)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/284wqeIxMnfs1Q0SJWALQA?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TTLIpazO_DI/AAAAAAAABt0/RdkerOq_3YI/s640/snowleopard_client-10.6.6_update.png" height="640" width="423" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;サイズは 114.8 MB。10.6.5 のときの 680 MB とくらべるとかなり小さめのアップデートだ。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;MacBook (Early 2008)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/BmGS-E1gh6zEHD6VtCO7ow?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TTLIrA4IDPI/AAAAAAAABuE/oyQs49fWk8o/s640/snowleopard_client%28macbook%29-10.6.6_update.png" height="640" width="431" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;MacBook 用のサイズも iMac と同じ 114.8 MB だった。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;About パネルは以下のようになった。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard (client) 10.6.6 About dialog&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iMac (Mid 2010)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/TCe_BGC1dSBetM2N2y_VqA?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TTLIpwi0K1I/AAAAAAAABt4/RnPfc3AOR_Q/s800/snowleopard_client-10.6.6_about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;MacBook (Early 2008)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/J3bGHT7mBJ3bf6GSvOvCFQ?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TTLIrq6pWUI/AAAAAAAABuI/1U96geD2Jtg/s800/snowleopard_client%28macbook%29-10.6.6_about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;Mac mini Server&lt;/h4&gt;

&lt;p&gt;サーバ版の場合。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard Server 10.6.6 update&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;Mac mini Server (Late 2009)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/YjNLktC67v-1pYPcq3_oaA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TTLIqbMouSI/AAAAAAAABt8/zB25pQaqJSo/s640/snowleopard_server-10.6.6_update.png" height="640" width="412" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;Server も client と同じサイズだ。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;以下は、Mac mini Server の About パネル。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Snow Leopard Server 10.6.6 About dialog&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;Mac mini Server (Late 2009)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/RmLSzkvpv6aVGU8ZJUu5_w?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TTLIqpajGbI/AAAAAAAABuA/cCD19P1emgU/s800/snowleopard_server-10.6.6_about.png" height="379" width="307" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;カーネルバージョン&lt;/h4&gt;

&lt;p&gt;例によって、カーネルバージョンも記録しておく。これまで同様(→「&lt;a href="http://logrepo.blogspot.com/2010/11/snow-leopard-1064-1065.html"&gt;Snow Leopard アップデート (10.6.4 → 10.6.5)&lt;/a&gt;」参照)、サーバとクライアントで同一になっている。&lt;/p&gt;

&lt;pre class="terminal"&gt;
[mini] mnbi% uname -a
Darwin mini.private 10.6.0 Darwin Kernel Version 10.6.0: \
Wed Nov 10 18:13:17 PST 2010; root:xnu-1504.9.26~3/RELEASE_I386 i386
&lt;/pre&gt;

&lt;pre class="terminal"&gt;
[imac] mnbi% uname -a
Darwin imac.private 10.6.0 Darwin Kernel Version 10.6.0: \
Wed Nov 10 18:13:17 PST 2010; root:xnu-1504.9.26~3/RELEASE_I386 i386
&lt;/pre&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://internet.watch.impress.co.jp/docs/news/20110107_418619.html"&gt;「Mac OS X 10.6.6」公開、Mac App Storeが利用可能に&lt;/a&gt; (INTERNET Watch)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/snow-leopard-1064-1065.html"&gt;Snow Leopard アップデート (10.6.4 → 10.6.5)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/11/snow-leopard-1062.html"&gt;Snow Leopard アップデート (→ 10.6.2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/imac.html"&gt;iMac、届いた！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2009/08/snow-leopardmacbook.html"&gt;Snow Leopard、MacBook にインストールした。&lt;/a&gt; (Macをプログラムする)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2009/08/snow-leopard.html"&gt;Snow Leopard、届いた。&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2385274234919694986?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2385274234919694986/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/01/snow-leopard-1065-1066.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2385274234919694986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2385274234919694986'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/01/snow-leopard-1065-1066.html' title='Snow Leopard アップデート (10.6.5 → 10.6.6)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TTLIpazO_DI/AAAAAAAABt0/RdkerOq_3YI/s72-c/snowleopard_client-10.6.6_update.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3896631992499927401</id><published>2011-01-15T14:21:00.003+09:00</published><updated>2011-01-15T14:21:47.003+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. 物読みファイル'/><title type='text'>最終定理 ([著]アーサー・C・クラーク, フレデリック・ポール, [版]早川書房)</title><content type='html'>&lt;p&gt;言わずと知れたクラーク最後の長編。タイトルの最終定理とは&lt;a href="http://ja.wikipedia.org/wiki/フェルマーの最終定理"&gt;フェルマーの最終定理&lt;/a&gt;のこと。数百年にわたり数学者を悩ました問題の 1 つで、1995 年にワイルズによって解決されたあれだ。数学者でなくとも数学好きなら中学や高校のときに一度は目にしたことのある定理だろう。&lt;/p&gt;

&lt;h4&gt;2 つの物語&lt;/h4&gt;

&lt;p&gt;これは 2 つの物語が(ほぼ)並列に語られる作品だ。1 つは主人公となるスリランカの数学者の半生を描くもの。もう 1 つは銀河間スケールで活動する知性体による人類粛清の物語。&lt;/p&gt;

&lt;p&gt;主人公のランジットは数学を志す学生だったが、ひょんなことから海賊の一員と間違われ投獄、拷問の憂き目に会い、友人の尽力で解放され、最終定理の「簡潔な」証明を発見したことで著名な数学者となり、美人と結婚し、娘と息子に恵まれ、幸せに暮らす。&lt;/p&gt;

&lt;p&gt;一方、いくつもの銀河に君臨するグランド・ギャラクティクスは、核をもてあそび始めた地球人類を有害な存在とみなし、従属する種属にその粛清を命じる。が、土壇場で気を変え、人類は滅亡を免れる。&lt;/p&gt;

&lt;p&gt;2 つの物語は終盤でランジットの娘を接点として絡み合う。この点を除いて、2 つの物語は独立していると言って良い。ランジットはグランド・ギャラクティクスの決定に対して何ら特別な影響を及ぼさないし、グランド・ギャラクティクスとその従属種属は取りたてて(最終定理をふくむ)数学に興味を示しはしない。&lt;/p&gt;

&lt;p&gt;絡むようで絡まない 2 つの物語。それがこの作品の特徴だ。&lt;/p&gt;

&lt;h4&gt;SF らしさ&lt;/h4&gt;

&lt;p&gt;SF として見るなら、本作はこれまで SF が生み出してきた楽天的なアイデアの数々を盛り込んだ作品になっている。世界政府しかり、宇宙エレベータしかり。さまざまな銀河種族に君臨するオーバーロード(超知性)も、今では懐しく感じる。これらを題材としたクラークの他の作品を思い出すのも楽しい。&lt;/p&gt;

&lt;p&gt;また、あわや人類滅亡かという瀬戸際まで行くものの、最後は大団円で終わるところも、読後感すっきりの古き良き SF を思い出させる。&lt;/p&gt;

&lt;h4&gt;クラークと言うよりはポールの作品&lt;/h4&gt;

&lt;p&gt;全体の語り口を通して感じられるのは、&lt;a href="http://ja.wikipedia.org/wiki/フレデリック・ポール"&gt;フレデリック・ポール&lt;/a&gt;の作風。ことに異星人の描写ではそれが顕著だ。どちらかと言うと淡々と事実を叙述するクラークのそれとは異なり、描き方に起伏が感じられる。読んでいて「ゲートウェイ」のことを思い出した。とくに終盤で主人公(をふくむ人類)がマシンの中(のシミュレートされた環境)で生き続けるという辺りではその思いを強くした。&lt;/p&gt;

&lt;p&gt;解説によれば、本作は、クラークが基本的なアイデアと原稿の一部をポールにわたし、ポールがそれをもとに書き上げ、クラークがチェックをして完成したとのこと。ポール風味になっていて当然か。&lt;/p&gt;

&lt;p&gt;かといって、クラークらしさが感じられないかと言うとそうではない。とくに主人公が退屈な大学の授業の中で天文学の講義に魅せられる様子や、スカイフック(宇宙エレベータ)が登場する辺りは、クラークの SF 作品に多く見られる(というよりクラーク自身の)「宇宙への憧れ」がストレートに表われている。&lt;/p&gt;

&lt;h4&gt;不満は……&lt;/h4&gt;

&lt;p&gt;唯一の不満点はタイトルになっている「最終定理」が、物語の展開上、ほとんど意味のないこと。暴虐な異星人を撃退するために使われることもなければ、異質な知性とのコミュニケーションに必要なわけでもない。ただ、その証明が主人公のキャリアの転回点になったというだけの扱い。看板に偽りありだ。&lt;/p&gt;

&lt;p&gt;ストーリー上、主人公が数学者である必然性はまったくないし、&lt;a href="http://ja.wikipedia.org/wiki/フェルマーの最終定理"&gt;フェルマーの最終定理&lt;/a&gt;を持ち出す必要も感じられない。タイトルから&lt;a href="http://ja.wikipedia.org/wiki/グレッグ・イーガン"&gt;イーガン&lt;/a&gt;の「ディアスポラ」ようなものを期待していると裏切られることになる。&lt;/p&gt;

&lt;p&gt;この点に目をつむれば、読んでいて楽しく、読み終わって爽快な良作 SF だ。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4270003359/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/511s2enDkHL._SL160_.jpg" alt="宇宙旅行はエレベーターで" title="宇宙旅行はエレベーターで" width="109" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4270003359/mnbi-22/ref=nosim" target="_blank"&gt;宇宙旅行はエレベーターで&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;ブラッドリー C エドワーズ, フィリップ レーガン&lt;br /&gt;武田ランダムハウスジャパン ( 2008-04-23 )&lt;br /&gt;ISBN: 9784270003350&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4270003359" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本作中で「スカイフック」として登場する宇宙エレベータについて知りたいならこの本を読むと良い。&lt;/p&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/415011546X/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51Z3TGQR7GL._SL160_.jpg" alt="楽園の泉 (ハヤカワ文庫SF)" title="楽園の泉 (ハヤカワ文庫SF)" width="112" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/415011546X/mnbi-22/ref=nosim" target="_blank"&gt;楽園の泉 (ハヤカワ文庫SF)&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;アーサー・C. クラーク&lt;br /&gt;早川書房 ( 2006-01 )&lt;br /&gt;ISBN: 9784150115463&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=415011546X" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;宇宙エレベータを描いた SF ならこれ。シェフィールドの「星ぼしに架ける橋」(ハヤカワ文庫 SF)も有名だけど、こちらは現在では入手困難。&lt;/p&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4150115311/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51YA8DR2SCL._SL160_.jpg" alt="ディアスポラ (ハヤカワ文庫 SF) / 山岸 真" title="ディアスポラ (ハヤカワ文庫 SF) / 山岸 真" width="110" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4150115311/mnbi-22/ref=nosim" target="_blank"&gt;ディアスポラ (ハヤカワ文庫 SF) / 山岸 真&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;グレッグ・イーガン&lt;br /&gt;早川書房 ( 2005-09-22 )&lt;br /&gt;ISBN: 9784150115319&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4150115311" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;こちらは数学が物語に織り込まれた作品。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://googleblog.blogspot.com/2008/03/appreciation-of-arthur-c-clarke.html"&gt;An appreciation of Arthur C. Clarke&lt;/a&gt; (Official Google Blog; 90 歳のクラークのスピーチが見られる)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://slashdot.jp/science/article.pl?sid=08/03/19/0030231"&gt;SF作家Arthur C. Clarke死去&lt;/a&gt; (スラッシュドット・ジャパン)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ja.wikipedia.org/wiki/フレデリック・ポール"&gt;フレデリック・ポール&lt;/a&gt; (Wikipedia:ja)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ja.wikipedia.org/wiki/フェルマーの最終定理"&gt;フェルマーの最終定理&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ja.wikipedia.org/wiki/グレッグ・イーガン"&gt;グレッグ・イーガン&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/04/sf.html"&gt;知性化宇宙シリーズ ([著]デイヴィッド・ブリン, [訳]酒井昭伸, ハヤカワ文庫SF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/12/sf1272.html"&gt;キリンヤガ ([著]マイク・レズニック, [訳]内田昌之, ハヤカワ文庫SF1272)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/11/ja930.html"&gt;フリーランチの時代 ([著]小川一水, [版]ハヤカワ文庫JA930)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3896631992499927401?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3896631992499927401/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2011/01/c.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3896631992499927401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3896631992499927401'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2011/01/c.html' title='最終定理 ([著]アーサー・C・クラーク, フレデリック・ポール, [版]早川書房)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1574488520831972232</id><published>2010-12-28T19:06:00.002+09:00</published><updated>2010-12-28T19:06:55.960+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='zsh'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><title type='text'>ZSH のパス設定</title><content type='html'>&lt;p&gt;以前、MacBook と Mac mini Server の間で &lt;kbd&gt;git clone&lt;/kbd&gt; しようとして失敗したことがあった(→「&lt;a href="http://logrepo.blogspot.com/2009/11/macbook-git.html"&gt;MacBook にも Git を (あるいはリモートリポジトリの準備)」&lt;/a&gt;)。原因は、リモート側のシェルの PATH 設定だった。git でリモートリポジトリから SSH 経由で clone しようとする場合、リモート側で(SSH 経由で) git コマンドが実行される。リモート側のシェルのパスに git コマンドをふくむディレクトリが入っていなければ、git コマンドが見つからずエラーになる。&lt;/p&gt;

&lt;p&gt;シェルは起動時に設定ファイルを読み込む(実行する)ことで実行環境を整える。このとき、たいていのシェルでは実行モードによって読み込む設定ファイルが変わる。実行モードというのはログインシェル、対話シェル、そしてスクリプト実行用シェルの 3 つのことで、&lt;kbd&gt;git clone&lt;/kbd&gt; で起動されるシェルはスクリプト実行用シェルになる。このため、たとえ ssh コマンドでリモートログインした状態で対話的に git コマンドを使えたとしても、&lt;kbd&gt;git clone&lt;/kbd&gt; しようとすると (git コマンドが見つからず)エラーになることがある。&lt;/p&gt;

&lt;p&gt;今日も、MacBook と iMac の間で &lt;kbd&gt;git clone&lt;/kbd&gt; しようとして同じエラーが出て失敗した。以前解決したはずの問題にまた遭遇したことになる。というのも、あれからしばらくしてシェルを zsh に変えたからだ(→「&lt;a href="http://logrepo.blogspot.com/2010/08/twitter-2010-08-03.html"&gt;twitter より (2010-08-03)&lt;/a&gt;」)。最初の設定のときに、ssh コマンドでリモートログインしてローカルなシェルの場合と同様に使えることを確認して安心していた。今日まで &lt;kbd&gt;git clone&lt;/kbd&gt; のようなリモートのコマンドを直接実行することはなかったから、この問題に気付かなかった。&lt;/p&gt;

&lt;p&gt;では、zsh を使う場合リモート(のスクリプト実行用)のシェルが参照するパスを設定するにはどうすれば良いのか？ より具体的に言えば、git のリモートリポジトリを置いたコンピュータのシェルが zsh のとき、&lt;kbd&gt;git clone&lt;/kbd&gt; 等が失敗しないようにするにはどうすれば良いのか？&lt;/p&gt;

&lt;h4&gt;答えは ~/.zshenv&lt;/h4&gt;

&lt;p&gt;結論を先に書くと、スクリプト用シェルのために PATH を設定するには &lt;span class="path"&gt;~/.zshenv&lt;/span&gt; に書けば良い。書式は通常のシェルスクリプトと同じ。つまり、&lt;kbd&gt;git clone&lt;/kbd&gt; が失敗する問題を解決するためには、リモート側(clone されるリポジトリがある方)に &lt;span class="path"&gt;~/.zshenv&lt;/span&gt; を作り、以下のように PATH の設定をしてやれば良い(git コマンドを MacPorts でインストールしている場合)。&lt;/p&gt;

&lt;pre class="prettyprint lang-sh"&gt;
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
&lt;/pre&gt;

&lt;p&gt;これまで、zsh のためのパスは &lt;span class="path"&gt;~/.zshrc&lt;/span&gt; で設定してきた。この方法どこが悪かったのだろう？&lt;/p&gt;

&lt;p&gt;それは実行モードごとの設定ファイルの読み込みが以下のようになっているからだ(→「&lt;a href="http://journal.mycom.co.jp/column/zsh/001/index.html"&gt;【コラム】漢のzsh (1) 最強のシェル、それは「zsh」 | マイコミジャーナル&lt;/a&gt;」参照)。一言で言えば、&lt;span class="path"&gt;~/.zshrc&lt;/span&gt; はスクリプト実行用シェルのときは読み込まれない。&lt;/p&gt;

&lt;table&gt;
&lt;caption&gt;zsh の設定ファイル読み込み順序&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
  &lt;th&gt;実行モード&lt;/th&gt;
  &lt;th&gt;読み込み順&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;ログインシェル&lt;/th&gt;
  &lt;td&gt;
    &lt;ol&gt;
      &lt;li&gt;~/.zshenv&lt;/li&gt;
      &lt;li&gt;~/.zprofile&lt;/li&gt;
      &lt;li&gt;~/.zshrc&lt;/li&gt;
      &lt;li&gt;~/.zlogin&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;対話シェル&lt;/th&gt;
  &lt;td&gt;
    &lt;ol&gt;
      &lt;li&gt;~/.zshenv&lt;/li&gt;
      &lt;li&gt;~/.zshrc&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;スクリプト用シェル&lt;/th&gt;
  &lt;td&gt;
    &lt;ol&gt;
      &lt;li&gt;~/.zshevn&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;これを見れば、スクリプト用シェルが参照する PATH は &lt;span class="path"&gt;~/.zshenv&lt;/span&gt; で設定しなければならないことがわかる。&lt;/p&gt;

&lt;h4&gt;~/.zshenv を読むより前に起きていること&lt;/h4&gt;

&lt;p&gt;さて、これで MacBook と iMac の間で &lt;kbd&gt;git clone&lt;/kbd&gt; に失敗する問題は解決できたが、ついでに zsh における PATH の設定について調べてみた。&lt;/p&gt;

&lt;p&gt;zsh の man ページによれば、シェルが起動時に最初に読み込むのは &lt;span class="path"&gt;/etc/zshenv&lt;/span&gt; だとのこと。その後、上述の表のような順序で設定を読み込む。では、&lt;span class="path"&gt;/etc/zshenv&lt;/span&gt; はどうなっているのか？&lt;/p&gt;

&lt;p&gt;Mac OS X 10.6.5 の &lt;span class="path"&gt;/etc/zshenv&lt;/span&gt; の内容を以下に示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-sh"&gt;
# system-wide environment settings for zsh(1)
if [ -x /usr/libexec/path_helper ]; then
 eval `/usr/libexec/path_helper -s`
fi
&lt;/pre&gt;

&lt;p&gt;ここに書いてあるのは &lt;span class="path"&gt;/usr/libexec/path_helper&lt;/span&gt; があれば、それを実行しろというもの(バッククォート記法なので、正確には path_helper の実行結果をスクリプトとして実行しろ、となる)。&lt;/p&gt;

&lt;p&gt;この path_helper というコマンドは PATH を設定するためのシェルスクリプトを生成するためのもので -s オプションで B-shell 用のスクリプトが作られる。man ページには直接実行するものじゃないゾ、と書いてあるが、そこをあえて実行してみた結果を以下に示す。実行の前に PATH と MANPATH の設定を消しているのは、このコマンドが現在の設定に追加するようになっているから。空にしておくことで、zsh が &lt;span class="path"&gt;/etc/zshenv&lt;/span&gt; でこのコマンドを実行したときの結果がわかる。&lt;/p&gt;

&lt;pre class="terminal"&gt;
[imac] mnbi% PATH=''
[imac] mnbi% MANPATH=''
[imac] mnbi% /usr/libexec/path_helper -s
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin"; export PATH;
MANPATH="/usr/share/man:/usr/local/share/man:/usr/X11/share/man"; export MANPATH;
&lt;/pre&gt;

&lt;p&gt;では、path_helper は PATH (と MANPATH) に入れるべきディレクトリをどうやって知るのだろう？ man ページによれば、それは &lt;span class="path"&gt;/etc/paths&lt;/span&gt; と &lt;span class="path"&gt;/etc/manpaths&lt;/span&gt; だとのこと。そして、ここに書かれたものに、さらに  &lt;span class="path"&gt;/etc/paths.d&lt;/span&gt; と &lt;span class="path"&gt;/etc/manpaths.d&lt;/span&gt; に置かれたファイルの内容を付け加えて、PATH と MANPATH とする。&lt;/p&gt;

&lt;p&gt;以下に、Mac OS X 10.6.5 の  &lt;span class="path"&gt;/etc/paths&lt;/span&gt; の内容を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-sh"&gt;
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
&lt;/pre&gt;

&lt;p&gt;同じく、Mac OS X 10.6.5 では &lt;span class="path"&gt;/etc/paths.d&lt;/span&gt; には &lt;span class="path"&gt;X11&lt;/span&gt; というファイルがあり、その内容は以下のようになっている。&lt;/p&gt;

&lt;pre class="prettyprint lang-sh"&gt;
/usr/X11/bin
&lt;/pre&gt;

&lt;p&gt;以上のことから、&lt;span class="path"&gt;~/.zshenv&lt;/span&gt; を使ってユーザごとにパスを設定する以外にも、追加したいパスを書いたファイルを &lt;span class="path"&gt;/etc/paths.d&lt;/span&gt; に置けば、システム全体で(正確には zsh を使っているすべてのユーザで)同様の効果が得られることがわかる。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://journal.mycom.co.jp/column/zsh/001/index.html"&gt;【コラム】漢のzsh (1) 最強のシェル、それは「zsh」&lt;/a&gt; (マイコミジャーナル)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/11/macbook-git.html"&gt;MacBook にも Git を (あるいはリモートリポジトリの準備)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/twitter-2010-08-03.html"&gt;twitter より (2010-08-03)&lt;/a&gt; (iMac が届いた記念にシェルを tcsh から zsh に変えたというつぶやき)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1574488520831972232?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1574488520831972232/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/zsh.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1574488520831972232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1574488520831972232'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/zsh.html' title='ZSH のパス設定'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3265500021662952450</id><published>2010-12-27T19:43:00.002+09:00</published><updated>2010-12-27T19:43:46.380+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>NSTableView でソートしたとき、選択された要素をモデル配列から正しく取り出す方法</title><content type='html'>&lt;p&gt;NSTableView では、見出し行をクリックするとカラムの内容によって行をソートすることができる。ところが、これはモデル(となっている NSMutableArray)をソートしているわけではないから、ソートしたビュー上の並び順とモデル内の要素の並び順は一致していない。&lt;/p&gt;

&lt;p&gt;たとえば、もともと「りんご」「みかん」「ぶどう」の順にモデル配列の中に収められているとして、これをビュー上でソートして「ぶどう」「みかん」「りんご」の順に表示されているとする。ここでビュー上で「りんご」を選択すると、そのインデックスは(0 から数えるので) 2 となる。このインデックスを使い、モデル配列から objectAtIndex: で要素を取り出せば、それは「りんご」ではなく「ぶどう」になってしまう。&lt;/p&gt;

&lt;p&gt;つまり、ビューでソートした状態でビュー上の選択(のインデックス)に合わせてモデルの要素を取り出すと、ビューの選択とは異なる要素を取り出してしまうことになるのだ。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass の場合で言うと、記事の一覧をタイトルまたは日付けでソートした状態で一覧から記事の選択を行うと、表示される記事の内容がずれてしまう。&lt;/p&gt;

&lt;p&gt;こうしたビューとモデルにおける要素の並び順の不一致を避けるには、両者を結びつけている NSArrayController を使えば良い。&lt;/p&gt;

&lt;p&gt;具体的には、NSArrayController から selectedObjects で要素オブジェクト(の配列)を取り出せば、ビューでソートしているかどうかに関係なく、適切なオブジェクト(つまりビュー上の選択と同じオブジェクト)が取り出せる。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass の場合、以下のようなコードを使う(AppController.m より抜粋)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
    Entry *entry = [[feedController selectedObjects] objectAtIndex:0];
&lt;/pre&gt;

&lt;p&gt;feedController が記事一覧(と記事内容)用のモデル配列のための NSArrayController だ。AppController の IBOutlet として確保し、Interface Builder で NSArrayController のインスタンスと結びつけている。記事一覧では複数選択を許していないから、selectedObjects の返す配列の最初の要素を取り出している。&lt;/p&gt;

&lt;p&gt;ちなみに、この NSArrayController を使う解決方法はググって見つけた(→&lt;a href="http://cocoadays.blogspot.com/2010/12/nsarraycontroller-nstableview.html"&gt;Cocoaの日々: NSArrayController を使った NSTableView で選択行の情報を取得する&lt;/a&gt;)&lt;/p&gt;

&lt;h4&gt;NStableView と NSArrayController の関係&lt;/h4&gt;

&lt;p&gt;では、なぜ NSArrayController からは、ソートの状態に関係なく、適切なモデルオブジェクトを取り出すことができるのだろうか？&lt;/p&gt;

&lt;p&gt;オブジェクトの配列をモデルとして、その内容を NSTableView に表示させる場合(配列の要素になっているオブジェクトのプロパティを表のカラムに表示させる、という意味)、Cocoa Bindings では両者を NSArrayController で結びつける。&lt;/p&gt;

&lt;p&gt;これは詳細に言えば、NSTableColumn の値として、NSArrayController の arrangedObjects が返す配列の要素のプロパティを結びつける、ということだ。初期状態では、arrangedObjects はモデルの配列の並び順そのままの配列を返す。だから、モデル配列の順序がそのままビューの表示順序になる。一方、NSTableView の見出し行でソートしたときは、NSArrayController の arrangedObjects が返す配列の並び順が変わる。その結果、ビューの表示順序が変わる。&lt;/p&gt;

&lt;p&gt;実はビュー(NSTableView)の行がソートされるのはむしろ結果で、実際にはコントローラ(NSArrayController)が返す要素の順序がソートされていたのだ。これは、NSArrayController がソートされた状態のモデル配列を保持している、と言っても良い。だから、ビュー上でソートされた状態のインデックスを使っても、適切なオブジェクトを取り出すことができるのだ。&lt;/p&gt;

&lt;p&gt;では、なぜ NSArrayController の arrangedObjects の並び順が変わるのか？ それは、NSTableView の見出し行がクリックされたときに NSArrayController に対して setSortDescriptors が呼ばれ、NSSortDescriptor がセットされることによる。これはソートの方法を指示するオブジェクトで、NSArrayController は arrangedObjects を作り出す際に参照するものだ。初期状態ではこれがセットされていないため(ソートされず)、arrangedObjects はモデルの要素順と同じ順序の配列になっている。&lt;/p&gt;

&lt;li&gt;見出しをクリックしてからの一連の動きを時系列で並べると以下のようになる。&lt;/li&gt;

&lt;ol&gt;
&lt;li&gt;NSTableView の見出しがクリックされる。&lt;/li&gt;
&lt;li&gt;NSTableView が NSArrayController に対して、NSSortDescriptor をセットする。&lt;/li&gt;
&lt;li&gt;NSTableView (実際には NSTableColumn) が NSArrayController に対して arrangedObjects を要求する(メソッドを呼び出す)。&lt;/li&gt;
&lt;li&gt;NSArrayController は先にセットされた NSSortDescriptor にしたがってソートした配列を返す。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;まとめると、ソートはビューで起きているのではなく、コントローラの内部で起きている、ということになる。だから、コントローラはソートされた状態のインデックスから適切なオブジェクトを取り出すことができる、と。&lt;/p&gt;

&lt;h4&gt;iOS アプリではどうする？&lt;/h4&gt;

&lt;p&gt;Cocoa Bindings が使えない iOS アプリでは NSArrayController に相当する部分を自前で用意しなければならないはず。なんだか面倒なことになりそうな予感がする。ソートしなければ(できないようにすれば)良いんだけど。そもそも、iOS のアプリで表をソートするアプリって見かけないような……。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://cocoadays.blogspot.com/2010/12/nsarraycontroller-nstableview.html"&gt;NSArrayController を使った NSTableView で選択行の情報を取得する&lt;/a&gt; (Cocoaの日々)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSArrayController_Class/Reference/Reference.html"&gt;NSArrayController Class Reference&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSTableView_Class/Reference/Reference.html"&gt;NSTableView Class Reference&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass.html"&gt;記事の表示機能を作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3265500021662952450?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3265500021662952450/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/nstableview.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3265500021662952450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3265500021662952450'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/nstableview.html' title='NSTableView でソートしたとき、選択された要素をモデル配列から正しく取り出す方法'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4523485392174874757</id><published>2010-12-25T09:30:00.000+09:00</published><updated>2010-12-25T09:30:00.826+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>進捗状況を表示する (MacBloggerGlass)</title><content type='html'>&lt;p&gt;フィードをネットから取得する際には、ブログの記事数にもよるが、やはり多少の時間(数秒)がかかる。その間、ユーザにフィードバックがないのはユーザ体験上の問題だ。GAE 版ではブラウザが矢印を回したり、アドレスバーを塗り潰したりして「ネットにアクセスしてますよ」と主張してくれるが、Cocoa 版ではこれも自前でどうにかしなければならない。&lt;/p&gt;

&lt;h4&gt;フィードバックの方法&lt;/h4&gt;

&lt;p&gt;今回は、進捗状況をフィードバックする仕組みとしては、最も手軽な部類の「ステータスメッセージの表示」を作り込むことにした。&lt;/p&gt;

&lt;p&gt;メッセージを表示する場所は、Safari などでもお馴染のアプリウィンドウの最下部。ここにテキスト 1 行分の領域を空け、ラベル (NSTextField) を配置する。あとは、アプリコントローラで進捗に合わせてメッセージを setStringValue: してやれば良い。&lt;/p&gt;

&lt;p&gt;問題は進捗状況をどうやって知るのか。実際のフィードの取得は GData ライブラリの中でアプリの実行ループとは並列に実行されている。スレッドなのか、あるいは他の仕組みなのかまではまだ調べていないが、便宜上、別タスクと呼んでおく。このフィード取得用の別タスクが、アプリに対してコールバックを呼ぶなどして途中の状況を知らせてくれれば簡単なのだが、そういう仕組みは GData ライブラリには用意されていない。データをアップロードする場合には、そういうものがあるが、取得(ダウンロード)時にはないようだ。&lt;/p&gt;

&lt;p&gt;しばらく悩んだ後、何も難しく考えることはないと気付いた。フィードの取得が完了したことは GData ライブラリからのコールバックでわかる。ならば、取得を開始するときにタイマで定期的に進捗していることを知らせるメッセージを表示させ、完了したらそれを止めれば良い。「ネットにアクセスしているよ」を知らせるだけなら、これで十分だ。&lt;/p&gt;

&lt;h4&gt;タイマを使った遅延実行&lt;/h4&gt;

&lt;p&gt;NSObject には、指定した時間間隔の後、オブジェクトに対してメッセージを送る(つまり、そのオブジェクトでメソッドを実行する)仕組みが存在する。それが以下のメソッドだ。&lt;/p&gt;

&lt;pre&gt;
- (void)performSelector:(SEL)aSelector
             withObject:(id)anArgument
             afterDelay:(NSTimeInterval)delay;
&lt;/pre&gt;

&lt;p&gt;delay には遅延させる時間を秒単位で指定するが、NSTimeInterval は実は double 型なので 1.0 や 10.0 のように実数(というか浮動小数)で指定する。&lt;/p&gt;

&lt;p&gt;他にもときどき見かけるけど、基本型を typedef しただけの型っていうのは何か落ち着かない。クラスにしてファクトリなりイニシャライザを用意して欲しいよ。即値をどう指定したものか迷うから。NSTimeInterval なら ファクトリに intervalWithSeconds:(NSUInteger)sec とかがあれば迷わずにすむ。書くのは少しメンドウだけど。&lt;/p&gt;

&lt;h4&gt;遅延実行を使った進捗メッセージの表示&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;フィードの取得開始時にメッセージとして「Loading.」を表示&lt;/li&gt;
  &lt;li&gt;一定時間後にメッセージを更新するようにタイマをセット&lt;/li&gt;
  &lt;li&gt;メッセージの更新では、「.」を追加したメッセージを表示するとともに、再び一定時間が経過したらメッセージの更新するようにタイマをセット&lt;/li&gt;
  &lt;li&gt;取得が完了したら、メッセージの更新を停止した上で、「(Complete)」を追加したメッセージを表示&lt;/li&gt;
  &lt;li&gt;ある程度時間が経過したら、メッセージを消す。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;つまり、最初に「Loading.」と表示され、その後時間の経過とともに「Loading...」と「.」が増えて行き、完了した時点で「Loading........(Complete)」と表示される、というものだ。&lt;/p&gt;

&lt;h4&gt;フィードの取得開始と完了を通知する&lt;/h4&gt;

&lt;p&gt;あと少し問題が残っている。それはフィードの取得開始をどうやってアプリコントローラ(AppController)に伝えるか、というもの。&lt;/p&gt;

&lt;p&gt;実際にネットからフィードを取得するかどうかは、FeedManager だけが知っている(→「&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成&lt;/a&gt;」)。アプリコントローラから「このフィードをよこせ」と言われたとき、すでにメモリ中に FeedBlogPost クラスのインスタンスがあればそれを返すし、インスタンスがないときにもすぐにはネットから取得せず、まずはローカルのストレージ中で保存されたフィードを探す。そこにもないとなって、ようやくネットから取得することになる。この動きは FeedManager 中にカプセル化されていて、アプリコントローラからは見えない。&lt;/p&gt;

&lt;p&gt;FeedManager をアプリコントローラに依存するようにはしたくなかったので、通知(NSNotification)を使って、取得の開始と完了を知らせることにした(→「&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;設定変更を通知する&lt;/a&gt;」)。&lt;/p&gt;

&lt;p&gt;FeedManager の実装部で開始を通知する部分のコードがこれだ。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)fetchPostsForReceiver:(id)feedReceiver
                         blog:(EntryBlog *)blog
                      account:(NSString *)account
                     password:(NSString *)password {
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    // send notification
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:blog
                                                         forKey:@"fetchingBlog"];
    [nc postNotificationName:BGKeyPostsLoadStartNotification
                      object:self userInfo:userInfo];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}
&lt;/pre&gt;

&lt;p&gt;同様に完了を通知するコードは以下になる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)postsTicket:(GDataServiceTicket *)ticket
   finishedWithFeed:(GDataFeedBlogPost *)gfeed
              error:(NSError *)error {
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    // send notification
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc postNotificationName:BGKeyPostsLoadCompleteNotification object:self];
}
&lt;/pre&gt;

&lt;h4&gt;実装: 進捗メッセージの表示&lt;/h4&gt;

&lt;p&gt;実際に進捗メッセージを表示するアプリコンントローラの実装を以下に示す(AppController.m より抜粋)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)awakeFromNib {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(handleBlogIDChange:)
               name:BGKeyBlogIDChangeNotification object:nil];

    [nc addObserver:self selector:@selector(handleFeedLoadStart:)
               name:BGKeyPostsLoadStartNotification object:nil];
    [nc addObserver:self selector:@selector(handleFeedLoadComplete:)
               name:BGKeyPostsLoadCompleteNotification object:nil];

    [statusMessage setStringValue:@&amp;quot;&amp;quot;];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}

- (void)handleFeedLoadStart:(NSNotification *)note {
    EntryBlog *blog = [[note userInfo] objectForKey:@&amp;quot;fetchingBlog&amp;quot;];
    [self startIndicatorWithMessage:[NSString
                                     stringWithFormat:@&amp;quot;%@: Loading.&amp;quot;,
                                     blog.title]];
}

- (void)handleFeedLoadComplete:(NSNotification *)note {
    [NSObject cancelPreviousPerformRequestsWithTarget:self
                                             selector:@selector(handleUpdateIndicator:)
                                               object:nil];
    NSString *message =
    [NSString stringWithFormat:@&amp;quot;%@(Complete)&amp;quot;, [statusMessage stringValue]];
    [statusMessage setStringValue:message];

    [self performSelector:@selector(handleClearIndicator:)
               withObject:nil afterDelay:BGIndicatorClearInterval];
}

- (void)startIndicatorWithMessage:(NSString *)message {
    [NSObject cancelPreviousPerformRequestsWithTarget:self
                                             selector:@selector(handleClearIndicator:)
                                               object:nil];
    
    [statusMessage setStringValue:message];
    [self performSelector:@selector(handleUpdateIndicator:)
               withObject:nil afterDelay:BGIndicatorUpdateInterval];
}

- (void)handleUpdateIndicator:(id)object {
    NSString *message = [NSString stringWithFormat:@&amp;quot;%@.&amp;quot;,
                                  [statusMessage stringValue]];
    [statusMessage setStringValue:message];

    [self performSelector:@selector(handleUpdateIndicator:)
               withObject:nil afterDelay:BGIndicatorUpdateInterval];
}

- (void)handleClearIndicator:(id)object {
    [statusMessage setStringValue:@&amp;quot;&amp;quot;];
}
&lt;/pre&gt;

&lt;p&gt;取得開始の通知を受ける handleFeedLoadStart: で startIndicatorWithMessage: の遅延実行をセットする。startIndicatorWithMessage では開始メッセージを表示した後、handleUpdateIndicator: の遅延実行をセットする。handleUpdateIndicator: ではメッセージを更新(「.」を追加)した後、handleUpdateIndicator: (つまり自分自身)の遅延実行をセットする。こうして handleUpdateIndicator: の実行が繰り返され、取得完了通知を受けた handleFeedLoadComplete: でキャンセルされるまで続く。また、handleFeedLoadComplete: では、完了メッセージを表示した後、進捗メッセージを消去するための遅延実行をセットしている。これにより、いつまでも完了メッセージがステータス行に残ることを防いでいる。&lt;/p&gt;

&lt;h4&gt;まとめ&lt;/h4&gt;

&lt;p&gt;メンドウだと言ってしまえばその通りなんだけど、GUI っていうのはそういうものだ。それに利用している仕組みも難しいものではないし。&lt;/p&gt;

&lt;p&gt;一方で、テキストメッセージを表示するというフィードバックは、GUI アプリのユーザ体験としてはほめられたものじゃない。グルグル回ったり、塗り潰されたりする方が見やすいし、わかりやすい。フィードバックを表示するための裏の仕掛けには、今回のように通知と遅延実行を使えば良いが、何を表示するかには工夫の余地がある。&lt;/p&gt;

&lt;p&gt;Cocoa Touch 版(つまり iPhone アプリ)では、この方式は使えない。グラフィカルな仕掛けが必要だ。グルグル回るやつとか。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361786/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41tHHnWaRqL._SL160_.jpg" alt="詳解 Objective-C 2.0 改訂版" title="詳解 Objective-C 2.0 改訂版" width="126" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361786/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0 改訂版&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2010-12-17 )&lt;br /&gt;ISBN: 9784797361780&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797361786" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;タイマを使ったメソッドの実行については「15-01 アプリケーションと実行ループ」に記述がある。とくに今回用いた遅延実行については p.351 の「メッセージの遅延実行」を参考にした。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html%23//apple_ref/doc/uid/10000057i-CH16-SW1"&gt;Threading Programming Guide: Run Loops&lt;/a&gt; (Mac OS X Reference Library; 「Cocoa Perform Selector Sources」というセクションに performSelector:withObject:afterDelay の説明がある)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html%23//apple_ref/occ/instm/NSObject/performSelector:withObject:afterDelay:"&gt;NSObject Class Reference: performSelector:withObject:afterDelay&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;設定変更を通知する - 環境設定パネルを作る #4 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4523485392174874757?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4523485392174874757/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass_25.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4523485392174874757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4523485392174874757'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass_25.html' title='進捗状況を表示する (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1232598088150245689</id><published>2010-12-24T23:53:00.002+09:00</published><updated>2010-12-24T23:53:03.571+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>PasswordManager の導入 - 非公開ブログの記事を取得する (MacBloggerGlass)</title><content type='html'>&lt;p&gt;今日の作業は「いくつか気になっている細部のつめ」の一つ。非公開ブログの記事を取得できるようにすること。GAE 版では Google サービスに対する認証処理をサボったため公開ブログだけしか扱えなかった。しかし、Cocoa 版ではブログの一覧を取得するために認証付きのアクセスを利用している。記事の取得でも同様にすれば良いだけのこと。&lt;/p&gt;

&lt;p&gt;GData ライブラリを使った記事の取得を実行している部分は、FeedManager (の一部のメソッド)に局所化されている。そこだけを変更すればできると考えた。が、この目論見は甘かった。必要な情報を伝播させるため、あちこちに手を入れる必要があった。結局、大小さまざまの変更をほぼ全体に施すことになってしまったのだ。以下で説明する PasswordManager もその一つ。&lt;/p&gt;

&lt;p&gt;これまではアカウント(Google アカウント)のパスワードは PreferenceController だけで保管、利用してきた。取得にパスワードが必要な情報(ブログ一覧)を使うのがここだけだったから。一方、ブログ記事を取得するトリガーは AppController にある。非公開ブログの記事を取得するためには AppController から FeedManager にパスワードを知らせなければならない。&lt;/p&gt;

&lt;p&gt;環境設定パネル(PreferenceController で制御している)で入力したパスワードを AppController でも利用できるようにするための仕組みが PasswordManger だ。パスワードの保管と読み出しを局所化することがその目的になる。インタフェース部を以下に示す。FeedManager 同様、これもシングルトンパターンを実装している(→「&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html"&gt;FeedManager の実装&lt;/a&gt;」)。メソッドの実装は、PreferenceController にあったキーチェーンを使った保存と読み出しをほぼそのまま流用している(「→&lt;a href="http://logrepo.blogspot.com/2010/12/blog-post.html"&gt;キーチェーンサービスを使ってパスワードを保存する&lt;/a&gt;」)&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface PasswordManager : NSObject {
    NSMutableDictionary *passwords;
}

+ (PasswordManager *)sharedManager;

- (NSString *)currentPassword:(NSString *)account;
- (void)updatePasswordForAccount:(NSString *)account
                        password:(NSString *)password;

@end
&lt;/pre&gt;

&lt;p&gt;この他にも、GData API の認証付きサービスを利用するために必要な情報をFeedManager にわたすための仕掛けが必要になったり、と結構大掛かりな変更になった。FeedManager だけを 2、3 行変更するだけのつもりで始めた変更だっただけに、なおさら変更量が多く感じられたのかも。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/3-macbloggerglass.html"&gt;ラベルによる記事の抽出 - ラベル検索の実装 #3 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html"&gt;FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/blog-post.html"&gt;キーチェーンサービスを使ってパスワードを保存する&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1232598088150245689?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1232598088150245689/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/passwordmanager-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1232598088150245689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1232598088150245689'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/passwordmanager-macbloggerglass.html' title='PasswordManager の導入 - 非公開ブログの記事を取得する (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-369348087726692819</id><published>2010-12-23T23:37:00.003+09:00</published><updated>2010-12-24T11:10:33.778+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>ラベルによる記事の抽出 - ラベル検索の実装 #3 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;前回(→「&lt;a href="http://logrepo.blogspot.com/2010/12/nsdrawer-2-macbloggerglass.html"&gt;ドロワー (NSDrawer) の使い方&lt;/a&gt;」)に続いて、ラベルの付いた記事を抽出する仕組みを作り込む。&lt;/p&gt;

&lt;h4&gt;Feed にクエリを指定して Entry を絞り込む&lt;/h4&gt;

&lt;p&gt;GAE 版では、GData API への問い合わせにクエリーを指定することで、取得するフィードデータの段階で記事を絞り込んでいた。Cocoa 版ではフィードデータをローカルストレージに保存しているため、この方式は使えない。&lt;/p&gt;

&lt;p&gt;記事の抽出は MVC のうち、M (モデル) でも C (コントローラ) でも可能だが、今回は M (モデル) で行うことにした。この場合、記事の抽出というよりも絞り込みと言うべきだろう。&lt;/p&gt;

&lt;p&gt;具体的には、Feed クラスに対して絞り込みのための「何か」を付与することで、Feed のプロパティ entries (配列)に収められる要素(Entry のインスタンス)を制限する。Feed が抱える要素(Entry)をフィルタにかけるようなイメージだ。&lt;/p&gt;

&lt;p&gt;Feed に付与する「何か」をクエリー(Query)と呼ぶ。その実体は、Entry クラスの述語メソッドと引数となるオブジェクトを組み合わせたものだ。Feed が抱える Entry のインスタンスそれぞれに対して、述語メソッドを(組み合わされた引数とともに)適用し、結果が真 (BOOL の YES) のものだけを残すわけだ。&lt;/p&gt;

&lt;p&gt;今回は、ラベルによる絞り込みなので、Entry クラスには - (BOOL)hasLabel:(NSString *)label というメソッドを定義しておく。以下にその定義を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (BOOL)hasLabel:(NSString *)aLabel {
    NSString *label;
    for (label in self.labels) {
        if ([label isEqualToString:aLabel]) {
            return YES;
        }
    }
    return NO;
}
&lt;/pre&gt;

&lt;h4&gt;NSInvocation を使った Query の実現&lt;/h4&gt;

&lt;p&gt;Query の定義を以下に示す。これを見てわかるように、Query の実体は NSInvocation そのものだ。Entry のインスタンスに適用するために特殊化された NSInvocation と言っても良い。&lt;/p&gt;

&lt;p&gt;NSInvocation は言わば、オブジェクトに対するメソッド呼び出し自体を独立したオブジェクトとして扱えるようにするためのものだ。そこには、メソッドの情報(セレクタとシグナチャ)、引数、ターゲットとなるオブジェクトが収められる。もともと、分散処理を実現するための仕組みの一つのようだが、今回のような目的にも利用できる。&lt;/p&gt;

&lt;p&gt;単にラベル(文字列)による要素の絞り込みを実現するだけなら、NSInvocation のような仕組みは必要ない。ただラベル(の配列)を Feed クラスにわたすだけで良い。単純な方法をとらなかったのは、他の種類のクエリーもサポートするためだ。たとえば、投稿日時による絞り込み等。Entry で適切な述語を定義してやれば、そういった絞り込みも可能になる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface Query : NSObject {
    NSInvocation *invocation;
}

+ (Query *)queryWithPredicate:(SEL)predicate argument:(id)object;
- (id)initWithInvocation:(NSInvocation *)anInvocation;

- (BOOL)applyTo:(Entry *)target;

@end

@implementation Query

+ (Query *)queryWithPredicate:(SEL)predicate argument:(id)object {
    NSMethodSignature *signature =
    [Entry instanceMethodSignatureForSelector:predicate];

    NSInvocation *anInvocation =
    [NSInvocation invocationWithMethodSignature:signature];
    [anInvocation setSelector:predicate];
    [anInvocation setArgument:&amp;amp;object atIndex:2];

    Query *query = [[Query alloc] initWithInvocation:anInvocation];
    return [query autorelease];
}

- (id)initWithInvocation:(NSInvocation *)anInvocation {
    [super init];
    invocation = [anInvocation retain];
    return self;
}

- (void)dealloc {
    [invocation release];
    [super dealloc];
}

- (BOOL)applyTo:(Entry *)target {
    [invocation invokeWithTarget:target];

    BOOL result;
    [invocation getReturnValue:&amp;amp;result];
    return result;
}
@end
&lt;/pre&gt;

&lt;p&gt;コントローラ(AppController)では、ドロワー内のテーブルビューで選択が変わったときに、Feed に対して Query を付与している。ラベルが複数選択された場合は Query も複数付けられることになる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
    id object = [notification object];
    if (object == postTable) {
        &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    } else if (object == labelsTable) {
        NSIndexSet *indexes = [labelsTable selectedRowIndexes];
        NSArray *selectedLabels = [feed.labels objectsAtIndexes:indexes];
        NSString *label;
        NSMutableArray *queries =
        [NSMutableArray arrayWithCapacity:[indexes count]];
        for (label in selectedLabels) {
            Query *query =
            [Query queryWithPredicate:@selector(hasLabel:) argument:label];
            [queries addObject:query];
        }
        [feed setValue:queries forKey:@&amp;quot;queries&amp;quot;];
    }
}
&lt;/pre&gt;

&lt;p&gt;Feed での絞り込みは entries プロパティに対するアクセサを上書きすることで実現している。これを見てわかるように、複数の Query が付けられた場合、AND 結合による絞り込みとなる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSArray *)entries {
    if ([queries count] == 0) return entries;
    NSMutableArray *filteredEntries = [NSMutableArray array];
    Entry *entry;
    Query *query;
    for (entry in entries) {
        BOOL result = YES;
        for (query in queries) {
            result = [query applyTo:entry];
            if (! result) break;
        }
        if (result) {
            [filteredEntries addObject:entry];
        }
    }
    return filteredEntries;
}
&lt;/pre&gt;

&lt;p&gt;ちなみに今回は、この絞り込みは Feed クラスではなく、Feed から派生させた FilteredFeed クラスに実装している。そして記事フィードを表現する FeedBlogPost クラスをこの FilteredFeed クラスから派生するように変更した。というのも、ブログ一覧(のフィード)を表現する FeedBlog クラスには絞り込みは必要ない。絞り込みの実装がそちらに影響を及ぼすことのないようにしておきたかったのだ。&lt;/p&gt;

&lt;h4&gt;Cocoa 版 BloggerGlass の完成&lt;/h4&gt;

&lt;p&gt;ラベル検索が実現できたことで、Mac 用アプリとして作り始めたときの目標(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能&lt;/a&gt;」)を達成できた。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;MacBloggerGlass プロトタイプ #0&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;ラベル検索&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/BP01XHrG59kD0kJqWxf_ew?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TRNNjNtqlMI/AAAAAAAABtM/8ZIRPkjp040/s400/mbg_label_search_proto_0.png" height="239" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;スクリーンショットだけではわかりにくいが、左のドロワーで選択したラベルの付いた記事だけが一覧に現れている。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;これで Cocoa Touch 版、すなわち iOS デバイス向けのアプリとして作り直すための基礎ができた。Cocoa 版との大きな違いは、GUI の部品が異なること、Cocoa Bindings が使えないことなどだろうか。UI としては、Cocoa 版よりもむしろ iPhone 向けに最適化した GAE 版に近いものになるかもしれない。Cocoa Bindings を使わないとすると、コントローラの実装も変わる(コードが増える)はず。&lt;/p&gt;

&lt;p&gt;一方で、モデルと言うべき GData オブジェクトのアダプタ(Entry と Feed)クラスおよび FeedManager はほぼそのまま流用できるだろう。&lt;/p&gt;

&lt;p&gt;Cocoa Touch 版にとりかかるのは 2011 年になってからになるか。2010 年の残りはいくつか気になっている細部をつめる作業をしよう。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DistrObjects/Tasks/invocations.html"&gt;Distributed Objects Programming Topics: Using NSInvocation&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html%23//apple_ref/doc/uid/20000212"&gt;NSInvocation Class Reference&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/nsdrawer-2-macbloggerglass.html"&gt;ドロワー (NSDrawer) の使い方 - ラベル検索の実装 #2 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass_21.html"&gt;ラベル検索の実装 #1 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-369348087726692819?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/369348087726692819/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/3-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/369348087726692819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/369348087726692819'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/3-macbloggerglass.html' title='ラベルによる記事の抽出 - ラベル検索の実装 #3 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TRNNjNtqlMI/AAAAAAAABtM/8ZIRPkjp040/s72-c/mbg_label_search_proto_0.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3944241654808899690</id><published>2010-12-22T23:54:00.005+09:00</published><updated>2010-12-23T00:01:21.387+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>ドロワー (NSDrawer) の使い方 - ラベル検索の実装 #2 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;ラベルの一覧を表示する UI として、ドロワーを使うことにした。ドロワーとは、アプリウィンドウの端に(大抵は左右のどちらか)付加的に表示できるウィンドウのことだ。通常のアプリウィンドウと同じく、様々なビューを置くことができる。&lt;/p&gt;

&lt;p&gt;最近の Mac OS X 用アプリではあまり見かけなくなった(少々古臭い) UI だが、表示中はメインウィンドウにくっついており見失うこともないし、不要なときは隠しておけばメインウィンドウ上のレイアウトを圧迫することもない。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass のメインウィンドウは記事一覧表示とその内容表示で、すでに 2 分割(2 ペイン)しているが、ここにラベル一覧表示領域を追加するとさらに分割することになってしまう。その方が使いやすいならそうするが、まずは表示領域の分割ではなく、必要な時だけ領域を追加する方式で試してみることにした。&lt;/p&gt;

&lt;h4&gt;Interface Builder での操作&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/pe5WGOWPJPSWYm4ME8-QVg?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TRIC5aya8bI/AAAAAAAABss/PpqP88W-PVo/s800/interfacebuilder_library_drawer.png" height="648" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Cocoa アプリにドロワーを追加するには、ライブラリツールから Windows グループにある Windows and Drawer (右のスクリーンショットを参照)をドラッグしドキュメントウィンドウにドロップする。&lt;/p&gt;

&lt;p&gt;ライブラリツール上は 1 つのオブジェクトのように置かれているが、ドキュメントウィンドウにドロップすると以下の 3 つのオブジェクトが現れる(下のスクリーンショット参照。)。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Window&lt;/li&gt;
&lt;li&gt;Drawer Content View&lt;/li&gt;
&lt;li&gt;Drawer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;この 3 つのオブジェクトは、ドロップされた状態ですでに互いに関連づけられている。具体的には Drawer に備わったアウトレットのうち、parentWindow が Window に、contentView が Drawer Content View にそれぞれ結びつけられている。&lt;/p&gt;

&lt;p&gt;大抵の場合、アプリウィンドウはプロジェクトを作ったときから (MainMenu.xib 内に) 存在しているため、上記の Window オブジェクトは不要だ。当然、削除することになるが、そのときは Drawer オブジェクトの parentWindow を適切に設定しなければならない。&lt;/p&gt;

&lt;div class="clear"&gt;&lt;/div&gt;

&lt;div class="left"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/CzeQymz0W5cyjvEh8jTWxA?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TRIC5orWJ_I/AAAAAAAABsw/I7AHUughyu8/s800/interfacebuilder_drawer_objects.png" height="92" width="285" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Drawer オブジェクト(の parentWindow)にアプリのウィンドウを結びつけるには、Ctrl キーを押しながら Drawer オブジェクトからアプリウィンドウまでドラッグする。アプリウィンドウ上に接続するアウトレットの候補が現れるので、parentWindow を選ぶ。&lt;/p&gt;

&lt;p&gt;この Interface Builder のドキュメントにも書いてある正式な手順(Ctrl + ドラッグ)は正直言ってわかりづらい。いつもどっちからドラッグするのかで迷ってしまう。それよりも、アウトレットを備えたオブジェクト上でポップアップメニューを出す方がずっとわかりやすい。マウスなら(特に設定を変えていない限り)右クリック、TrackPad なら 2 本の指でタップした状態でクリックでポップアップメニューが出てくる。メニューにあるアウトレットの右端の端子(○)を接続したいオブジェクトまでドラッグする。Ctrl キーを押す必要はない。&lt;/p&gt;

&lt;h4 class="clear"&gt;ドロワーの表示&lt;/h4&gt;

&lt;p&gt;ドロワーを表示するにはアプリコントローラからアウトレットを通して openOnEdge: メソッドを呼ぶ。このとき、親ウィンドウのどの端にドロワーを付けるかを引数として指定する。隠す際は close メソッドだ。&lt;/p&gt;

&lt;p&gt;以下は、AppController.m からの抜粋だ。labelsDrawer は AppController クラスのアウトレットで、Interface Builder 上で Drawer オブジェクトに接続してある。&lt;/p&gt;

&lt;p&gt;以下のコードでは、現在のドロワーの状態を取得し、それに応じて表示中なら close、非表示中なら openOnEdge: を呼んでいる。openOnEdge: にわたしている NSMinXEdge はウィンドウの左端を意味する定数だ。ドロワーを右端に表示したいなら NSMaxXEdge を指定する。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (IBAction)toggleLabelsDrawer:(id)sender {
    NSDrawerState state = [labelsDrawer state];
    if (state == NSDrawerOpeningState || state == NSDrawerOpenState) {
        [labelsDrawer close];
        [showDrawerMenu setTitle:@&amp;quot;Show Labels&amp;quot;];
    } else {
        [labelsDrawer openOnEdge:NSMinXEdge];
        [showDrawerMenu setTitle:@&amp;quot;Hide Labels&amp;quot;];
    }
}
&lt;/pre&gt;

&lt;p&gt;上記で、showDrawerMenu に対して setTitle: しているのは、ドロワーの状態によってドロワーを表示(または非表示)にするためのメニュー項目の文字列を変えるためだ。非表示状態では「Show Labels」に、表示中なら「Hide Labels」に置き換えている。showDrawerMenu も AppController のアウトレットで NSMenuItem * として宣言し、Interface Builder 上でメニュー項目と接続してある。&lt;/p&gt;

&lt;h4&gt;ラベル一覧の表示&lt;/h4&gt;

&lt;p&gt;前回(→「&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass_21.html"&gt;ラベル検索の実装 #1&lt;/a&gt;」)のラベル列挙と合わせると、ラベル一覧表示が可能になる。以下がそのスクリーンショットだ。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;MacBloggerGlass プロトタイプ #0&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;ラベル一覧表示&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/93-Jjd3kq4wp3QgxRZ02QA?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TRIQDe9KZkI/AAAAAAAABtA/EYxZA0A_VkE/s400/mbg_labels_proto_0.png" height="400" width="370" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;ラベル一覧をドロワーとして実現した状態。一覧表示にふくまれているすべての記事に付いたラベルを集めてドロワー内のテーブルに表示している。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Drawers/Drawers.html"&gt;Drawers&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSDrawer_Class/Reference/Reference.html"&gt;NSDrawer Class Reference&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#samplecode/DrawerMadness/Introduction/Intro.html"&gt;DrawerMadness&lt;/a&gt; (同上; ドロワーの使い方を示すサンプルプログラム)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass_21.html"&gt;ラベル検索の実装 #1 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3944241654808899690?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3944241654808899690/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/nsdrawer-2-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3944241654808899690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3944241654808899690'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/nsdrawer-2-macbloggerglass.html' title='ドロワー (NSDrawer) の使い方 - ラベル検索の実装 #2 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TRIC5aya8bI/AAAAAAAABss/PpqP88W-PVo/s72-c/interfacebuilder_library_drawer.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-677745550581527888</id><published>2010-12-21T23:39:00.002+09:00</published><updated>2010-12-21T23:39:56.017+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='gdata'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>ラベル検索の実装 #1 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;ラベル検索を実現するために必要な仕組みは以下の 2 つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ラベルの列挙&lt;/li&gt;
&lt;li&gt;指定されたラベルの付いた記事の抽出&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;今回は、まず簡単な方、ラベルの列挙を実装してみる。&lt;/p&gt;

&lt;h4&gt;GDataCategory&lt;/h4&gt;

&lt;p&gt;GData ライブラリで Blogger の記事は GDataEntryBlogPost クラスが表現している。各記事に付けられたラベルはこのクラスの categories というプロパティに収められている(実際に categories が定義されているのは GDataEntryBlogPost の基底クラスである GDataEntryBase)。このプロパティは NSArray として取り出すことができ、その各要素は GDataCategory クラスのインスタンスになっている。&lt;/p&gt;

&lt;p&gt;もともと、Blogger の記事データを表す Atom フィードでは、カテゴリは以下のような XML 要素として表現されている。この term 属性が Blogger の記事に付けたラベルになっている。&lt;/p&gt;

&lt;pre class="prettyprint lang-xml"&gt;
&amp;lt;category
    scheme=&amp;quot;http://www.blogger.com/atom/ns#&amp;quot;
    term=&amp;quot;1. Macをプログラムする&amp;quot;&amp;gt;
&amp;lt;/category&amp;gt;
&lt;/pre&gt;

&lt;p&gt;GDataCategory クラスには上記の term 属性を参照するための term プロパティが定義されている。ややこしいことに label というプロパティも定義されているが、こちらは Blogger のラベルとは関係ない。&lt;/p&gt;

&lt;h4&gt;実装&lt;/h4&gt;

&lt;h5&gt;Entry クラス&lt;/h5&gt;

&lt;p&gt;Entry クラスでは、内部に抱えた GDataEntryBlogPost の categories プロパティから各 GDataCategory のインスタンスの term プロパティを取り出し、NSArray の一時オブジェクトとして返すメソッド(プロパティへのアクセサ)を定義した。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSArray *)labels {
    NSArray *categories = [gDataEntry categories];
    if (! categories) return nil;
    NSMutableArray *lbls = [NSMutableArray arrayWithCapacity:[categories count]];
    GDataCategory *category;
    for (category in categories) {
        [lbls addObject:[category term]];
    }
    return lbls;
}
&lt;/pre&gt;

&lt;h5&gt;Feed クラス&lt;/h5&gt;

&lt;p&gt;Feed クラスでは、初期化時に各 Entry からラベルを(上述の labels メソッドで)取り出し、NSMutableSet にまとめておき、labels プロパティとして保持するコードを追加した。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)gatherLabels {
    labels = [[NSMutableSet alloc] init];
    Entry *entry;
    NSString *label;
    for (entry in entries) {
        for (label in entry.labels) {
            [labels addObject:label];
        }
    }
}
&lt;/pre&gt;

&lt;p&gt;配列ではなく集合を使ったのは、集合は addObject 時に要素の重複を排除してくれるから。&lt;/p&gt;

&lt;h5&gt;FeedManager クラス&lt;/h5&gt;

&lt;p&gt;また、内部に抱えた Feed インスタンスの labels プロパティをそのまま返す、同名のプロパティを FeedManager クラスにも用意することにした。アプリコントローラからは FeedManager 経由でアクセスすることにしたいから。&lt;/p&gt;

&lt;h4&gt;次は？&lt;/h4&gt;

&lt;p&gt;ラベルの「見せ方」を考えているところだが、まずは単純にドロワー(NSDrawer)に NSTableView でラベルを列挙する方式にするつもり。&lt;/p&gt;

&lt;p&gt;ドロワーは最近ではあまり見かけなくなったけど、標準で用意されている部品だし、使わないときは隠しておけるし。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-677745550581527888?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/677745550581527888/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/1-macbloggerglass_21.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/677745550581527888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/677745550581527888'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/1-macbloggerglass_21.html' title='ラベル検索の実装 #1 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2931109781134533315</id><published>2010-12-20T22:28:00.003+09:00</published><updated>2010-12-20T22:28:44.938+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><title type='text'>Xcode プロジェクトを git で管理する</title><content type='html'>&lt;p&gt;MacBloggerGlass のプロジェクトの git 管理を開始することにした。Xcode はツール自身が生成するファイルがいろいろあって、何を git 管理に任せれば良いのかが難しい。ツールのことを知るためには自分で試行錯誤を繰り返すのが一番だが、ここは素直に先人の知恵に頼ることにした。&lt;/p&gt;

&lt;h4&gt;Xcode-Git-User-Script&lt;/h4&gt;

&lt;p&gt;今回利用したのは、github に登録されている &lt;a href="https://github.com/tbarbe/Xcode-Git-User-Script"&gt;Xcode-Git-User-Script&lt;/a&gt; というプロジェクトのスクリプトだ。この先、何度も繰り返すことになることだから、ツールの機能として取り込む方が良いと判断した。&lt;/p&gt;

&lt;p&gt;このプロジェクトが提供するスプリプトは Xcode のスクリプトメニュー(「ウィンドウ」メニューと「ヘルプ」メニューの間にあるスクロールっぽいアイコン)にユーザスクリプトとして追加するもので、メニューから実行することで Xcode のプロジェクトに対して .gitignore と .gitattributes を追加してくれる。&lt;/p&gt;

&lt;h5&gt;ユーザスクリプトとして追加&lt;/h5&gt;

&lt;p&gt;まず、github の &lt;a href="https://github.com/tbarbe/Xcode-Git-User-Script"&gt;Xcode-Git-User-Script&lt;/a&gt; プロジェクトを開き、pasteMe_into_xcode_script_menu というファイルの内容を全選択しコピーしておく。その後の手順は以下のとおり。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/BUSabuhS_Y3t3Wlz6jZtGA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TQ9VqyfNHoI/AAAAAAAABsE/4-bmgjpp0Is/s288/xcode_user_script_create_gitignore.png" height="179" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Xcode のスクリプトメニュー(スクロールっぽいアイコン)から「ユーザスクリプトを編集…」を実行する。&lt;/li&gt;
&lt;li&gt;「ユーザスクリプトを編集」ダイアログが現れるので、左下にある「＋」ボタンを押す。&lt;/li&gt;
&lt;li&gt;(「＋」ボタンを押すことで現れる)追加メニューの中から「新規シェルスクリプト」を選択する。&lt;/li&gt;
&lt;li&gt;右側のペインにスクリプトをペーストする。&lt;/li&gt;
&lt;li&gt;必要なら左側のリスト項目から追加したスクリプトの名前を変更しておく(ダブルクリックで編集状態になる)。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;右のスクリーンショットは「Create .gitignore」という名前で追加したところ。&lt;/p&gt;

&lt;h5 class="clear"&gt;スクリプトを実行してみる&lt;/h5&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/ltG0G38H0WZrM-SBDn0uqg?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TQ9XdkSAezI/AAAAAAAABsc/e7u143SU738/s288/xcode_warning_dot_files.png" height="109" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;スクリプトメニューから追加したスクリプトの名前を選ぶ。するとフォルダを選択するダイアログが出てくる。.gitignore を追加したいプロジェクトのフォルダを選べば良い。&lt;a href="https://github.com/tbarbe/Xcode-Git-User-Script/blob/master/README"&gt;README&lt;/a&gt; にも書かれているが、このダイアログで「保存」ボタンを押すと、右のスクリーンショットにあるような警告パネルが現れる。気にせず「&amp;quot;.&amp;quot;を使用」を押せば良い。&lt;/p&gt;

&lt;p&gt;また、このスクリプトでは .gitignore に加えて、.gitattributes というファイルも追加するため、スクリプトの実行中にフォルダを選ぶダイアログが 2 回現れる。当然、警告パネルも 2 回現れることになる。&lt;/p&gt;

&lt;p&gt;あとは、対象のプロジェクトのフォルダ(ディレクトリ)で &lt;code&gt;git init&lt;/code&gt; すればリポジトリのセットアップが完了だ。&lt;/p&gt;

&lt;h4&gt;Xcode の次のバージョンに期待&lt;/h4&gt;

&lt;p&gt;Xcode の次のバージョンでは git のサポートも入るらしい。そうなれば .gitignore のことなんかもツール自身が面倒を見てくれるはず。ま、いつ出てくるのかはわからないけど。β版を試す勇気はないしな。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tbarbe/Xcode-Git-User-Script"&gt;tbarbe/Xcode-Git-User-Script&lt;/a&gt; (github)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/gitattributes.html"&gt;gitattributes(5) Manual Page&lt;/a&gt; (kernel.org)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/technologies/tools/whats-new.html#version-editor"&gt;What's New in Xcode: Version Editor&lt;/a&gt; (Apple)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html"&gt;FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2931109781134533315?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2931109781134533315/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/xcode-git-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2931109781134533315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2931109781134533315'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/xcode-git-macbloggerglass.html' title='Xcode プロジェクトを git で管理する'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_wobSevGXOh4/TQ9VqyfNHoI/AAAAAAAABsE/4-bmgjpp0Is/s72-c/xcode_user_script_create_gitignore.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6430560997325541499</id><published>2010-12-19T22:47:00.000+09:00</published><updated>2010-12-19T22:47:26.190+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>「詳解 Objective-C 2.0」の改訂版が出た</title><content type='html'>&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361786/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41tHHnWaRqL._SL160_.jpg" alt="詳解 Objective-C 2.0 改訂版" title="詳解 Objective-C 2.0 改訂版" width="126" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361786/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0 改訂版&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2010-12-17 )&lt;br /&gt;ISBN: 9784797361780&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797361786" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;荻原(2.0)本の改訂版が出た。「はじめに」に書かれている主な更新点は以下の 5 つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ブロックオブジェクトの章を新設 (→ CHAPTER 14)&lt;/li&gt;
&lt;li&gt;並列処理に関する記述を一新 (→ CHAPTER 19)&lt;/li&gt;
&lt;li&gt;iOS に関する記述を充実 (→ 07-03 等)&lt;/li&gt;
&lt;li&gt;Core Foundation の概要を追加 (→ APPENDIX B)&lt;/li&gt;
&lt;li&gt;NSURL に関する記述を充実 (→ 08-07)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;大きな変化は Snow Leopard で導入されたブロックオブジェクトと GCD に関する解説が追加されたことだろう。これら新しい並列処理機能に興味があるかどうかが、旧版を持っているプログラマが新版を買うかどうかの目安になるか。&lt;/p&gt;

&lt;h4&gt;目次&lt;/h4&gt;

&lt;p&gt;今後の参照のため、目次を引き写しておく。章立てレベルで目に付く&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;旧版&lt;/a&gt;との差異は「CHAPTER 14 ブロックオブジェクト」が追加されたことと、旧版での「CHAPTER 18 スレッド」と「CHAPTER 19 分散オブジェクトが新版では「CHAPTER 19 並列プログラミング」に統合されたこと、そして「APPENDIX B CoreFoundation フレームワークの概要」が追加されたこと。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CHAPTER 01 オブジェクトに基づくソフトウェアの作成&lt;/li&gt;
  &lt;li&gt;CHAPTER 02 Objective-C のプログラム&lt;/li&gt;
  &lt;li&gt;CHAPTER 03 継承とクラス&lt;/li&gt;
  &lt;li&gt;CHAPTER 04 オブジェクトの型と動的結合&lt;/li&gt;
  &lt;li&gt;CHAPTER 05 リファレンスカウンタを用いたメモリ管理方式&lt;/li&gt;
  &lt;li&gt;CHAPTER 06 ガーベジコレクション&lt;/li&gt;
  &lt;li&gt;CHAPTER 07 NSObjectクラスとランタイムシステム&lt;/li&gt;
  &lt;li&gt;CHAPTER 08 Foundation フレームワークの重要なクラス&lt;/li&gt;
  &lt;li&gt;CHAPTER 09 カテゴリ&lt;/li&gt;
  &lt;li&gt;CHAPTER 10 抽象クラスとクラスクラスタ&lt;/li&gt;
  &lt;li&gt;CHAPTER 11 プロトコル&lt;/li&gt;
  &lt;li&gt;CHAPTER 12 宣言プロパティとアクセサ&lt;/li&gt;
  &lt;li&gt;CHAPTER 13 オブジェクトのコピーと保存&lt;/li&gt;
  &lt;li&gt;CHAPTER 14 ブロックオブジェクト&lt;/li&gt;
  &lt;li&gt;CHAPTER 15 メッセージ送信のパターン&lt;/li&gt;
  &lt;li&gt;CHAPTER 16 アプリケーションの構造&lt;/li&gt;
  &lt;li&gt;CHAPTER 17 例題: 簡易画像ビューア&lt;/li&gt;
  &lt;li&gt;CHAPTER 18 例外とエラー&lt;/li&gt;
  &lt;li&gt;CHAPTER 19 並列プログラミング&lt;/li&gt;
  &lt;li&gt;CHAPTER 20 キー値コーディング&lt;/li&gt;
  &lt;li&gt;APPENDIX A Foundationフレームワークの概要&lt;/li&gt;
  &lt;li&gt;APPENDIX B CoreFoundation フレームワークの概要&lt;/li&gt;
  &lt;li&gt;APPENDIX C コーディングの指針&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;ブロックオブジェクト&lt;/h4&gt;

&lt;p&gt;新設された「CHAPTER 14 ブロックオブジェクト」をざっと読んでみた。まずは章の冒頭部分から引用。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797361786"&gt;詳解 Objective-C 2.0 改訂版&lt;/a&gt;」p.322 より)&lt;br&gt;
&lt;b&gt;ブロックオブジェクト&lt;/b&gt; (block object) は、Mac OS X 10.6 (Snow Leopard) および iOS 4.0 で利用できるようになった機能で、Objective-C ではなく、C 言語の機能として実装されています。 &lt;span class="snip"&gt;[...snip...]&lt;/span&gt; 他のプログラミング言語では&lt;b&gt;クロージャ&lt;/b&gt; (closure) として知られている言語機能に相当します。
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;ブロックって&lt;a href="http://ja.wikipedia.org/wiki/クロージャ"&gt;クロージャ&lt;/a&gt;だったのか。それは知らなかったよ。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797361786"&gt;詳解 Objective-C 2.0 改訂版&lt;/a&gt;」p.327 より)&lt;br&gt;
どうやら、ブロックオブジェクトは、&lt;b&gt;そのブロックリテラルが記述された位置での自動変数の値を保存している&lt;/b&gt;ようです。
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;なるほど、これなら確かにクロージャだ。これがなければただの関数ポインタになってしまうよな。&lt;/p&gt;

&lt;p&gt;この章の前半は、ブロックオブジェクトの使い方と実現のための仕組みの簡単な紹介になっている。仕組みについては、ブロックオブジェクトがスタック上に確保されることの説明があるが、これはブロックを理解するめの基本と言えそうだ。「&lt;a href="http://d.hatena.ne.jp/brazil/20060131/1138692196"&gt;クロージャはオブジェクトの裏返し&lt;/a&gt;」と言われる意味が、この説明を読んで理解できた。&lt;/p&gt;

&lt;p&gt;また、後半では、ブロックオブジェクト(という名のクロージャ)の Cocoa アプリでの使用例として、コレクションクラス(配列や辞書)に追加されたブロック対応のメソッドを使ったソートと検索の方法と、シート(パネルの一種でウィンドウにくっついているもの)のデリゲートの置き換えとして使う方法が紹介されている。前者は、ブロックを &lt;code&gt;for (item in collection)&lt;/code&gt; の裏返しとして使う方法ってところか。ループの中に記述するコードをクロージャとしてコレクションクラスに適用するもの。&lt;/p&gt;

&lt;p&gt;そう言えば、GData ライブラリの実装でも(利用可能なら)ブロックを使うようになっていたな。CHAPTER 19 の並列プログラミングとあわせてじっくり読んでみるか。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;こちらは旧版。これまでこのブログでは「荻原(2.0)本」として何度も参照してきた。旧版についても目次を写してある(→「&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt;」)&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.sbcr.jp/products/4797361780.html"&gt;詳解 Objective-C 2.0 改訂版&lt;/a&gt; (SoftBank Creative; 出版社の商品紹介ページ)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ja.wikipedia.org/wiki/クロージャ"&gt;クロージャ&lt;/a&gt; (Wikipedia:ja)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/brazil/20060131/1138692196"&gt;関数、オブジェクト、クロージャ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/twitter-2010-12-09.html"&gt;twitter より (2010-12-09)&lt;/a&gt; (荻原本のアップデートを知ったときのつぶやき)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt; (Macをプログラムする)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6430560997325541499?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6430560997325541499/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/objective-c-20.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6430560997325541499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6430560997325541499'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/objective-c-20.html' title='「詳解 Objective-C 2.0」の改訂版が出た'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3769495955589972164</id><published>2010-12-19T07:34:00.002+09:00</published><updated>2010-12-19T20:36:53.687+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='appletv'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><title type='text'>twitter より (2010-12-18)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/16024316399329282"&gt;15:57&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iphone の ipod からairplay で apple tv に音を送るとまるで iphone がリモコンのよう。実際は本体なんだけどな。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/16026871762264065"&gt;16:07&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;apple tv って iphone (または ipod touch) の再生装置としても良いかも(・∀・)ｂ ipod のささるスピーカーでもいいけど、あれだと iphone 自体で離れた場所から操作できない。大抵リモコンがあるけど操作性はやはり iphone そのものが上。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/16027425871765504"&gt;16:10&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;ってか、アレだな。airplay 対応の再生装置が出てくればそれでも良いのか。&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/16085478210019328"&gt;20:00&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;iOS/Mac 向けアプリ開発者で iPad を持っているなら、おすすめです。→ &lt;a href="http://logrepo.blogspot.com/2010/12/ibook-apple.html"&gt;http://logrepo.blogspot.com/2010/12/ibook-apple.html&lt;/a&gt; (iBook で Apple 提供の開発者向けドキュメントが読める)&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3769495955589972164?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3769495955589972164/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-18.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3769495955589972164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3769495955589972164'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-18.html' title='twitter より (2010-12-18)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7088220522856671022</id><published>2010-12-18T19:52:00.005+09:00</published><updated>2010-12-19T22:58:25.300+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><title type='text'>iBook で Apple 提供の開発者向けドキュメントが読める</title><content type='html'>&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/TjRV_3tPoc3TXdn5tHuczA?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TQxxQPsK9iI/AAAAAAAABr8/KhUzQkHD3Gc/s400/IMG_0001.jpg" height="237" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;昨夜(2010-12-17)のこと、iPod touch で App Store を開くと iBook のアップデートが配布されていることに気付いた。他にもいろいろアップデートが出ていたのでまとめてアップデートした。で、ふと iBook を開き、さらに iBookStore を開いてみたところ、iOS 関連の開発者向けドキュメントが配信されていることに気付いた(右のスクリーンショットは iPad で iBookStore を開いたもの)。&lt;/p&gt;

&lt;p&gt;そう、ようやく Apple がやってくれた。ずっと、これが欲しかった。もともと PDF 版が配布されているから iPad に取り込んで読むことはできたし、さらに iPad がネットにつながるなら Safari でウェブ版を開けば iPad に最適化された状態で読むこともできた(→「&lt;a href="http://logrepo.blogspot.com/2010/07/ipad-apple.html"&gt;iPad で Apple 提供の開発者向けドキュメントを読む&lt;/a&gt;」)。実際、このウェブ版は、iPad というデバイスの特性とあいまって、「ADC のドキュメントを読むならコレだ！」と思わせるぐらい読みやすいものになっていた。iMac で Xcode を開いているときにも、そばに立てた iPad でガイド系の文書(のウェブ版)を開いて参照していた(Xcode のドキュメントブラウザは主にクラスリファレンスを参照するのに使っている)。&lt;/p&gt;

&lt;p&gt;とはいえ、ウェブ版のドキュメント(というよりブラウザで長い文書を読むこと)には不満がある。スクロールして読む読書体験と、ページをめくって読む読書体験を比較すれば、後者の方が圧倒的に心地良い。画面上を文字が移動するのは「読む」ことを前提にした場合は気持の良いものじゃない(せめて、iPad 上の Safari に「Page Up」と「Page Down」があれば……)。もっとも、これはわたしがデジタル以前の世界で育った世代からかもしれない。情報の入れ物としての「本」というカタチに慣れてしまっていることがそう思わせるのだろう。&lt;/p&gt;

&lt;p&gt;また、いくら Wi-Fi 接続だろうとネット上のリソースを開くのには「遅れ」が生じる。アドレスバーが水色で塗られていくのを眺めていても楽しくない。&lt;/p&gt;

&lt;p&gt;だから、iPad で ADC ドキュメントを読むようになってからずっと思っていた。いや、むしろ願っていた。Apple が ebook として提供してくれるようになることを。&lt;/p&gt;

&lt;h4&gt;iPad で読んでみる&lt;/h4&gt;

&lt;p&gt;いくつかスクリーンショットを撮ったので貼ってみる。すべて、iPad のスクリーンショット機能(電源ボタン + ホームボタンの同時押し)で撮影したもの。ここに並ぶんでいるのはサムネイル。実際の見え方は画像をクリックして Picasa ウェブアルバムにアップしたものを見てほしい。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;iPad の iBook で ADC ドキュメントを読む&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iBook の本棚に並んだ iOS 開発者向けドキュメントたち&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/gXACB314wlv8n5qChgyTFA?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQxxQ5dJzZI/AAAAAAAABrk/4yagtYBZ-J4/s400/IMG_0002.jpg" height="228" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;早速、ダウンロードしてみた。最上段の 5 つが iOS 開発者向けドキュメントだ。O'Reilly の ebook と比べると表紙が文字だけで少しさびしい。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;iOS Application Programming Guide を開いてみた (#1)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/xdUFyOf0GqhInqPXlmgvtA?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQxxSYejuyI/AAAAAAAABqQ/orgYbxbwDh0/s400/IMG_0004.PNG" height="400" width="300" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;文字はもちろん、写真もクッキリ。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;iOS Application Programming Guide を開いてみた (#2)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/mMmmyBBO8QQpz87VT4m1EA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TQxxR0OMUMI/AAAAAAAABqM/DIXd8qB6Bd8/s400/IMG_0003.PNG" height="400" width="300" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;図もはっきり見える。図中の文字も十分に判別できる。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;もう、なんて言うかさ、iPad はこのために作られたんじゃないか、って思えるぐらい。Apple は開発者向けに ADC ドキュメントを(iOS 用のものだけじゃなく)丸ごと収録した iPad を販売するべきだよ。有料の Dev program (iOS 向けと Mac 向けのものがある)に参加している場合はちょっと安くしてさ。&lt;/p&gt;

&lt;h4&gt;iPod touch で読んでみる&lt;/h4&gt;

&lt;p&gt;画素数ではともかく、画面が小さいから iPad ほどの見やすさは得られないとわかっているけど、せっかくなので iPod touch でも試してみた。以下は iPod touch のスクリーンショット機能で撮ったもの。残念ながらスクショでは iPod touch の画面での見た目は再現できないから参考程度に見てほしい。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;iPod touch の iBook で ADC ドキュメントを読む&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;iOS Application Programming Guide を開いてみた (#1)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/KF4yGH1mKCzhGZO2whZHTQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TQxxS_X7TbI/AAAAAAAABqU/mgQu9CjNgd8/s400/IMG_0001_2.PNG" height="400" width="267" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;スクショだとやけに文字が大きく見えるが、実機で見るとこれより小さいと読みづらくなるサイズ。ま、見えやすさには個人差が出ると思うが。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;iOS Application Programming Guide を開いてみた (#2)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/6XpGmFXXYuU1eQsC3sVaCQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TQxxTT95OtI/AAAAAAAABqY/0ptxkiKQJrU/s400/IMG_0002_2.PNG" height="400" width="267" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;iPad のスクショと同じ図。実機で見ると、図中の文字の判別はかなり厳しい。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;図だけを表示(横向き)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/eG8LLeKquJL1P6mf3PMGMg?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQxxTsLq3lI/AAAAAAAABrc/Y0KO4Ajy6L0/s400/IMG_0003_2.jpg" height="267" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;横向きで、図だけを表示させると少し大きくなる。ただ、やはり実機上では図中の文字は読みづらい。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;iBook では図をダブルタップすると図だけを表示することができる。この状態ではピンチオープン、クローズによる拡大縮小も可能。iPod touch でも、ダブルタップからピンチオープンで拡大すれば図中の文字も読み取れる。&lt;/p&gt;

&lt;h4&gt;他のドキュメントも欲しい&lt;/h4&gt;

&lt;p&gt;iBook 向けに提供されているのはまだたった 5 つだけ。ADC にはそれこそ山ほどのドキュメントがある。iOS 向けだけじゃなくて、Mac 向けの方のものも iPad で読みたい。&lt;/p&gt;

&lt;p&gt;Apple さん、どんどん配信してください。お願いします。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Introduction/Introduction.html"&gt;iOS Application Programming Guide&lt;/a&gt; (iOS Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/07/ipad-apple.html"&gt;iPad で Apple 提供の開発者向けドキュメントを読む&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7088220522856671022?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7088220522856671022/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/ibook-apple.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7088220522856671022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7088220522856671022'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/ibook-apple.html' title='iBook で Apple 提供の開発者向けドキュメントが読める'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_wobSevGXOh4/TQxxQPsK9iI/AAAAAAAABr8/KhUzQkHD3Gc/s72-c/IMG_0001.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7921564766081688911</id><published>2010-12-17T23:52:00.003+09:00</published><updated>2010-12-17T23:53:29.125+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;ようやく、FeedManager の実装とそれを使うコントローラ(AppController、PreferenceController)部分の修正が完了した。FeedManager は必要に応じてフィードを GData API を通じて入手し、ローカルに保存する。これで、取得ずみのフィードが存在すれば、ネットワークにアクセスすることなく記事を表示することができるようになった。つまりは、オフライン機能が実現できたことになる。&lt;/p&gt;

&lt;h4&gt;XML テキストの保存と復元&lt;/h4&gt;

&lt;h5&gt;ファイルへの書き出し&lt;/h5&gt;

&lt;p&gt;最初は、XML テキストの保存に NSString の writeToFile 系のメソッドを使っていた。問題なくファイルに書き込めていたのだけれど、気になったのはファイルの許可情報。保存先が ~/Library の下だということもあり、所有者以外の読み取り許可を付けたくなかった。ところが、NSString の writeToFile だとファイル作成時に許可情報を指定することができない。デフォルトだと 0644 で作られてしまう(グループと全ユーザに対して読み取り許可が付いている)。&lt;/p&gt;

&lt;p&gt;作った後に許可を変更することも考えたが、何かまずいことが起きてアプリがクラッシュした際に、許可を変更できないままのファイルが残る(という可能性がある)ことが気持ち悪い。結局、NSString から NSData に変換し、NSFileManager の createFileAtPath:contents:attributes: を使うことにした。該当する部分のコード(FeedManager のメソッド)を以下に示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)writeFileAtPath:(NSString *)path contents:(NSString *)text {
    // always overwrites a existing file
    NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
    if (! data) {
        NSLog(@&amp;quot;Some data might be lost in converting into the UTF-8 encoding.&amp;quot;);
        return;
    }

    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    [attrs setObject:[NSNumber numberWithInt:0600]
              forKey:NSFilePosixPermissions];

    BOOL result = [fileManager createFileAtPath:path contents:data attributes:attrs];
    if (! result) {
        NSLog(@&amp;quot;Fail to write a file: %@&amp;quot;, path);
    }    
}
&lt;/pre&gt;

&lt;p&gt;実はこの部分はちょっと気になっている。というのも、Feed オブジェクトから XML データをテキストとして抽出する際に、NSData から NSString に変換している。ファイルに書き出す際に、それを FeedManager で NSData に戻していることになる。どう考えてもムダだ。途中で NSString を利用しているわけでもないし。&lt;/p&gt;

&lt;p&gt;気が向いたら修正しておこう。&lt;/p&gt;

&lt;h5&gt;XML テキストからの復元&lt;/h5&gt;

&lt;p&gt;ファイルから XML テキストを読み込み、Feed オブジェクトを復元しているコードを以下に示す。まずは、FeedManager のメソッドから。ここでは指定されたファイルの内容を NSString として取り出し、Feed クラスのファクトリーメソッドにわたしている。&lt;p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (Feed *)restoreFeedFromFile:(NSString *)path feedClass:(Class)classOfGData {
    Feed *feed;
    NSString *xmltext =
    [NSString stringWithContentsOfFile:path
                              encoding:NSUTF8StringEncoding
                                 error:NULL];
    feed = [Feed feedWithXMLText:xmltext feedClass:classOfGData];
    return feed;
}
&lt;/pre&gt;

&lt;p&gt;次は、実際に Feed オブジェクトを生成している部分。こちらは Feed クラスのクラスメソッドだ。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
+ (Feed *)feedWithXMLText:(NSString *)xmltext feedClass:(Class)classOfGData {
    NSXMLDocument *xmldocument =
    [[NSXMLDocument alloc] initWithXMLString:xmltext
                                     options:0
                                       error:NULL];
    GDataFeedBase *gfeed =
    [[classOfGData alloc] initWithXMLElement:[xmldocument rootElement]
                                      parent:nil];
    
    Feed *feed;
    if (classOfGData == [GDataFeedBlog class]) {
        feed = [[[FeedBlog alloc] initWithGDataFeed:gfeed] autorelease];
    } else if (classOfGData == [GDataFeedBlogPost class]) {
        feed = [[[FeedBlogPost alloc] initWithGDataFeed:gfeed] autorelease];
    } else {
        NSLog(@&amp;quot;Unexpected class was specified: %@&amp;quot;, classOfGData);
        feed = nil;
    }

    [xmldocument release];
    [gfeed release];
    return feed;
}
&lt;/pre&gt;

&lt;p&gt;ここで引っかかったのは、引数としてわたってきた NSString から NSXMLDocument を生成する部分。より正確には NSXMLDocument の initWithXMLString:options:error に対する第 2 引数 options の値だ。始めこれに NSXMLDocumentTidyXML という定数を指定していた。リファレンスによれば構造的に誤りをふくむ XML を正しい XML に修正する、と書いてあった。とくに XML の修正機能が必要だったわけではなく、ただなんとなく選んだだけだ。ところが、この指定で記事内容(HTML で書かれた部分)の pre 要素の改行がすべて取り除かれてしまった。&lt;/p&gt;

&lt;p&gt;たしかにリファレンスには「pretty-print で付けられた余分の空白等を取り除く」と書かれていた。ここを見落としたのだ。&lt;/p&gt;

&lt;p&gt;ともあれ、この値として 0 (余分な動作は何もしない) を指定して、期待した通りの結果を得ることができた。&lt;/p&gt;

&lt;h4&gt;Feed オブジェクトをキャッシュする&lt;/h4&gt;

&lt;p&gt;メモリ中に生成した Feed オブジェクトをキャッシュしておくための仕組みを FeedManager に組み込んだ。といっても、実体は NSMutableDictionary だ。&lt;/p&gt;

&lt;p&gt;この仕組みは、ブログ一覧 (FeedBlog) の方はアカウントをキーとしてキャッシュし、ブログ記事 (FeedBlogPost) の方はアカウントと Blog ID をキーとしてキャッシュする。環境設定パネルでブログが切り替えられたとき(またはアカウントが変更されたとき)に、すでに生成ずみの Feed オブジェクトがあれば、ネットワークに取りに行くこともなく、さらにはディスクから読み直すこともせずに、メモリ中のオブジェクトを使うことができるようにするためのものだ。&lt;/p&gt;

&lt;p&gt;以下にインタフェース部だけを示す。実装は、単なる NSMutableDictionary へのアクセスで、ほぼ自明だから。Blog ID とアカウントの 2 つをキーとして指定する方は、単に文字列として連結しているだけだ。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
// FeedCache is made to encapsulate the implementation detail
// to manage cache for Feed instances.
@interface FeedCache : NSObject
{
    NSMutableDictionary *blogFeeds;
    NSMutableDictionary *postsFeeds;
}

// manage cache for Feed instances
- (Feed *)feedBlogForAccount:(NSString *)account;
- (void)setFeedBlog:(Feed *)feed account:(NSString *)account;

- (Feed *)feedBlogPostForBlogID:(NSString *)blogID account:(NSString *)account;
- (void)setFeedBlogPost:(Feed *)feed blogID:(NSString *)blogID account:(NSString *)account;

- (NSString *)keyForBlogID:(NSString *)blogID account:(NSString *)account;

@end
&lt;/pre&gt;

&lt;h4&gt;プロトタイプ #0 の完成まで、もう少し&lt;/h4&gt;

&lt;p&gt;始めに挙げた機能のうち、残るはラベルによる検索だけとなった(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能&lt;/a&gt;」参照)。&lt;/p&gt;

&lt;p&gt;必要になる仕組みとしては、おおよそ「ラベルの列挙」と「指定されたラベルが付いた記事の抽出」の 2 つ。前者は記事を表現した Entry オブジェクト (実体は GDataEntryBlogPost) からラベルをかき集めれば良い。後者については、GAE 版では GData API にクエリーをかけたが、Mac 版ではフィードはすべてローカルに保存してある(メモリ中にオブジェクトとして、あるいはディスク中に XML テキストとして)から、GData API をたたく必要はない。GData ライブラリにメモリ中のオブジェクトに対してフィルタをかけられればそれを使うし、できないならその部分を自前で作り込むことになる。&lt;/p&gt;

&lt;p&gt;実装よりも「見せ方」をどうするかが悩みどころ。プロトタイプなのだから、凝った UI を作るつもりはないのだけれど……。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51TjCsZCcwL._SL160_.jpg" alt="Objective-C逆引きハンドブック" title="Objective-C逆引きハンドブック" width="113" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;Objective-C逆引きハンドブック&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;林 晃&lt;br /&gt;シーアンドアール研究所 ( 2010-02-26 )&lt;br /&gt;ISBN: 9784863540514&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4863540515" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLDocument_Class/Reference/Reference.html"&gt;NSXMLDocument Class Reference&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html"&gt;FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass_16.html"&gt;クラスクラスタの導入 - オフライン機能の実現 #4 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7921564766081688911?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7921564766081688911/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7921564766081688911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7921564766081688911'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/feedmanager-5-macbloggerglass.html' title='FeedManager が完成 - オフライン機能の実現 #5 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4294706567914032683</id><published>2010-12-17T07:34:00.002+09:00</published><updated>2010-12-17T23:54:07.897+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='ユーザ体験'/><category scheme='http://www.blogger.com/atom/ns#' term='2. 検索のある生活'/><title type='text'>twitter より (2010-12-16)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/15338936821874688"&gt;18:34&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;Safari の Command + Ctrl + D を知らなかったよ。単語をダブルクリックで選択状態にして、Command + Ctrl + D。単語のそばに小さい辞書ウィンドウが開く。ナニコレ、便利すぎる。→ &lt;a href="http://bit.ly/ethdtl"&gt;http://bit.ly/ethdtl&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4294706567914032683?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4294706567914032683/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-16.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4294706567914032683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4294706567914032683'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-16.html' title='twitter より (2010-12-16)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2766776478412144935</id><published>2010-12-16T23:47:00.003+09:00</published><updated>2010-12-16T23:52:19.207+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>クラスクラスタの導入 - オフライン機能の実現 #4 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;FeedManager を実装していて、どうにも FeedBlog と FeedBlogPost の取り扱いがメンドウになってきたので、両者に共通の基底クラスを作ることにした。ついでに、これらをクラスクラスタとしてまとめることにした。&lt;/p&gt;

&lt;h4&gt;クラスクラスタとは&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt;から引用する。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809"&gt;詳解 Objective-C 2.0&lt;/a&gt;」p.245 より)&lt;br&gt;
&lt;b&gt;クラスクラスタ&lt;/b&gt; (class cluster) とは、同じインタフェースを持ち、同じ機能を提供する複数のクラスの集合体です。インタフェースを表す抽象クラスのみを公開し、これをそのクラスクラスタの&lt;b&gt;パブリッククラス&lt;/b&gt; (public class) と呼びます。個々の具体的なクラスはパブリッククラスのインタフェースによって抽象化され、クラスタの内部に隠蔽されます。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Cocoa (Foundation フレームワーク)で提供されている具体的なクラスタには、NSString や NSArray などがある。&lt;/p&gt;

&lt;p&gt;公式ドキュメントにも独立した項目として解説されているが(→「&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ClassCluster.html%23//apple_ref/doc/uid/TP40008195-CH7-SW1"&gt;Cocoa Core Competencies: Class Cluster&lt;/a&gt;」等)、言語および実行環境として特別なサポートの仕組みが提供されているわけではない。設計上の慣習 (convention) と見るべき。デザインパターンよりはもっと広い概念だ。&lt;/p&gt;

&lt;p&gt;ずい分、昔のことだけど、C++ で可変 (mutable) オブジェクトとして生成したものを、不変 (immutable) オブジェクトとして返すなんてコードを書いていた。あれもクラスクラスタの一種だと言える。&lt;/p&gt;

&lt;h4&gt;実例: Feed クラス&lt;/h4&gt;

&lt;p&gt;MacBloggerGlass のモデルクラスは、これまで FeedBlog と FeedBlogPost として作ってきた。前者は Google アカウントごとのブログの一覧を表し、GData ライブラリのクラス、GDataFeedBlog に対応する。後者は、ブログごとの記事のフィードを表現するもので、GData ライブラリでは GDataFeedBlogPost に対応する。FeedBlog は環境設定パネルでブログの一覧を表示するために使い、FeedBlogPost はメインウィンドウで記事の一覧とその内容表示に使っている。&lt;/p&gt;

&lt;p&gt;GData ライブラリのフィードを表現するクラスたちには共通の基底クラス (GDataFeedBase) があり、ほとんどの機能はこのクラス(あるいはさらにその基底クラス)で定義されていて、GDataFeedBlog と GDataFeedBlogPost の違いはわずかなものだ。当然、そのラッパーと言うべき FeedBlog と FeedBlogPost にも大きな差はなかった。&lt;/p&gt;

&lt;p&gt;以下に、Feed クラスタの公開クラス Feed のインタフェース部を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface Feed : NSObject {
    NSMutableArray *entries;
    NSMutableDictionary *pairOfLinks;
    GDataFeedBase *gDataFeed;
}

@property (readwrite, retain) NSMutableArray *entries;
@property (readonly) NSMutableDictionary *pairOfLinks;

// class factories
+ (Feed *)feedWithGDataFeedBlog:(GDataFeedBlog *)gfeed;
+ (Feed *)feedWithGDataFeedBlogPost:(GDataFeedBlogPost *)gfeed;
+ (Feed *)feedWithXMLText:(NSString *)xmltext feedClass:(Class)classOfGData;

// initializer
- (id)initWithGDataFeed:(GDataFeedBase *)gfeed; // desiginated initializer

// element manipulation
// subclass must be override the following 2 methods.
+ (Class)classForEntries;
+ (Entry *)entryWithGDataEntry:(GDataEntryBase *)gentry;

// for KVC compliance (required)
- (void)insertObject:(Entry *)entry inEntriesAtIndex:(NSUInteger)index;
- (void)removeObjectFromEntriesAtIndex:(NSUInteger)index;
- (void)replaceObjectInEntriesAtIndex:(NSUInteger)index withObject:(Entry *)entry;
// for KVC compliance (optional)
- (int)countOfEntries;
- (Entry *)objectInEntriesAtIndex:(NSUInteger)index;

// utilities
- (NSString *)XMLText;
- (NSUInteger)findEntry:(NSString *)identifier;

// for subclasses
- (void)setGDataFeed:(GDataFeedBase *)gfeed;
@end
&lt;/pre&gt;

&lt;p&gt;継承関係にあるクラス群をクラスクラスタとして機能させるには、状況に合わせて具体的な派生クラスを生成するためのファクトリーメソッドが必要になる。上記の例では 3 つのファクトリーメソッドを Feed クラスのクラスメソッドとして定義している。対応する GDataFeedBase オブジェクトを指定して生成するものが 2 つと、もう 1 つは XML テキスト(フィードの生データ)から生成するものだ。XML から GDataFeedBase オブジェクトを作り、それに対応する Feed クラスを生成することを想定している。このため、どの GDataFeedBase クラスを作るかを引数でわたすようになっている。&lt;/p&gt;

&lt;p&gt;また、Feed クラスが要素として抱えるオブジェクトの方も同様の理由でクラスクラスタとしている(Entry)。ただ、こちらの方は Feed よりもさらに派生クラスの差異が目立たないため、ほとんど Entry 単独のクラスのようになってしまった。まあ、クラスクラスタ化してしまえば、後から派生クラスを基底クラスにまとめたとしても(あるいは派生クラスを増やしたとしても)、それを使う側のコードに影響はほとんどない。派生クラスが不要だと思えば、削除してしまえば良い。&lt;/p&gt;

&lt;p&gt;実はこのコード、まだ実装が完了していないので、動くかどうかは未確認。正確には、Feed クラスタの実装は完了しているが、それを使う FeedManager の方がまだできていない。明日にはできると思うのだけど……。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;クラスクラスタの説明は「CHAPTER 10 抽象クラスとクラスクラスタ」の「10-02 クラスクラスタ」にある。あと、(インスタンス)オブジェクトの所属するクラスを調べる方法については「07-01 NSObject クラス」に書かれている。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ClassCluster.html%23//apple_ref/doc/uid/TP40008195-CH7-SW1"&gt;Cocoa Core Competencies: Class Cluster&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html"&gt;FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2766776478412144935?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2766776478412144935/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/4-macbloggerglass_16.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2766776478412144935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2766776478412144935'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/4-macbloggerglass_16.html' title='クラスクラスタの導入 - オフライン機能の実現 #4 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2921794440454004757</id><published>2010-12-15T22:59:00.002+09:00</published><updated>2010-12-15T22:59:26.957+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;前回の記事で書いたように(→「&lt;a href="http://logrepo.blogspot.com/2010/12/オフライン機能の実現-2-macbloggerglass.html"&gt;オフライン機能の実現 #2&lt;/a&gt;」)、フィードデータの保存、読み込みは、FeedManager と呼ぶ管理用のクラスを作り、ネットワークからのデータの取得と合わせて、その詳細を隠すことに決めた。&lt;/p&gt;

&lt;p&gt;今日は、その FeedManager を作っている。残念ながら FeedManager はまだ完成していない。今回は、この実装の過程で気付いた注意点を 2 点ほど書き留めておく。&lt;/p&gt;

&lt;h4&gt;シングルトン・パターン&lt;/h4&gt;

&lt;p&gt;FeedManager にはシングルトン・パターンを使う。Cocoa でシングルトン・パターンを使うには少し注意が必要だ。簡単に言うと、NSObject で定義ずみのいくつかのメソッドを上書きしなければならない。また、シングルトン・パターンには付きもののファクトリーメソッドの定義にも Cocoa 独特の「お作法」がある。詳しくは公式ドキュメントの &lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html%23//apple_ref/doc/uid/TP40002974-CH4-SW32"&gt;Cocoa Fundamentals Guide: Cocoa Objects: Creating a Singleton Instance&lt;/a&gt; を参照のこと。&lt;/p&gt;

&lt;p&gt;この「Creating a Singleton Instance」には、シングルトン・パターンを実装するための実例が載っていて、そのまま流用できる。以下が、FeedManager 用に書き直したもの。sharedManager の戻り値以外は、実例そのままになっている。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
+ (FeedManager *)sharedManager {
    if (sharedFeedManager == nil) {
        sharedFeedManager = [[super allocWithZone:NULL] init];
    }
    return sharedFeedManager;
}

+ (id)allocWithZone:(NSZone *)zone {
    return [[self sharedManager] retain];
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

- (void)release {
    // do nothing
}

- (id)autorelease {
    return self;
}
&lt;/pre&gt;

&lt;h4&gt;コールバック方式への対応&lt;/h4&gt;

&lt;p&gt;GData ライブラリではフィードデータの取得は非同期に実行される。このため、アプリ(のコントローラ)は、コールバックのメソッドを呼び出してもらうことでその完了を知る。データ取得が非同期に実行されるということは、取得したデータからオブジェクトを生成して返す GetFeedBlog のような関数を定義できないことを意味する。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@implementation FeedManager
- (void)trigger {
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    FeedBlog *feedBlog = GetFeedBlog(account, password);
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}
@end
&lt;/pre&gt;

&lt;p&gt;非同期方式の場合は、取得開始のトリガーとなるコードと、取得したデータを受け取るコードの間で、引数と戻り値を使った直接的な情報の交換(通信)ができない。これを解決するための方法は 2 つある。1 つは、トリガーと受け取りのコードを同一のオブジェクトのメソッドとして実装し、オブジェクトのインスタンス変数を介して情報のやり取りを行うもの。以下のような感じのコードになる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface FeedManger : NSObject {
    FeedBlog *feedBlog;
}
@end

@implementation FeedManager
- (void)trigger {
    Service *service = &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    [service fetch];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}

- (void)callback:(id)dataObject {
    feedBlog = [[FeedBlog alloc] initWithDataObject:dataObject];
}
@end
&lt;/pre&gt;

&lt;p&gt;GData ライブラリの GDataServiceBase クラスでは、上記とは別の方法をサポートしている。それは、サービス(フィードデータの取得)がコールバックに対して送り返すデータの一部として、クライアント(サービスを利用するコード)がユーザデータとして任意のオブジェクトを添付する仕組みだ。具体的には GDataServiceBase のインスタンスメソッド setServiceUserData だ。&lt;/p&gt;

&lt;p&gt;GDataServiceBase に setServiceUserData でわたされたオブジェクトは、その後の同サービスオブジェクトに対するフィード取得のリクエストを処理する中で、コールバックに引数としてわたすオブジェクト(GDataServiceTicket)に封入される。コールバックメソッドでは、GDataServiceTicket の userData として、トリガーコードが添付したオブジェクトを受け取ることができる。おおよそ、こういう感じ(↓)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@implementation FeedManager
- (void)trigger {
    GDataServiceBase *service = &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    id userData = /* an arbitrary object */;
    [service setServiceUserData:userData];
    [service fetchFeedWithURL:&lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;]n;
}

- (void)callback:(GDataServiceTicket *)ticket &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;] {
    id userData = [ticket userData];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}
&lt;/pre&gt;

&lt;p&gt;この userData として、フィードデータ(から生成したオブジェクト)を受け取るオブジェクト(レシーバー)を添付すれば、callback の中でレシーバーにフィードデータをわたすことができる(レシーバーのメソッド経由で)。&lt;/p&gt;

&lt;p&gt;FeedManager ではフィードデータから生成されるオブジェクトを保持するインスタンス変数を別の目的(オブジェクトが生成ずみかどうかの判別)で使っているため、GDataServiceBase (のサブクラス)に userData を添付する方法を採用している。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html%23//apple_ref/doc/uid/TP40002974-CH4-SW32"&gt;Cocoa Fundamentals Guide: Cocoa Objects: Creating a Singleton Instance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/オフライン機能の実現-2-macbloggerglass.html"&gt;オフライン機能の実現 #2 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2921794440454004757?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2921794440454004757/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2921794440454004757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2921794440454004757'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/feedmanager-3-macbloggerglass.html' title='FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1291928563190155252</id><published>2010-12-14T23:05:00.002+09:00</published><updated>2010-12-14T23:05:34.363+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='デザイン'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>オフライン機能の実現 #2 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;GData オブジェクトの保存については実現の目処が立った(→「&lt;a href="http://logrepo.blogspot.com/2010/12/gdata-1-macbloggerglass.html"&gt;GData オブジェクトを保存する&lt;/a&gt;」)ので、今回はオフライン機能全般について検討してみた。&lt;/p&gt;

&lt;h4&gt;ユーザ体験&lt;/h4&gt;

&lt;p&gt;前にも書いたように、MacBloggerGlass はドキュメントアーキテクチャに基づいたアプリではない。言わば(Blogger で作ったブログ専用の)ブログブラウザだが、アプリの分類では特殊用途の RSS リーダだ。そして、ユーザ体験から言えば Mail.app と言ったところ(ただし閲覧専用)。&lt;/p&gt;

&lt;p&gt;Mail.app (を始めとするメーラのほとんど)がそうであるように、このアプリの場合もユーザが明示的に保存を指示することはない。Mac (や PC) のアプリでは、こういうタイプは少数派だが、iOS アプリではむしろ主流となっている。データを保存し読み込むというよりは、(アプリの終了によって)中断した状態を復元する方式だ。&lt;/p&gt;

&lt;p&gt;現状の UI は、Google アカウントを切り替えることを想定したものではないが(わたし自身、複数のアカウントは使っていないし)、オフライン機能のためのデータ保存はアカウントごとに行う。これは、アカウントを切り替えたとしてもデータを上書きしないことを意味する。この方式を取った場合、アカウントごとにデータを削除する手段も用意すべきだが、それは将来の検討課題としておく。&lt;/p&gt;

&lt;p&gt;一方で、ブログを切り替える UI を持つのだから、データの保存をブログごとに独立して行うのは当然。ただし、こちらもデータの削除は将来の検討課題に。&lt;/p&gt;

&lt;p&gt;データの保存はユーザに意識させないが、データの更新はユーザに明示的な指示を出してもらう。メニューの「Reload」をそれに使う。アプリの動きとしては、(User Defaults に保管されている)ブログ ID に対応するフィードデータが存在した場合、データの取得は行わなず、保存されたデータから GData オブジェクトを復元し、表示する。もちろん、データが保存されていないければ、特にユーザからの指示がなくとも取得する。&lt;/p&gt;

&lt;h4&gt;デザイン(設計)&lt;/h4&gt;

&lt;p&gt;保存先は &lt;span class="path"&gt;~/Library/BloggerGlass&lt;/span&gt; とする。この中にアカウント名でフォルダを作る。そこにさらに Blog ID でフォルダを作り、データファイルはこの Blog ID によるフォルダに置く。ファイル名は feedblogpost.xml とする。将来的にフィードデータを分割保存することがあるかもしれない。その場合は feedblogpost-20101214.xml のように日付をファイル名に取り込む。&lt;/p&gt;

&lt;p&gt;また、アカウントごとのブログ一覧のフィードも、アカウント名のフォルダ直下に置く。ファイル名は feedblog.xml とする。&lt;/p&gt;

&lt;p&gt;データファイルの形式は XML のテキスト。言わゆる human readable な形にしておく。&lt;/p&gt;

&lt;p&gt;「ユーザ体験」のところで書いたように、ユーザに保存を指示させない方式だとすると、そのトリガーをどうするか？ まずは、フィードの取得が完了し(GData オブジェクトが作られ)た直後に実行される、GData ライブラリのコールバックが良いだろう。ユーザへのレスポンスの速さが気になるなら別スレッドで非同期に保存すれば良い。ただ、今の段階では同期的に保存する方式で。&lt;/p&gt;

&lt;p&gt;では読み込みのトリガーは？ これはブログの切り替えになる。つまり、環境設定パネルでブログが切り替えられた通知のハンドラで行うことになる。今は、このタイミングでフィードをネットワーク経由で取得に行っている。保存が実現でき後は、ここでまず保存されたデータの有無を確認し、あればそれを読み込み、なければネットワークにアクセスする、となる。&lt;/p&gt;

&lt;p&gt;データの保存、読み込みはアプリコントローラとは独立させる。FeedManager クラス(仮名)を作り、ローカルのデータとネットワーク経由の取得の違いを隠す。アプリコントローラは FeedManager にアカウント名と Blog ID をわたして、FeedBlogPost オブジェクトを受け取ることになる。&lt;/p&gt;

&lt;h4&gt;実装に関する考慮点&lt;/h4&gt;

&lt;p&gt;&lt;span class="path"&gt;~/Library&lt;/span&gt; に保存用のフォルダを作る際には、パスを直接指定するのではなく(そもそもユーザ ID を知らずにユーザのホームディレクトリを直接指定できない)、Cocoa に用意された API 経由でライブラリ用フォルダを取得する。それが NSSearchPathForDirectoriesInDomains 関数だ。&lt;/p&gt;

&lt;p&gt;NSSearchPathForDirectoriesInDomains 関数は 3 つの引数を取る。1 つ目がフォルダの種類、2 つ目がフォルダのドメイン(のマスク)、そして 3 つ目がチルダ(~)を展開するか否か。&lt;/p&gt;

&lt;p&gt;ドメインマスクには NSUserDomainMask、NSLocalDomainMask、NSNetworkDomainMask、NSSystemDomainMask、NSAllDomainMask のうちから 1 つを指定する。今回の実装ではユーザのライブラリフォルダ(のパス)を知りたいのだから、NSUserDomainMask で良い。&lt;/p&gt;

&lt;p&gt;戻り値は配列になる。ユーザドメイン(のマスク)を指定すれば要素は 1 つだけだ。&lt;/p&gt;

&lt;p&gt;以下に簡単なサンプルプログラムを示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
int main (int argc, const char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    NSArray *paths =
    NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
                                        NSUserDomainMask,
                                        YES);
    
    NSString *path;
    for (path in paths) {
        NSLog(@&amp;quot;Path: %@&amp;quot;, path);
    }
    
    [pool drain];
    return 0;
}
&lt;/pre&gt;

&lt;p&gt;上記のプログラムの実行結果は以下の通り(コンソールへの出力)。&lt;/p&gt;

&lt;pre class="terminal"&gt;
2010-12-14 22:35:48.525 ExpApp[11456:903] Path: /Users/mnbi/Library
&lt;/pre&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/LowLevelFileMgmt/Articles/StandardDirectories.html%23//apple_ref/doc/uid/20001279-BCIIHBEE"&gt;Low-Level File Management Programming Topics: Creating Paths and Locating Directories&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/gdata-1-macbloggerglass.html"&gt;GData オブジェクトを保存する - オフライン機能の実現 #1 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1291928563190155252?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1291928563190155252/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/%E3%82%AA%E3%83%95%E3%83%A9%E3%82%A4%E3%83%B3%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E7%8F%BE-2-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1291928563190155252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1291928563190155252'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/%E3%82%AA%E3%83%95%E3%83%A9%E3%82%A4%E3%83%B3%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E7%8F%BE-2-macbloggerglass.html' title='オフライン機能の実現 #2 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5395437586279053534</id><published>2010-12-13T22:37:00.004+09:00</published><updated>2010-12-14T21:58:13.584+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>GData オブジェクトを保存する - オフライン機能の実現 #1 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;今回から、オフライン機能を実現するための作業を開始する。まずは、フィードから構築したオブジェクトの保存と復元について実験する。&lt;/p&gt;

&lt;h4&gt;GData オブジェクトの保存と復元&lt;/h4&gt;

&lt;p&gt;MacBloggerGlass では内部データとして GData ライブラリのオブジェクト(以下では GData オブジェクトと呼ぶ)をそのままの形で保持している。モデルオブジェクトで覆うことで、表示のために加工が必要なデータ(ID や日付け)はキャッシュしているものの、記事のタイトルや内容は GData オブジェクトのデータをそのまま使っている。このためオフライン機能を実現するためには、GData オブジェクトの保存と復元が必要だ。&lt;/p&gt;

&lt;p&gt;GData オブジェクト自体には Cocoa アプリでそのまま保存、復元できるための仕組み(NSCoding プロトコルの実装)はないが、その代わりに NSXMLDocument として参照することができるように作られている。&lt;p&gt;

&lt;p&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLDocument_Class/Reference/Reference.html"&gt;NSXMLDocument&lt;/a&gt; は NSCoding プロトコルこそ実装していないものの、NSData 化するためのメソッドが用意されており、これを抱えるクラスで NSCoding を実装することは難しくない。&lt;/p&gt;

&lt;p&gt;GData オブジェクト (GDataObject とその派生クラスたち)を NSData 化するための手順は以下のようになる:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GData オブジェクトから NSXMLDocument オブジェクトを取り出す。 (XMLDocument メソッド)&lt;/li&gt;
&lt;li&gt;取り出した NSXMLDocument を NSData 化する。 (XMLData メソッド)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;また、NSData から GData オブジェクトを復元する手順は以下の通り:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NSData から NSXMLDocument オブジェクトを作る。 (initWithData:options:error: メソッド)&lt;/li&gt;
&lt;li&gt;NSXMLDocument からルート要素を取り出す。 (rootElement メソッド)&lt;/li&gt;
&lt;li&gt;取り出したルート要素で GData オブジェクトを初期化する。 (initWithXMLElement:parent: メソッド)&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;実験的な実装&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;環境設定パネル&lt;/a&gt;のモデルとなっている FeedBlog は内部にブログ一覧のフィード(GDataFeedBlog)を抱えるオブジェクトで、初期化に GDataFeedBlog のオブジェクトを指定する。今回は、このクラスを使って GDataFeedBlog の保存と復元を作り込むことにする。&lt;/p&gt;

&lt;p&gt;インタフェース部では NSCodig プロトコルを実装することを宣言する。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface FeedBlog : NSObject &amp;lt;NSCoding&amp;gt; {
    NSMutableArray *entries;
    GDataFeedBlog *gDataFeed;
}
&lt;/pre&gt;

&lt;p&gt;実装部では、NSCoding プロトコルの 2 つのメソッドを実装する。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
// for conformance to NSCoding protocol
- (id)initWithCoder:(NSCoder *)aDecoder {
    [super init];
    NSData *data = [aDecoder decodeDataObject];
    NSXMLDocument *xmldocument =
    [[NSXMLDocument alloc] initWithData:data
                                options:NSXMLDocumentTidyXML
                                  error:NULL];
    NSXMLElement *xmlroot = [xmldocument rootElement];
    GDataFeedBlog *aFeed =
    [[GDataFeedBlog alloc] initWithXMLElement:xmlroot parent:nil];
    return [self initWithGDataFeed:aFeed];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    NSData *data = [[gDataFeed XMLDocument] XMLData];
    [aCoder encodeDataObject:data];
}
&lt;/pre&gt;

&lt;p&gt;NSCoding プロトコルを実装したオブジェクトは、保存時に NSKeyedArchive を使って NSData 化する。逆に、NSData オブジェクトからの復元には NSKeyedUnarchive を使う。&lt;/p&gt;

&lt;p&gt;以下は環境設定パネルのコントローラクラス(PreferenceController)の実装の一部で、FeedBlog を NSData 化したものを User Defaults に保存している。本来、ここに保存するようなデータではないが、とりあえずこれで FeedBlog の保存と復元を確認することができる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    // feed data
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.feed];
    [defaults setObject:data forKey:BGKeyFeedBlogData];
&lt;/pre&gt;

&lt;p&gt;復元時は User Defaults から NSData オブジェクトを取り出し、NSKeyedUnarchive にかけている。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
    // load feed blog data
    NSData *data = [self getUserDefaults:BGKeyFeedBlogData];
    @try {
        self.feed = [NSKeyedUnarchiver unarchiveObjectWithData:data];        
    }
    @catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        self.feed = [[[FeedBlog alloc] init] autorelease];
    }
    @finally {
    }
&lt;/pre&gt;

&lt;h4&gt;次の一手&lt;/h4&gt;

&lt;p&gt;保存と復元の仕組みはわかったので、次はアプリとして、どこに、どのような形式でデータを書き出すかを考えなくてはならない。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass は、Cocoa のドキュメントアーキテクチャを使ったアプリではない。フィードを XML として書き出すことを選んだとしても Finder からそれをダブルクリックしてアプリを開くというタイプのものではない。ユーザ体験から見たアプリのタイプとしては、Mail.app に近いものを想定している。&lt;span class="path"&gt;~/Library&lt;/span&gt; の下にアプリ専用のフォルダを作って、そこにブログごとのフィードデータを置くというような方式になるか。&lt;/p&gt;

&lt;p&gt;書き出しの形式に関しては、まずは XML (のテキスト)で良いだろう。パフォーマンス上の問題が出てこない限り、人が読める形にしておくとデバッグをふくめて、何かと便利だから。ただ、データをブログごとに分けるのは当然として、同じブログのすべての記事を 1 つにまとめるのか、それとも一定の数で分割するのかについては検討する必要がある。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41pyKTm2fxL._SL160_.jpg" alt="Cocoa Programming for Mac OS X" title="Cocoa Programming for Mac OS X" width="121" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;Aaron Pablo Hillegass&lt;br /&gt;Addison-Wesley Professional ( 2008-05-15 )&lt;br /&gt;ISBN: 9780321503619&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Chapter 10 Archiving では NSCoder を使ったオブジェクトの保存と復元のサンプルが載っている。&lt;/p&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51TjCsZCcwL._SL160_.jpg" alt="Objective-C逆引きハンドブック" title="Objective-C逆引きハンドブック" width="113" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;Objective-C逆引きハンドブック&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;林 晃&lt;br /&gt;シーアンドアール研究所 ( 2010-02-26 )&lt;br /&gt;ISBN: 9784863540514&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4863540515" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;CHAPTER 10 に XML の操作に関する項目が載っている。NSXMLDocument の NSData 化は 187、ルート要素の取り出しは 188。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLDocument_Class/Reference/Reference.html"&gt;NSXMLDocument Class Reference&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る #1 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-5395437586279053534?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/5395437586279053534/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/gdata-1-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5395437586279053534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5395437586279053534'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/gdata-1-macbloggerglass.html' title='GData オブジェクトを保存する - オフライン機能の実現 #1 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-9074951191215145118</id><published>2010-12-12T20:46:00.004+09:00</published><updated>2010-12-12T20:50:24.657+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><title type='text'>キーチェーンサービスを使ってパスワードを保存する</title><content type='html'>&lt;p&gt;以前の記事で書いたように(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題&lt;/a&gt;」)、ユーザのパスワード、それもアプリとは異なる別のサービス用のアカウントのものを、アプリの設定(User Defaults)にそのまま保存するのは望ましくない。たとえ、アプリの設定(~/Library/Preferences に plist ファイルとして保存される)がバイナリ形式で、かつファイルモードが 0600 であったとしても。&lt;/p&gt;

&lt;p&gt;パスワードを保存するなら、やはり「キーチェーン」を使うべきだ。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://developer.apple.com/library/mac/#documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html"&gt;Keychain Services Programming Guide: Introduction&lt;/a&gt;」より)&lt;br&gt;
Keychain Services provides secure storage of passwords, keys, certificates, and notes for one or more users. A user can unlock a keychain with a single password, and any Keychain Services–aware application can then use that keychain to store and retrieve passwords.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;今回は、このキーチェーンサービスについて調べてみた。&lt;/p&gt;

&lt;h4&gt;アプリへの組み込み&lt;/h4&gt;

&lt;p&gt;アプリからキーチェーンサービスにアクセスするために必要な関数は以下の 2 つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SecKeychainAddGenericPassword (キーチェーンにパスワードを追加)&lt;/li&gt;
&lt;li&gt;SecKeychainFindGenericPassword (キーチェーンからパスワードを取得)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;これをアプリ内で使う場合の注意点は、(a) security/security.h を import すること、(b) Security.framework をリンクすること、の 2 点だ。(a) はソースに書くだけ。一方、(b) のためには Xcode のプロジェクトウィンドウで以下のような操作を行う。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;「グループとファイル」の「Frameworks」グループ上でポップアップメニューを出し「追加」&amp;gt;「既存のフレームワーク...」を選ぶ。&lt;/li&gt;
&lt;li&gt;現れたシートから Security.framework を選び「追加」ボタンを押す。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上記を実行すれば「Frameworks」グループ内に Security.framework が追加されるが、実際にはどこでポップアップメニューを開いても大差はない。それよりも重要なのは「ターゲット」のビルドフェーズ「バイナリをライブラリにリンク」に Security.framework が追加されていることだ。&lt;/p&gt;

&lt;h4&gt;サンプルアプリ&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/zukDyFNRDO6yy0wBxCtNWw?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TQSyKP6lunI/AAAAAAAABps/Pc9TvxjtRAk/s288/keychaintest_app.png" height="184" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;この 2 つの関数を使って、簡単なサンプルアプリを作ってみた。右のスクリーンショットがそのアプリウィンドウだ。Save Password でアカウントとパスワードを指定して「Save」ボタンを押すと、パスワードがキーチェーンに保存される。次に、Load Password でアカウントを入力し「Load」ボタンを押すと、下のパスワードフィールドに保存したパスワードが表示される。もちろん、アカウントが間違っていればパスワードの取得に失敗する。&lt;/p&gt;

&lt;p&gt;デフォルトのキーチェーンがロックされていれば、保存時にロックを解除するかというダイアログが出てくるし、同様に取得時にはキーチェーンへのアクセスを許可するかというダイアログも出てくる。また、実際にパスワードが保存できていることは「キーチェーンアクセス.app」を起動すれば確認できる。&lt;/p&gt;

&lt;p&gt;以下に、アプリのソースの一部(AppController.{h,m})を示す。&lt;/p&gt;

&lt;pre class="clear prettyprint lang-m"&gt;
#import &amp;lt;Cocoa/Cocoa.h&amp;gt;


@interface AppController : NSObject {
    NSTextField *accountFieldForSave;
    NSTextField *passwordFieldForSave;
    NSTextField *accountFieldForLoad;
    NSTextField *passwordFieldForLoad;
}

@property (assign) IBOutlet NSTextField *accountFieldForSave;
@property (assign) IBOutlet NSTextField *passwordFieldForSave;
@property (assign) IBOutlet NSTextField *accountFieldForLoad;
@property (assign) IBOutlet NSTextField *passwordFieldForLoad;

- (IBAction)load:(id)sender;
- (IBAction)save:(id)sender;

@end
&lt;/pre&gt;

&lt;pre class="prettyprint lang-m"&gt;
#import &amp;lt;Security/Security.h&amp;gt;
#import &amp;quot;AppController.h&amp;quot;

NSString * const KCTServiceName = @&amp;quot;KeyChainTest App&amp;quot;;

@implementation AppController

@synthesize accountFieldForSave, passwordFieldForSave;
@synthesize accountFieldForLoad, passwordFieldForLoad;

- (IBAction)load:(id)sender {
    NSString *account = [accountFieldForLoad stringValue];

    const char *serviceName = [KCTServiceName UTF8String];
    const char *accountName = [account UTF8String];

    void *passwordData = nil;
    SecKeychainItemRef itemRef = nil;
    UInt32 passwordLength;
    
    OSStatus status;
    status = SecKeychainFindGenericPassword(NULL,
                                            strlen(serviceName),
                                            serviceName,
                                            strlen(accountName),
                                            accountName,
                                            &amp;passwordLength,
                                            &amp;passwordData,
                                            &amp;itemRef);

    NSLog(@&amp;quot;Load password: status = %d&amp;quot;, status);
    if (status == noErr) {
        NSString *password =
        [[NSString alloc] initWithBytes:passwordData
                                 length:passwordLength
                               encoding:NSUTF8StringEncoding];
        [passwordFieldForLoad setStringValue:password];

        status = SecKeychainItemFreeContent(NULL,
                                            passwordData);
        [password release];
    } else {
        [passwordFieldForLoad setStringValue:@&amp;quot;&amp;quot;];
    }

}

- (IBAction)save:(id)sender {
    NSString *account = [accountFieldForSave stringValue];
    NSString *password = [passwordFieldForSave stringValue];
    // !!!:
    const char *serviceName = [KCTServiceName UTF8String];
    const char *accountName = [account UTF8String];
    const char *passwordData = [password UTF8String];
    OSStatus status;
    status = SecKeychainAddGenericPassword(NULL,
                                           strlen(serviceName),
                                           serviceName,
                                           strlen(accountName),
                                           accountName,
                                           strlen(passwordData),
                                           passwordData,
                                           NULL);
    NSLog(@&amp;quot;Save password: status = %d&amp;quot;, status);
}

@end
&lt;/pre&gt;

&lt;p&gt;Cocoa のオブジェクトにくるまれていないサービスのため、使うのが少し面倒(NSString を直接わたせなかったり)だが、実質的に 2 つの関数を呼び出すだけでパスワードのような機密性の高い情報を安全に保管できる。やってみると思ったよりも簡単だった。&lt;/p&gt;

&lt;p&gt;実は、この 2 つの関数だけではパスワードを変更することができない(同じアカウントで Add を呼ぶとエラーになる)。実際のアプリ(MacBloggerGlass)に組み込むときは、もちろん変更もできるようにしなければならない。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html"&gt;Keychain Services Programming Guide&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題 - 環境設定パネルを作る #2 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-9074951191215145118?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/9074951191215145118/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/9074951191215145118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/9074951191215145118'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/blog-post.html' title='キーチェーンサービスを使ってパスワードを保存する'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TQSyKP6lunI/AAAAAAAABps/Pc9TvxjtRAk/s72-c/keychaintest_app.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2311148601586594935</id><published>2010-12-11T19:30:00.003+09:00</published><updated>2010-12-12T18:23:57.373+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa アプリに JavaScript のライブラリを組み込む - Xcode の使い方</title><content type='html'>&lt;p&gt;MacBloggerGlass に &lt;a href="http://code.google.com/p/google-code-prettify/"&gt;google-code-prettify&lt;/a&gt; を組み込んだ。今回はその手順を説明する。概略は次の通り。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;JavaScript ライブラリをプロジェクトのリソースとして追加する。&lt;/li&gt;
&lt;li&gt;JavaScript ファイルをコンパイル対象から外す。&lt;/li&gt;
&lt;li&gt;アプリバンドルへのファイルコピーのためのビルドフェーズを追加する。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;今回の手順は、ビルドにより作られるアプリバンドルを以下のような構造にするためのものだ。また、Xcode でプロジェクトウィンドウの「グループとファイル」で見たときにも &lt;span class="path"&gt;lib/prettify&lt;/span&gt; という構造に見え、かつ Finder から見えるフォルダ構造(つまりファイルシステム上の構造)も同じになるようにしている。Xcode では、プロジェクトのグループ構造が実際のフォルダ構造と同じである必要はないし、アプリバンドルの構造と一致させる必要もない。しかし、同じにしておく方がわかりやすい。むしろ、異なる構造にしておくと間違いの元だろう。&lt;/p&gt;

&lt;pre&gt;
MacBloggerGlass.app/
+-- Contents/
    +-- Info.plist
    +-- MacOS/
    |   +-- ...
    +-- PkgInfo
    +-- Resources/
        +-- English.lproj/
        |   +-- ...
        +-- template.html
        +-- default.css
        +-- &lt;b&gt;lib&lt;/b&gt;/
            +-- &lt;b&gt;prettify&lt;/b&gt;/
                +-- ...
                +-- &lt;b&gt;prettify.js&lt;/b&gt;
                +-- &lt;b&gt;prettify.css&lt;/b&gt;
&lt;/pre&gt;

&lt;p&gt;ちなみに、以下が現在のプロトタイプ #0 のスクリーンショット。google-code-prettify を組み込んだことによりソースコードが色付けされていることがわかる。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;MacBloggerGlass プロトタイプ #0&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;アプリウィンドウ&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/b5MOkyI8piJqDpdGOcRSBA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TQM25qMG60I/AAAAAAAABos/NG-hYFHbYSQ/s400/mbg_main_window_proto_0_with_prettify.png" height="312" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;&lt;a href="http://code.google.com/p/google-code-prettify/"&gt;google-code-prettify&lt;/a&gt; を組み込んだ。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;JavaScript ライブラリをプロジェクトのリソースとして追加する&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://code.google.com/p/google-code-prettify/"&gt;google-code-prettify&lt;/a&gt; は以下のように ~/tmp の下の lib フォルダに置いてあるものとする。この lib 以下をプロジェクトに追加することになる。&lt;/p&gt;

&lt;pre&gt;
~/tmp/lib/
+-- prettify/
    +-- ...
    +-- prettify.js
    +-- prettify.css
&lt;/pre&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/16g2LSvggFAfEcjbRYtUfA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TQM6m8ARNzI/AAAAAAAABo4/TUzPEijR82c/s288/xcode_add_js_files_to_resources.png" height="271" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;まず、プロジェクトウィンドウの「グループとファイル」から Resources を選択した状態で、メニューより「プロジェクト」&amp;gt;「プロジェクトに追加...」を実行する。ファイル選択シートが現れるので、上記の lib フォルダを選択し「追加」ボタンを押す。&lt;/p&gt;

&lt;p&gt;ここで、さらにオプションを選択するシートが現れるが、そこでは右のスクリーンショットのように選ぶ。これにより、プロジェクトのフォルダ内に lib フォルダ、さらにその中に prettify フォルダが作られ、ライブラリのファイルがコピーされている。&lt;/p&gt;

&lt;div class="clear"&gt;&lt;/div&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/h2c-vEueDr7A4HkDw0yCeA?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQM6nRdIcVI/AAAAAAAABo8/T5TCxU-8tGw/s800/xcode_add_js_files_to_resources_result.png" height="343" width="264" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;また、同時に「グループとファイル」では Resource の中に lib グループ、その中に prettify グループが作られている(右のスクリーンショットを参照)。&lt;/p&gt;

&lt;h4 class="clear"&gt;JavaScript ファイルをコンパイル対象から外す&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/RO5nY8Wom05Cd2QG45sJQQ?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TQM6nrzmWAI/AAAAAAAABpA/3reJEfEh_Ng/s800/xcode_js_files_in_compile_target.png" height="499" width="348" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Xcode のデフォルトルールでは、JavaScript ファイルはコンパイルが必要な「ソースファイル」の一種に設定されている。このため、JavaScript ファイルをプロジェクトに追加すると、ビルドフェーズの「ソースをコンパイル」に追加されてしまう(右のスクリーンショット参照)。&lt;/p&gt;

&lt;p&gt;このままでもビルド時に warning が出るだけで実害はないけれど、大量の黄色の三角を見るのは気に入らない。この warning を消すには「ソースをコンパイル」から JavaScript ファイルを削除すれば良い。具体的には「グループとファイル」から「ターゲット」&amp;gt;「(アプリ名).app」&amp;gt;「ソースをコンパイル」を開き(右のスクリーンショットの状態になる)、js ファイルをすべて選択して削除すれば良い。ここに並んでいるのはファイルへの参照だけなので、ここで削除してもプロジェクトに追加したファイル自体が(プロジェクトから)消えることはない。&lt;/p&gt;

&lt;p&gt;もう一つ。google-code-prettify には &lt;span class="path"&gt;prettify.css&lt;/span&gt; という CSS ファイルがふくまれている。Xcode のデフォルトルールでは CSS ファイルはコピーされるべきバンドルリソースになっており、ビルドフェーズの「バンドルリソースをコピー」に追加される。このままでは適切な場所((アプリ名).app/Contents/Resources/lib/prettify/)にコピーされないので、ここからは削除しておく。手順は js ファイルの場合と同様。&lt;/p&gt;

&lt;h4 class="clear"&gt;アプリバンドルへのファイルコピーのためのビルドフェーズを追加する&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/wS8Z0ERQv47HNnQh3Re4OA?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQM6oDAhzOI/AAAAAAAABpE/J69QDy4ymV0/s288/xcode_new_target_to_copy_files.png" height="91" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;最後のステップは、ビルドによって作られるアプリバンドルに JavaScript ライブラリが構造を保ったままコピーされるようにすることだ。これには、ファイルコピーのためのビルドフェーズを新たに追加すれば良い。&lt;/p&gt;

&lt;p&gt;具体的には、メニューから「プロジェクト」&amp;gt;「新規ビルドフェーズ」&amp;gt;「新規コピーファイル」を実行する。右のスクリーンショットのようなダイアログ(インスペクタのパネル)が現れるので、「デスティネーション」として「リソース」を選び(デフォルトでそうなっている)、「パス」に &lt;span class="path"&gt;lib/prettify&lt;/span&gt; を入力する。これは、アプリバンドルのリソースフォルダ中に &lt;span class="path"&gt;lib/prettify&lt;/span&gt; を作って対象ファイルをコピーする、というビルドフェーズを意味する。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/wDnuIN0PaV7CNTPKVEkZRQ?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQM6ocokVmI/AAAAAAAABpI/anXurJRTdNo/s800/xcode_new_target_to_copy_files_result.png" height="286" width="317" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;後は、このビルドフェーズに Resouces/lib/prettify の下にあるファイルをすべて追加するだけ(選択してドラッグ&amp;amp;ドロップ)。右がその結果だ。ビルドフェーズの名前もわかりやすいものに変更している。&lt;/p&gt;

&lt;p&gt;確認には、一度、クリーンしてからビルドを行い、Finder から build/Debug (または build/Release) の下にできているアプリバンドル(アプリ名.app)の内容を調べる。つまり、ポップアップメニューから「パッケージの内容を表示」でアプリバンドルを開き、Contents、Resources とたどり、そこに lib があること、またその中に prettify が、さらにその中には js ファイル(と 1 つの CSS ファイル)があることを確かめる。&lt;/p&gt;

&lt;h4&gt;おまけ&lt;/h4&gt;

&lt;p&gt;前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html"&gt;内部リンクの置き換えを実現する&lt;/a&gt;」)の HTML テンプレートと CSS ファイルの下りで、WebView にわたすベース URL について少し書いた。そこではアプリバンドルのトップの位置を(ファイル URL の形で)ベース URL に指定していた。このため、テンプレート中で CSS ファイルをリンクするために &lt;code&gt;href='Contents/Resources/default.css'&lt;/code&gt; と書く必要があった。しかし、前回の記事中にも書いたように、これはベース URL の位置が適切とは言えない。&lt;span class="path"&gt;Contents/Resources&lt;/span&gt; がベースになるべきだろう。&lt;/p&gt;

&lt;p&gt;NSBundle のリファレンスを探してみたが、都合良く &lt;span class="path"&gt;Contents/Resources&lt;/span&gt; をパスなり URL なりで取り出すメソッドは見つからなかった。そこで考えたのは、&lt;span class="path"&gt;Contents/Resources&lt;/span&gt; をベースと考えるのではく、&lt;span class="path"&gt;template.html&lt;/span&gt; の位置をベースとするというものだ。&lt;/p&gt;

&lt;div class="postscript"&gt;
&lt;h5&gt;追記@2010-12-12&lt;/h5&gt;
&lt;p&gt;上記の「都合良く &lt;span class="path"&gt;Contents/Resources&lt;/span&gt; をパスなり URL なりで取り出すメソッド」は存在していて(resourcePath と resourceURL)、見つけられなかったのは、単にわたしの目がフシアナだっただけ。別件で「Objective-C逆引きハンドブック」を調べていて見つけた。「NSBundle Class Reference」を見直したら、そこにもちゃんと書かれていた。&lt;/p&gt;

&lt;p&gt;このメソッドを使うなら、以下に書いた baseURL メソッドはこう書ける。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSURL *)baseURL {
    return [[NSBundle mainBundle] resourceURL];
}
&lt;/pre&gt;

&lt;p&gt;そうだよなあ。ないはずないよなあ。昨日のわたしはどんな目をしてたんだろう。(´・ω・｀)&lt;/p&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51TjCsZCcwL._SL160_.jpg" alt="Objective-C逆引きハンドブック" title="Objective-C逆引きハンドブック" width="113" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4863540515/mnbi-22/ref=nosim" target="_blank"&gt;Objective-C逆引きハンドブック&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;林 晃&lt;br /&gt;シーアンドアール研究所 ( 2010-02-26 )&lt;br /&gt;ISBN: 9784863540514&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4863540515" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;具体的には NSBundle のメソッドで &lt;span class="path"&gt;template.html&lt;/span&gt; の位置をファイル URL として取得し、パスのコンポーネントに分解。最後の要素(&lt;span class="path"&gt;template.html&lt;/span&gt; そのもの)を取り除いた上で、残った要素からファイル URL を組み立てる。これで &lt;span class="path"&gt;template.html&lt;/span&gt; が置かれたフォルダのファイル URL が出来上がる。&lt;/p&gt;

&lt;p&gt;該当する部分のコードを以下に示す(&lt;span class="path"&gt;AppController.m&lt;/span&gt; より)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSURL *)fileURLForHTMLTemplate {
    NSBundle *mainBundle = [NSBundle mainBundle];
    return [mainBundle URLForResource:BGHTMLTemplateName withExtension:@&amp;quot;html&amp;quot;];
}

- (NSURL *)baseURL {
    // the base URL must be identical to the path of the HTML template.
    NSMutableArray *components = 
    [NSMutableArray
     arrayWithArray:[[self fileURLForHTMLTemplate] pathComponents]];

    [components removeLastObject]; // remove the template name

    return [NSURL fileURLWithPathComponents:components];
}
@end
&lt;/pre&gt;

&lt;p&gt;ただし、この方法では、&lt;span class="path"&gt;template.html&lt;/span&gt; がローカライズ対象となった場合に他のリソース(CSS や JavaScript)もローカライズ対象にしなければならない。CSS ならフォントの指定あたりで一部ローカライズすることもあるだろうが、さすがに JavaScript をローカライズすることはないと思う。ローカライズ対象に指定したところで English.lproj とか Japanese.lproj にコピーされるだけのことだが、少しムダかなと言う気にはなる。&lt;/p&gt;

&lt;p&gt;今回は &lt;span class="path"&gt;template.html&lt;/span&gt; をベース URL のための目印に使ったが、ローカライズが絡む場合は別の目印を使うか、ビルドフェーズでアプリバンドル内にシンボリックリンクを張るなどの工夫が必要になるかもしれない。&lt;/p&gt;

&lt;h4 class="clear"&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/google-code-prettify/"&gt;google-code-prettify&lt;/a&gt; (Google Code)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/google-code-prettify.html"&gt;ソースコードの色付け - google-code-prettify を使う&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bloggerglass.appspot.com/post/?id=6401442706191922000"&gt;Xcode のファイル新規作成で作者名になるのは、どれ？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/pragma-mark-xcode-1.html"&gt;#pragma mark - Xcode のテキストエディタの使い方 #1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html"&gt;内部リンクの置き換えを実現する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2311148601586594935?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2311148601586594935/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-javascript-xcode.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2311148601586594935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2311148601586594935'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-javascript-xcode.html' title='Cocoa アプリに JavaScript のライブラリを組み込む - Xcode の使い方'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_wobSevGXOh4/TQM25qMG60I/AAAAAAAABos/NG-hYFHbYSQ/s72-c/mbg_main_window_proto_0_with_prettify.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3956040456614744006</id><published>2010-12-10T23:06:00.002+09:00</published><updated>2010-12-10T23:06:41.267+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>内部リンクの置き換えを実現する (MacBloggerGlass)</title><content type='html'>&lt;p&gt;MacBloggerGlass を作り始めたとき(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0&lt;/a&gt;」)、機能実現のステップとして以下の 5 つを挙げた。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;一覧表示&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/gdata-objective-c-client-library.html"&gt;Google Data API Objective-C Client Library の組み込み&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;記事表示&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;ラベル検索&lt;/li&gt;
  &lt;li&gt;内部リンクの置き換え&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;多少、順序は入れ替わっているが、これまでの作業で 1 〜 3 が大筋で実現できている。今回は、内部リンクの置き換えを実現する。&lt;a href="http://logrepo.blogspot.com/2010/11/1-blogger-glass.html"&gt;GAE 版のときにも書いた&lt;/a&gt;が、これができるようになると、アプリが実用的になる。以下が現在のプロトタイプ #0 のスクリーンショットだ。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;MacBloggerGlass プロトタイプ #0&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;アプリウィンドウ&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/RP9K4OVZazMp5ysm4ArSGw?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TQIYKlo9iVI/AAAAAAAABmw/aAYP1cNMcCU/s400/mbg_prototype_0.png" height="312" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;ウィンドウの上部に記事一覧、下部に一覧で選択した記事の内容をそれぞれ表示できるようになった。JavaScript を組み込んでいないものの、その他の点では Blogger Glass と同じスタイルで表示している。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;Apple イベントの処理の作り込み&lt;/h4&gt;

&lt;p&gt;前回のサンプルアプリ(→ 「&lt;a href="http://logrepo.blogspot.com/2010/12/apple-2.html"&gt;アプリに Apple イベントの処理を作り込む&lt;/a&gt;」)と同等の作り込みを行った。&lt;/p&gt;

&lt;h5&gt;独自スキームの定義&lt;/h5&gt;

&lt;p&gt;以下の内容を &lt;span class="path"&gt;MacBloggerGlass-Info.plist&lt;/span&gt; に追加。太字の値はこちらで定義、他は Xcode のエディタではリストから選ぶだけで良い。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;URL types
    &lt;ul&gt;
      &lt;li&gt;Item 0
 &lt;ul&gt;
   &lt;li&gt;URL identifier → &lt;b&gt;MacBloggerGlass Internal Link&lt;/b&gt;&lt;/li&gt;
   &lt;li&gt;Document Role → Viewer&lt;/li&gt;
   &lt;li&gt;URL Schemes
     &lt;ul&gt;
       &lt;li&gt;Item 0 → &lt;b&gt;bgls&lt;/b&gt;&lt;/li&gt;
     &lt;/ul&gt;
   &lt;/li&gt;
 &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;独自スキームを使った URL は以下の形とした。記事の識別に必要な情報だけを詰め込んでいる。&lt;/p&gt;

&lt;pre&gt;
bgls://&amp;lt;blog-id&amp;gt/&amp;lt;post-id&amp;gt;
&lt;/pre&gt;

&lt;h5&gt;Apple イベントハンドラの定義&lt;/h5&gt;

&lt;p&gt;追加するのは Get URL イベントのハンドラのみ。前回のサンプルアプリと同様、&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;公式ドキュメント&lt;/a&gt;で推奨されているようにアプリのデリゲートクラス (MacBloggerGlassAppDelegate) に追加している。&lt;/p&gt;

&lt;p&gt;追加後の MacBloggerGlassAppDelegate.m を以下に示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
#import &amp;quot;AppController.h&amp;quot;
#import &amp;quot;MacBloggerGlassAppDelegate.h&amp;quot;

@implementation MacBloggerGlassAppDelegate

@synthesize window, appController;

- (void)applicationWillFinishLaunching:(NSNotification *)notification {
    NSAppleEventManager *appleEventManager =
    [NSAppleEventManager sharedAppleEventManager];
    [appleEventManager setEventHandler:self
                           andSelector:@selector(handleGetURLEvent:withReplyEvent:)
                         forEventClass:kInternetEventClass
                            andEventID:kAEGetURL];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
 // Insert code here to initialize your application 
}

#pragma mark -
#pragma mark apple event handler
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
           withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
    NSAppleEventDescriptor *directObjectDescpritor =
    [event paramDescriptorForKeyword:keyDirectObject];
    if (! directObjectDescpritor) return;

    NSString *urlString = [directObjectDescpritor stringValue];
    if (! urlString) return;

    NSLog(@&amp;quot;APPLE EVET(GetURL): %@&amp;quot;, urlString);

    // custom URL must be in the form like:
    //     bgls://&amp;lt;blog-id&amp;gt;/&amp;lt;post-id&amp;gt;
    NSURL *objectURL = [NSURL URLWithString:urlString];
    NSString *blogID = [objectURL host];
    // path will return a string with the form '/&amp;lt;post-id&amp;gt;'.
    NSString *postID = [[objectURL path] substringFromIndex:1];

    [appController openPost:postID inBlog:blogID];
}
@end
&lt;/pre&gt;

&lt;p&gt;AppController.h を import しているのは、イベントハンドラの最後で、AppContoller のメソッドを呼び出しているから。アプリデリゲートでは、イベントから URL を取り出し、Blog ID と Post ID を切り出すところまでを行い、その後の処理はアプリコントローラに任せている。&lt;/p&gt;

&lt;p&gt;また、アプリデリゲートとアプリコントローラの結びつけは、デリゲート側にコントローラ用のアウトレットを設け、Interface Builder 上で接続している。どちらのオブジェクトも MainMenu.xib 内にインスタンス化されているので、これが一番手軽な方法だと思う。&lt;/p&gt;

&lt;h5&gt;メインコントローラ (AppController) への作り込み&lt;/h5&gt;

&lt;p&gt;コントローラ側での処理を以下に示す。ここでは、カスタム URL にふくまれている Blog ID を現在表示中のものと比較している。他のブログ(自分で作ったもの)の扱いについては、ひとまず無視することにしている。どう扱うと実用的かは将来の検討課題だ。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
#pragma mark -
- (void)openPost:(NSString *)aPostID inBlog:(NSString *)aBlogID
{
    NSLog(@&amp;quot;Open post request: %@ (%@)&amp;quot;, aPostID, aBlogID);
    if (! [aBlogID isEqualToString:blogID]) {
        NSLog(@&amp;quot;The blog requested is not currently displayed. (%@)&amp;quot;, aBlogID);
        return;
    }
    NSUInteger index = [feed findPost:aPostID];
    if (index == NSNotFound) return;

    [postTable selectRowIndexes:[NSIndexSet indexSetWithIndex:index]
           byExtendingSelection:NO];

    [self showPostWithIndex:index];
}
&lt;/pre&gt;

&lt;p&gt;指定した Posd ID に対応する記事オブジェクト(EntryBlogPost)を探すメソッドを一覧オブジェクト(FeedBlogPost)に実装している。検索は単純な線形探索。これで表示が遅くなるほどの記事数にはならないと思っている。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSUInteger)findPost:(NSString *)aPostID {
    EntryBlogPost *entry;
    for (NSUInteger i = 0; i &amp;lt; [entries count]; i++) {
        entry = [entries objectAtIndex:i];
        if ([entry.postID isEqualToString:aPostID]) {
            return i;
        }
    }
    return NSNotFound;
}
&lt;/pre&gt;

&lt;h4&gt;内部リンクの置き換え&lt;/h4&gt;

&lt;p&gt;内部リンクの置き換えのために、GAE 版でも作ったように(→「&lt;a href="http://logrepo.blogspot.com/2010/11/2-blogger-glass.html"&gt;内部リンクを置き換える #2&lt;/a&gt;」)、permalink と Post ID のひもづけを保持する仕組みが必要になる。&lt;/p&gt;

&lt;h5&gt;PairOfLinks は辞書で実現&lt;/h5&gt;

&lt;p&gt;蓄積と探索の処理を単純にするため、Cocoa 標準の辞書オブジェクト(NSMutableDictionary)を使った。ひもづけを保持するのは FeedBlogPost オブジェクトにした。これはこのオブジェクトが記事オブジェクト(EntryBlogPost)の配列を保持しているから、データを抽出するのに相応しいと考えたからだ。以下に FeedBlogPost.m 内の抽出処理を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)extractPairOfLinks {
    pairOfLinks =
    [[NSMutableDictionary alloc] initWithCapacity:[entries count]];
    EntryBlogPost *post;
    for (post in entries) {
        [pairOfLinks setObject:post.postID forKey:post.permalink];
    }
}
&lt;/pre&gt;

&lt;h5&gt;置換処理の実際&lt;/h5&gt;

&lt;p&gt;記事データ中のリンク文字列の置換は、WebView に HTML データをわたす直前に行っている。href=&amp;quot;&amp;lt;permalink&amp;gt;&amp;quot; と href='&amp;lt;permalink&amp;gt;' をそれぞれ置換するという手抜きコーディングにしてある。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)renderHTML:(NSString *)content
             title:(NSString *)title
       withBaseURL:(NSURL *)baseURL {
    // replace internal links into those have custom URL.
    NSString *replacedContent = content;
    NSString *permalink;
    for (permalink in [feed.pairOfLinks allKeys]) {
        NSString *aPostID = [feed.pairOfLinks objectForKey:permalink];
        NSString *customLink =
        [NSString stringWithFormat:@&amp;quot;href='bgls://%@/%@'&amp;quot;, blogID, aPostID];

        // replace href attribute values wrapped with double quotation
        NSString *targetLink = [NSString stringWithFormat:@&amp;quot;href=\&amp;quot;%@\&amp;quot;&amp;quot;, permalink];
        replacedContent =
        [replacedContent stringByReplacingOccurrencesOfString:targetLink
                                                   withString:customLink];

        // replace href attribute values wrapped with single quotation
        targetLink = [NSString stringWithFormat:@&amp;quot;href='%@'&amp;quot;, permalink];
        replacedContent =
        [replacedContent stringByReplacingOccurrencesOfString:targetLink
                                                   withString:customLink];
    }
    
    NSString *htmlData =
    [NSString stringWithFormat:templateData, title, replacedContent];

    NSLog(@&amp;quot;Base URL: %@&amp;quot;, baseURL);
    [[postView mainFrame] loadHTMLString:htmlData baseURL:baseURL];
}
&lt;/pre&gt;

&lt;p&gt;このメソッドはアプリウィンドウで記事の一覧が選択されたときに呼ばれる。その際、&lt;code&gt;baseURL&lt;/code&gt; としてアプリバンドル(&lt;span class="path"&gt;MacBloggerGlass.app&lt;/span&gt;)のトップが URL としてわたってくる。その部分を以下に示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)showPostWithIndex:(NSUInteger)index {
    EntryBlogPost *entry = [feed objectInEntriesAtIndex:index];
    NSLog(@&amp;quot;Post ID: %@&amp;quot, entry.postID);
    [self renderHTML:[entry content]
               title:[entry title]
         withBaseURL:[[NSBundle mainBundle] bundleURL]];
}&lt;/pre&gt;

&lt;h4&gt;表示機能の作り込み&lt;/h4&gt;

&lt;p&gt;最後に、記事表示機能の作り込みとして、記事データを流し込む HTML のテンプレートと CSS ファイルの組み込みについて説明する。&lt;/p&gt;

&lt;h5&gt;HTML のテンプレートと CSS ファイル&lt;/h5&gt;

&lt;p&gt;テンプレートも CSS ファイルも、アプリバンドル (MacBloggerGlass.app) の中に置くことにした。&lt;/p&gt;

&lt;p&gt;以下はテンプレートをロードする処理で、アプリコントローラ (AppController) で定義している。NSBundle を使ってテンプレートファイルのパスを特定し、NSString に読み込んでいる。&lt;code&gt;templateData&lt;/code&gt; という変数は AppContoller のメンバ変数だ。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)loadHTMLTemplate:(NSString *)templateName {
    NSBundle *mainBundle = [NSBundle mainBundle];
    NSString *pathForTempalte =
    [mainBundle pathForResource:templateName ofType:@&amp;quot;html&amp;quot;];

    templateData = [[NSString alloc] initWithContentsOfFile:pathForTempalte
                                                   encoding:NSUTF8StringEncoding
                                                      error:NULL];
}
&lt;/pre&gt;

&lt;p&gt;読み込まれるテンプレートは以下のもの。注意すべきは 6 行目のスタイルシートへのリンクだ。このテンプレートと CSS ファイルはアプリのリソースの一部として、アプリバンドル(&lt;span class="path"&gt;MacBloggerGlass.app&lt;/span&gt;)の中にコピーされる。先に示したように、WebView にわたすベース URL としてアプリバンドルのトップを指定している。このため、CSS ファイルの相対パスは 6 行目のようになるわけだ。ただ、これはベース URL の指定を変えて、&lt;span class="path"&gt;Contents/Resources&lt;/span&gt; がベースになるようにすべきかもしれない。後で、NSBundle のリファレンスをもう少し調べてみよう。&lt;/p&gt;

&lt;pre class="prettyprint lang-html linenums"&gt;
&amp;lt;!DOCTYPE HTML&amp;gt;
&amp;lt;html lang='ja'&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta content='text/html; charset=UTF-8' http-equiv='Content-Type'&amp;gt;
&amp;lt;title&amp;gt;MacBloggerGlass&amp;lt;/title&amp;gt;
&amp;lt;link type='text/css' rel='stylesheet' href='Contents/Resources/default.css'&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;article&amp;gt;

&amp;lt;div id='view-header'&amp;gt;
  &amp;lt;h3&amp;gt;%@&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div id='content'&amp;gt;
%@
&amp;lt;/div&amp;gt;

&amp;lt;/article&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;CSS は Blogger Glass で使っているものを手直しして流用。手直しは、テンプレートの構造の変更に対応するためのもの。冒頭のスクリーンショットは、現在の MacBloggerGlass のものだが、GAE 版の Blogger Glass と同じスタイルが使われていることが見てとれる。&lt;/p&gt;

&lt;h4&gt;次の一手&lt;/h4&gt;

&lt;p&gt;記事表示の機能については、まだ細部の詰めが残っている。たとえば、&lt;a href="http://logrepo.blogspot.com/2010/11/google-code-prettify.html"&gt;ソースコード表示用の JavaScript&lt;/a&gt; を組み込むことなど。&lt;/p&gt;

&lt;p&gt;記事表示が GAE 版と同等になったら、その次はラベル検索に取り組むか。機能的には  GAE 版と同じく GData を使って検索するもので良いが、問題はユーザ体験だ。まずは、そこからになるな。&lt;/p&gt;

&lt;p&gt;いや、それより先に「オフライン機能」すなわちデータの保存について考えるべきだろう。そもそもそのための Cocoa 版なのだから。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/apple-2.html"&gt;アプリに Apple イベントの処理を作り込む - アプリを開くリンク #2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;記事一覧を取得する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/gdata-objective-c-client-library.html"&gt;GData Objective-C Client Library を組み込む (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;設定変更を通知する - 環境設定パネルを作る #4 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/1-blogger-glass.html"&gt;内部リンクを置き換える #1 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/2-blogger-glass.html"&gt;内部リンクを置き換える #2 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/google-code-prettify.html"&gt;ソースコードの色付け - google-code-prettify を使う&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3956040456614744006?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3956040456614744006/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3956040456614744006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3956040456614744006'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass_10.html' title='内部リンクの置き換えを実現する (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TQIYKlo9iVI/AAAAAAAABmw/aAYP1cNMcCU/s72-c/mbg_prototype_0.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-937729358271313624</id><published>2010-12-10T07:34:00.002+09:00</published><updated>2010-12-10T12:42:38.257+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><title type='text'>twitter より (2010-12-09)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/12791247471116288"&gt;17:50&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;荻原本がアップデートするらしい。→ &lt;a href="http://www.sbcr.jp/products/4797361780.html"&gt;http://www.sbcr.jp/products/4797361780.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-937729358271313624?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/937729358271313624/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-09.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/937729358271313624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/937729358271313624'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-09.html' title='twitter より (2010-12-09)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3261588928659971564</id><published>2010-12-09T20:41:00.000+09:00</published><updated>2010-12-09T20:41:02.195+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>アプリに Apple イベントの処理を作り込む - アプリを開くリンク #2</title><content type='html'>&lt;p&gt;前回(→「&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass.html"&gt;アプリを開くリンク #1&lt;/a&gt;」)に引き続き、Launch Services が送ってくる Apple イベントを処理するための作り込みについて調べた。簡単なサンプルアプリを作って動作を確認した。&lt;/p&gt;

&lt;p&gt;以下では、WebView でアプリの独自スキームを持った URL へのリンクをクリックしたときに、アプリに送られてくる get URL という Apple イベントを処理するための作り込みの手順を説明する。&lt;/p&gt;

&lt;p&gt;今回の記事のネタは主に「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt;」から拾っている。&lt;/p&gt;

&lt;h4&gt;Cocoa アプリにおける Apple イベントの処理&lt;/h4&gt;

&lt;p&gt;Automator.app を使ってアプリを使った作業の自動化(→「&lt;a href="http://logrepo.blogspot.com/2010/08/applescript-url-safari.html"&gt;AppleScript を使って URL を Safari のタブで開く&lt;/a&gt;」)をしようとしてみると、アプリの中には Apple Script でさまざまな制御が可能なものと、そうでないものがあることに気付く。Safari などは前者で、ウィンドウのサイズを変えたり、指定した URL を開かせたりといったことが Apple Script から制御できる。一方、&lt;a href="http://logrepo.blogspot.com/2010/08/cocoa-emacs.html"&gt;Cocoa Emacs&lt;/a&gt; のように限られた制御(アプリの起動等)しかできないアプリもある。前者のように様々な制御を受けるアプリのことを「スクリプト制御可能(scriptable)」だと言う。&lt;/p&gt;

&lt;p&gt;アプリがスクリプト制御可能なように作られているかどうかに関係なく、Cocoa は Mac OS X が GUI アプリに送る必須イベントに対するデフォルトの処理(ハンドラ)を提供している。一番、わかりやすい例はアプリを起動する open application イベントだろう。このデフォルトの実装があるおかげで「Apple Script やイベントのことなんて知らない」と言うアプリでも、Launch Services から起動することができる。また、デフォルトハンドラはデリゲートの仕組みを使っており、NSApplication のデリゲート(NSApplicationDelegate プロトコルを実装したクラス)でデリゲートメソッドを定義してやればハンドラの動作をカスタマイズすることができる(イベントとデリゲートメソッドの組み合わせについては「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt;」を参照のこと)。&lt;/p&gt;

&lt;p&gt;ただ、Cocoa が提供するデフォルトのイベントハンドラは限られていて、具体的には「アプリの起動」「アプリの再起動(アクティベート)」「ドキュメントを開く」「印刷」「コンテンツを開く」、そして「アプリの終了」だ。残念ながら URL を開くイベント(get URL)に対するハンドラはデフォルトの実装には含まれていない。これについては、アプリが独自ハンドラとして追加しなければならない。&lt;/p&gt;

&lt;h4&gt;Apple イベントハンドラの追加&lt;/h4&gt;

&lt;p&gt;イベントハンドラの追加はそれほど難しいことではない。特定の形のメソッドを定義し、それをハンドラとしてイベントマネージャ(NSAppleEventManager)に登録するだけで良い。&lt;/p&gt;

&lt;p&gt;ハンドラは以下の形にする。ただ、handleAppleEvent の部分はイベントに応じて名前を変えても良いようだ。たとえば、後述するサンプルアプリで定義したハンドラではここは handleGetURLEvent となっている(これは「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt;」の Installing a Get URL Handler で例として使われている名前でもある)。一方で、Mac OS X Reference Library のサンプルコードでは同じイベントのハンドラに対して handleOpenLocationAppleEvent という名前が付いている。引数の数と型が一致していれば良いのかも。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event
          withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
&lt;/pre&gt;

&lt;p&gt;「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt;」には具体的なハンドラの定義は載っていなかったため、Mac OS X Reference Library を検索したところ、サンプルコードに get URL イベントのハンドラの例を見つけた(→ &lt;a href="http://developer.apple.com/library/mac/#samplecode/CoreRecipes/Listings/Sample_Applications_CoreRecipesApp_MainApplication_Sources_Application_AppDelegate_m.html%23//apple_ref/doc/uid/DTS10003664-Sample_Applications_CoreRecipesApp_MainApplication_Sources_Application_AppDelegate_m-DontLinkElementID_22"&gt;CoreRecipesApp/AppDelegate.m&lt;/a&gt;)。次に示すサンプルアプリのハンドラの定義は、ここから流用したものだ。&lt;/p&gt;

&lt;h4&gt;サンプルアプリ&lt;/h4&gt;

&lt;h5&gt;独自スキームの定義&lt;/h5&gt;

&lt;p&gt;以下は前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass.html"&gt;アプリを開くリンク #1&lt;/a&gt;」)に載せたスクリーンショットだが、再掲しておく。このサンプルアプリが受け付ける独自 URI スキームに sample を設定している。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Info.plist に URL タイプを追加する様子&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/7cV7NOXLK6RMFlIB8fzH3w?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TP9gkrCL02I/AAAAAAAABls/wqpKtSK-lRo/s400/xcode_info_plist_for_url_types.png" height="79" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="description"&gt;URI スキームとして sample という文字列を指定している。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h5&gt;Get URL ハンドラの定義&lt;/h5&gt;

&lt;p&gt;ハンドラの定義とその登録は、ドキュメントの推奨どおりにアプリケーションデリゲイトで行っている。以下はその部分の抜粋だ。applicationWillFinishLaunching: で、Get URL ハンドラ handleGetURLEvent:withReplyEvent を登録している。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
    NSAppleEventManager *appleEventManager = 
    [NSAppleEventManager sharedAppleEventManager];
    [appleEventManager setEventHandler:self 
                           andSelector:@selector(handleGetURLEvent:withReplyEvent:)
                         forEventClass:kInternetEventClass
                            andEventID:kAEGetURL];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
 // Insert code here to initialize your application 
}

// Apple event handler
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
           withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
    NSAppleEventDescriptor *directObjectDescriptor =
    [event paramDescriptorForKeyword:keyDirectObject];

    if (directObjectDescriptor) {
        NSString *urlString = [directObjectDescriptor stringValue];
        if (urlString) {
            NSURL *objectURL = [[NSURL alloc] initWithString:urlString];
            if (objectURL) {
                NSString *host = [objectURL host];
                NSString *path = [objectURL path];
                NSString *description =
                [NSString stringWithFormat:@&amp;quot;%@ in (%@)&amp;quot;, path, host];
                [appController updateContent:description];
            }
        }
    }
}
&lt;/pre&gt;

&lt;p&gt;このハンドラは受け取ったイベントの内容から URL を取り出し、さらにそこからホスト名とパスを抽出して、WebView を抱える AppController に渡している。&lt;/p&gt;

&lt;p&gt;AppController では、イベントハンドラから受け取った文字列を WebView に渡す HTML に埋め込み、再表示させている。以下にその実装部(AppController.m)を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
#import &amp;lt;WebKit/WebKit.h&amp;gt;

#import &amp;quot;LaunchAppAppDelegate.h&amp;quot;
#import &amp;quot;AppController.h&amp;quot;

NSString * const HTMLTemplate = 
@&amp;quot;&amp;lt;html&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;body&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;h1&amp;gt;%@&amp;lt;/h1&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;ul&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;li&amp;gt;Requested: %@&amp;lt;/li&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;/ul&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;p&amp;gt;&amp;lt;a href='sample://1234567890/0987654321'&amp;gt;Click me!&amp;lt;/a&amp;gt;&amp;quot;
@&amp;quot;(sample://1234567890/0987654321)&amp;lt;/p&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;/body&amp;gt;\n&amp;quot;
@&amp;quot;&amp;lt;/html&amp;gt;\n&amp;quot;;

@interface AppController (PrivateMethods)
- (void)renderHTML:(NSString *)html;
@end


@implementation AppController
- (void)awakeFromNib
{
    appDelegate.appController = self;
    [self renderHTML:[NSString
                      stringWithFormat:HTMLTemplate, @&amp;quot;Sample App&amp;quot;, @&amp;quot;&amp;quot;]];
}

- (void)updateContent:(NSString *)request
{
    [self renderHTML:[NSString
                      stringWithFormat:HTMLTemplate, @&amp;quot;Sample App&amp;quot;, request]];
}

@end

@implementation AppController (PrivateMethods)
- (void)renderHTML:(NSString *)html
{
    [[display mainFrame] loadHTMLString:html
                                baseURL:[NSURL URLWithString:@&amp;quot;http://localhost&amp;quot;]];
}
@end
&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;HTMLTemplate&lt;/code&gt; の定義では見易くするために文字列オブジェクト定数を並べて置いている。これらはコンパイル時に連結され 1 つの文字列オブジェクト定数となる。&lt;/p&gt;

&lt;h5&gt;動作確認&lt;/h5&gt;

&lt;p&gt;クリックする前と後のスクリーンショットを示す。これにより、アプリの抱える WebView からそのアプリの独自 URL へのリンクをクリックすることで、実行中のアプリ自身にイベントを飛ばせることが確認できた。MacBloggerGlass で内部リンクの置き換えを実現できる目処が立ったことになる。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;WebView で独自 URL をクリックした際の動作確認&lt;/caption&gt;
&lt;tr&gt;
&lt;th&gt;クリック前&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/_tXeDG5kjypVb1K8uv8psw?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TQCsN-wBrjI/AAAAAAAABmI/IppBJSOj-ZE/s400/app_custom_link_sample_before.png" height="318" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="description"&gt;「Requested」の横には何も表示されていない。Click me! をクリックすると……&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;クリック後&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/SYM9hNXfMDCT_OI5rjSp6Q?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TQCsOfhJGZI/AAAAAAAABmM/ns2i1_nHfng/s400/app_custom_link_sample_after.png" height="318" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="description"&gt;「Requested」の横に URL にふくまれていた情報が抜き出されてきた。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Applications Handle Apple Events&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#samplecode/CoreRecipes/Listings/Sample_Applications_CoreRecipesApp_MainApplication_Sources_Application_AppDelegate_m.html%23//apple_ref/doc/uid/DTS10003664-Sample_Applications_CoreRecipesApp_MainApplication_Sources_Application_AppDelegate_m-DontLinkElementID_22"&gt;Sample Applications/CoreRecipesApp/MainApplication/Sources/Application/AppDelegate.m&lt;/a&gt; (Mac OS X Reference Library; サンプルアプリのソース。handleOpenLocationAppleEvent:withReplyEvent: が GetURL を処理するハンドラ)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/1-macbloggerglass.html"&gt;アプリを開くリンク #1 - 内部リンクの置き換えのために (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/applescript-url-safari.html"&gt;AppleScript を使って URL を Safari のタブで開く&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/08/cocoa-emacs.html"&gt;Cocoa Emacs を使ってみたい&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3261588928659971564?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3261588928659971564/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/apple-2.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3261588928659971564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3261588928659971564'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/apple-2.html' title='アプリに Apple イベントの処理を作り込む - アプリを開くリンク #2'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_wobSevGXOh4/TP9gkrCL02I/AAAAAAAABls/wqpKtSK-lRo/s72-c/xcode_info_plist_for_url_types.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6500273066746189256</id><published>2010-12-09T07:33:00.002+09:00</published><updated>2010-12-09T13:44:35.765+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><title type='text'>twitter より (2010-12-08)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/12308338989596673"&gt;09:51&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;今の iPad は少々大きいと思う。Retina ディスプレイがあれば同じ画素数でももっと小さくできるよね。B6ぐらいのサイズで作ってくれないかな。 → &lt;a href="http://bit.ly/hejP3z"&gt;http://bit.ly/hejP3z&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/12313120873652224"&gt;10:10&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;RT @&lt;a href="http://twitter.com/yukihiro_matz"&gt;yukihiro_matz&lt;/a&gt;: 字下げ依存構文の解析 - 再帰の反復: &lt;a href="http://bit.ly/hzJwtX"&gt;http://bit.ly/hzJwtX&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6500273066746189256?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6500273066746189256/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-08.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6500273066746189256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6500273066746189256'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-08.html' title='twitter より (2010-12-08)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5437486290960336617</id><published>2010-12-08T20:10:00.000+09:00</published><updated>2010-12-08T20:10:40.299+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>アプリを開くリンク #1 - 内部リンクの置き換えのために (MacBloggerGlass)</title><content type='html'>&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/kPXce92YScHiWI1Pc9g-3w?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TP9N_RpogxI/AAAAAAAABlU/zEic5O7G27E/s288/googlechrome_external_app_confirmation_dialog.png" height="219" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;以前の記事で、内部リンクの置き換えを実現するためには記事内容をファイルとしてローカルのストレージに書き出すしかないかも、と書いた(→「&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass.html"&gt;記事の表示機能を作る (MacBloggerGlass)&lt;/a&gt;」)。ここ数日、Cocoa Bindings の使い方をあれこれ調べる間もそのことについて考え続けてきた。今日になって、「あれ、そう言えば iTunes Store のリンクって Safari から iTunes に飛ぶよな」と気付いた。&lt;/p&gt;

&lt;p&gt;たとえば、以下のリンクは「itms://」で始まる URL で、Safari でクリックすれば iTunes.app で Coldplay の新曲(2010-12-08 時点)のページが開く(Mac と iPad で確認)。&lt;/p&gt;

&lt;pre&gt;&lt;a href="itms://itunes.apple.com/jp/album/christmas-lights-single/id406970808"&gt;itms://itunes.apple.com/jp/album/christmas-lights-single/id406970808&lt;/a&gt;&lt;/pre&gt;

&lt;p&gt;ちなみに、Safari 以外のブラウザだと、iTunes.app を開く前に確認のダイアログが現れる。右のスクリーンショットは Google Chrome でこのリンクをクリックしたときに出てくるものだ。&lt;/p&gt;

&lt;p&gt;Safari (つまり WebView) でリンクをクリックしてアプリに通知させることができるなら、内部リンクをそれで置き換えれば良い。そうすれば MacBloggerGlass (以下、MBG) から、MBG 自身に開きたい記事の情報を伝えることができる。フィードデータから記事ごとに HTML ファイルを作って書き出して、file:/// でアクセスするよりもスマートだ。&lt;/p&gt;

&lt;p&gt;もっとも、MBG を作る動機にもなっている「オフラインでも読める」ようにするためには、記事の内容をふくめたフィードデータをローカルに保存しなくてはならないんだけど。&lt;/p&gt;

&lt;p&gt;ともあれ、これで方向性が定まった。まずは「アプリを開くリンク」について調べるところからだ。&lt;/p&gt;

&lt;h4&gt;itms:// を開こうとしたとき、誰が iTunes を起動するのか？&lt;/h4&gt;

&lt;p&gt;では、特定の &lt;a href="http://en.wikipedia.org/wiki/URI_scheme"&gt;URI スキーム&lt;/a&gt; (http://... の http の部分) とそれを扱うアプリの組み合わせを知っているのは誰だろう？ Safari のようなブラウザ自身だろうか？ 少なくとも Mac OS X では(おそらく iOS でも) 専用のサービス (API) が用意されている。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://developer.apple.com/library/mac/#documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html%23//apple_ref/doc/uid/TP30000999"&gt;Launch Services Programming Guide: Introduction&lt;/a&gt;」より)&lt;br&gt;
Launch Services is an API that enables a running application to open other applications or their document files or URLs (uniform resource locators) in a way similar to the Finder or the Dock. Using Launch Services, an application can perform such tasks as:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Open (launch or activate) another application&lt;/li&gt;
  &lt;li&gt;Open a document or a URL in another application&lt;/li&gt;
  &lt;li&gt;Identify the preferred application for opening a given document or URL&lt;/li&gt;
  &lt;li&gt;&lt;span class="snip"&gt;[...snip...]&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;この Launch Services がドキュメントのタイプや URL タイプとアプリの組み合わせを保持している。では、Launch Services はその組み合わせの情報をどこから得るのか？ それはアプリが持っている &lt;span class="path"&gt;Info.plist&lt;/span&gt; の記述からだ。&lt;/p&gt;

&lt;p&gt;具体的には、&lt;span class="path"&gt;Info.plist&lt;/span&gt; 中の CFBundleURLTypes にアプリが開くことのできる URL タイプが記述されている。&lt;/p&gt;

&lt;h4&gt;Property List の書き方&lt;/h4&gt;

&lt;p&gt;&lt;span class="path"&gt;Info.plist&lt;/span&gt; は Property List と呼ばれる XML 形式で記述する。以下に Xcode が作るデフォルトの Info.plist を示す。&lt;/p&gt;

&lt;pre class="prettyprint lang-xml"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;
&amp;lt;plist version=&amp;quot;1.0&amp;quot;&amp;gt;
&amp;lt;dict&amp;gt;
 &amp;lt;key&amp;gt;CFBundleDevelopmentRegion&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;English&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleExecutable&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;${EXECUTABLE_NAME}&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleIconFile&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleIdentifier&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;com.yourcompany.${PRODUCT_NAME:rfc1034identifier}&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleInfoDictionaryVersion&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;6.0&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleName&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;${PRODUCT_NAME}&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundlePackageType&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;APPL&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleShortVersionString&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;1.0&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleSignature&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;????&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;CFBundleVersion&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;1&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;LSMinimumSystemVersion&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;${MACOSX_DEPLOYMENT_TARGET}&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;NSMainNibFile&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;MainMenu&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;NSPrincipalClass&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;NSApplication&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/pre&gt;

&lt;p&gt;URL タイプは、トップレベルにある dict 要素の子として記述する。つまり、CFBundleExecutable などと同じレベルの要素にする。その際、CFBundleURLTypes が dict の array になるように構成する。そして、各 dict の中身には以下の 4 つの要素を置く。&lt;/p&gt;

&lt;table&gt;
&lt;caption&gt;CFBundleURLType 内の要素 (「&lt;a href="http://developer.apple.com/library/mac/#documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html%23//apple_ref/doc/uid/TP30000999"&gt;Launch Services Programming Guide&lt;/a&gt;」より)&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
  &lt;th&gt;Key&lt;/th&gt;
  &lt;th&gt;Type&lt;/th&gt;
  &lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td&gt;CFBundleTypeRole&lt;/td&gt;
  &lt;td&gt;string&lt;/td&gt;
  &lt;td&gt;アプリがドキュメントをどのように扱うものなのか。Launch Services が認識するのは Editor、Viewer、そして None の 3 つのみ。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;CFBundleURLName&lt;/td&gt;
  &lt;td&gt;string&lt;/td&gt;
  &lt;td&gt;URL タイプの名前&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;CFBundleURLIconFile&lt;/td&gt;
  &lt;td&gt;string&lt;/td&gt;
  &lt;td&gt;このタイプの URL を表示する際に使われるアイコンファイルの名前&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;CFBundleURLSchemes&lt;/td&gt;
  &lt;td&gt;array&lt;/td&gt;
  &lt;td&gt;このタイプにふくまれる URL のスキームの配列。各スキームは string として記述する。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;典型的な CFBundleURLTypes の構造は以下のようになる。&lt;/p&gt;

&lt;pre&gt;
CFBundleURLTypes
+-- array
    +-- dict
    |   +-- CFBundleTypeRole
    |   |   +-- string
    |   +-- CFBundleURLName
    |   |   +-- string
    |   +-- CFBundleURLIconFile
    |   |   +-- string
    |   +-- CFBundleURLSchemes
    |       +-- array
    |           +-- string
    |           +-- string
    |           ...
    +-- dict
        ...
&lt;/pre&gt;

&lt;p&gt;iTunes.app の Info.plist を覗いてみると、CFBundleURLIconFile は省略しても構わないようだ。&lt;/p&gt;

&lt;p&gt;以下のスクリーンショットは、実際に Xcode 中で Info.plist を編集し、URL タイプを 1 つ、追加したところのものだ。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Info.plist に URL タイプを追加する様子&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/7cV7NOXLK6RMFlIB8fzH3w?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TP9gkrCL02I/AAAAAAAABls/wqpKtSK-lRo/s400/xcode_info_plist_for_url_types.png" height="79" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="description"&gt;左に表示されている要素の種類はドロップダウンリストの中から選ぶことができる。トップレベルで URL Types を選ぶとその子要素の構造も同時に作られる(デフォルトでは URL Identifier だけが追加されている)。この例では URI スキームとして sample という文字列を指定している。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;アプリ側での作り込み&lt;/h4&gt;

&lt;p&gt;Launch Services はアプリに対してアプリの起動や URL を開く等の Apple イベントを送ってくる。つまり、アプリは送られてきたイベントを適切に処理することができなければならない。&lt;/p&gt;

&lt;p&gt;そのためにはどういうコードを書けば良いのか？ それは次回の記事で(ドキュメントは見つけてあるがまだ読んでいない→「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Cocoa Applications Handle Apple Events&lt;/a&gt;」)。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/URI_scheme"&gt;URI scheme&lt;/a&gt; (Wikipedia:en; IANA に登録ずみの公式スキームの他、公式ではないものの良く使われるスキームの一覧が載っている。アプリで使うスキームを考えるときに既存のスキームと衝突していないかのチェックに使える。)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html%23//apple_ref/doc/uid/TP30000999"&gt;Launch Services Programming Guide&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html"&gt;Property List Programming Guide&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/SAppsHandleAEs.html%23//apple_ref/doc/uid/20001239-BBCBCIJE"&gt;Cocoa Scripting Guide: How Cocoa Applications Handle Apple Events&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Security/Conceptual/SecureCodingGuide/Articles/ValidatingInput.html"&gt;Secure Coding Guide: Validating Input&lt;/a&gt; (同上; URL Commands というセクションにアプリで URL スキームを開く場合のセキュリティ上の考慮点についての記述がある)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/macbloggerglass.html"&gt;記事の表示機能を作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/1-blogger-glass.html"&gt;内部リンクを置き換える #1 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-5437486290960336617?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/5437486290960336617/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/1-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5437486290960336617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/5437486290960336617'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/1-macbloggerglass.html' title='アプリを開くリンク #1 - 内部リンクの置き換えのために (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_wobSevGXOh4/TP9N_RpogxI/AAAAAAAABlU/zEic5O7G27E/s72-c/googlechrome_external_app_confirmation_dialog.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-8084938442626194562</id><published>2010-12-08T07:33:00.002+09:00</published><updated>2010-12-08T11:20:46.491+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><title type='text'>twitter より (2010-12-07)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/11996595084066816"&gt;13:13&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;もう、4 は見送りかな。ここまで待って、今更、黒を手に入れるっていうのもモヤモヤが残る。5 を待つか。→ &lt;a href="http://ipodtouchlab.com/2010/12/iphone4-white-delay-confirmed.html"&gt;http://ipodtouchlab.com/2010/12/iphone4-white-delay-confirmed.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-8084938442626194562?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/8084938442626194562/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-07.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8084938442626194562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8084938442626194562'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-12-07.html' title='twitter より (2010-12-07)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6296928665949427050</id><published>2010-12-07T23:22:00.002+09:00</published><updated>2010-12-07T23:22:14.172+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Bindings の肝は KVC 準拠だ</title><content type='html'>&lt;p&gt;ようやく Cocoa Bindings の謎が解けたように思う。少なくとも、Cocoa Bindings を使ったアプリを作るための「心得」のようなものを見つけることができた。以下で、それを簡単に説明してみる。&lt;/p&gt;

&lt;h4&gt;用語の整理&lt;/h4&gt;

&lt;p&gt;まずは用語の整理。Cocoa Bindings 関連のドキュメントを読むためには以下の KVC の用語とそれが意味する概念を理解しておく必要がある。&lt;/p&gt;

&lt;p&gt;KVC (Key-Value Coding) とは、オブジェクトが持つプロパティに対してキーとなる名前を介してアクセスすること、およびそのためのオブジェクト側の実装のことだ。「KVC でアクセスする」とか「このクラスは KVC に準拠させてある」などと使う。&lt;/p&gt;

&lt;p&gt;プロパティ(property)とは、オブジェクトがその内部に抱える「何か」のことで、典型的にはオブジェクトの内部状態を示す様々な値のことだ。ただし、プロパティとはオブジェクトの外部から見えるもののことで、必ずしもプロパティの値そのものが内部で保持されているとは限らない。プロパティとはオブジェクトのインタフェースのうち「何か」を返すものと言っても良い。&lt;/p&gt;

&lt;p&gt;プロパティはその値とオブジェクトとの関係によって、以下の 3 つに分けられる。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;属性(attribute)&lt;/li&gt;
&lt;li&gt;対一関係(to-one relationship)&lt;/li&gt;
&lt;li&gt;対多関係(to-many relationship)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;属性(attribute)とはプロパティのうち、Objective-C で言う単純型のことで、数値や文字列の他、NSColor や NSNumber のような不変オブジェクトもふくまれる。&lt;/p&gt;

&lt;p&gt;対一関係(to-one relationship)とは、プロパティがそれ自身のプロパティを持ったオブジェクトの場合を言う。以下に示す、AppController のプロパティ entry がこれにあたる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface Entry : NSObject {
    NSString *title;
    NSString *content;
    NSDate *lastUpdateDate;
}

@interface AppController : NSObject {
    Entry *entry;
}
&lt;/pre&gt;

&lt;p&gt;対多関係(to-many relationship)とは、簡単に言えばプロパティが配列のような集合型になっている場合のことだ。以下の Feed のプロパティ entries がこれにあたる。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface Feed : NSObject {
    NSMutableArray *entries;
}
&lt;/pre&gt;

&lt;h4&gt;KVC 準拠&lt;/h4&gt;

&lt;p&gt;あるクラスを(正確にはあるクラスの特定のプロパティを) KVC 準拠にするためには、いくつかの要件を満たさなければならない。これは、要は、プロパティに対するアクセス用メソッドを一定の規則による名前で作る、ということだ。&lt;/p&gt;

&lt;p&gt;たとえば、title という文字列のプロパティを持ったクラスの場合なら、以下の 2 つのメソッドを定義する。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (NSString *)title;
- (void)setTitle:(NSString *)aTitle;
&lt;/pre&gt;

&lt;p&gt;この 2 つがあるなら、必ずしも属性を保持するインスタンス変数を定義する必要はない。&lt;/p&gt;

&lt;p&gt;加えて、プロパティへのアクセスにはすべて(クラスの内部でも) KVC 方式を用いる。これは準拠に必須ではないが、こうすることで KVO が正しく機能する。KVC 方式とはインスタンス名に「.」(dot) でプロパティ名を連結した記法のことだ。たとえば、先のクラスのインスタンス名が entry なら、&lt;code&gt;entry.title&lt;/code&gt; としてアクセスする。クラス内部では、インスタンス名として self を使えば良い。つまり、&lt;code&gt;self.title&lt;/code&gt; で読み書きする。&lt;/p&gt;

&lt;p&gt;対一関係の場合も同様だ。さらに、この場合、プロパティも KVC に準拠しているなら「.」でつなげてアクセスする。たとえば、&lt;code&gt;self.entry.title&lt;/code&gt; のように。&lt;/p&gt;

&lt;h5&gt;対多関係の場合の追加&lt;/h5&gt;

&lt;p&gt;一方、対多関係の場合はプロパティが配列(の類)であるため、その要素にアクセスするためのメソッドも必要になる。最初に挙げた Feed の場合、以下のようなメソッドを定義する必要がある(要素として Entry を抱えると想定)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
// for KVC compliance (required)
- (void)insertObject:(Entry *)entry inEntriesAtIndex:(NSUInteger)index;
- (void)removeObjectFromEntriesAtIndex:(NSUInteger)index;
- (void)replaceObjectInEntriesAtIndex:(NSUInteger)index withObject:(Entry *)entry;
// for KVC compliance (optional)
- (int)countOfEntries;
- (Entry *)objectInEntriesAtIndex:(NSUInteger)index;
&lt;/pre&gt;

&lt;p&gt;これらのメソッド名にはプロパティ名が埋め込まれている。たとえば、insert の場合、insertObject:in&amp;lt;key&amp;gt;AtIndex: の key のところにプロパティ名の entries が埋め込んである。他も同様だ。&lt;/p&gt;

&lt;h4&gt;KVO 準拠&lt;/h4&gt;

&lt;p&gt;KVO 準拠で何より大事なことはプロパティのアクセスはすべて KVC 方式で行う、ということだ。たとえば以下は、ここ数日、悩まされてきたエラーメッセージだ。このエラーの原因が、あるクラスで自分自身のプロパティへのアクセスに KVC 方式を使っていなかった(部分があった)ことにあった。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(実行時のログより)&lt;br&gt;
Cannot update for observer &amp;lt;NSAutounbinderObservance 0x113761290&amp;gt; for the key path "feed.entries" from &amp;lt;PreferenceController 0x11375cc50&amp;gt;, most likely because the value for the key "feed" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the PreferenceController class.
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;具体的には、以下のようなコードになっていた。本来、reload の中で newArray をプロパティにセットした際に、これを監視しているオブジェクトたちに通知が飛ぶことになる。ところが、reload が実行される前に awakeFromNib の中で KVC 方式を使わずに entries を変更してしまっていたため、この通知が送れなくなっていた。それが上のエラーだ。awakeFromNib の中でも、reload と同様に &lt;code&gt;self.entries&lt;/code&gt; という表記を使うようにすることで、この問題は解消した。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@implementation AppController
&lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
- (void)awakeFromNib
{
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    entries = [[NSMutableArray alloc] init];
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}

- (IBAction)reload:(id)sender
{
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    self.entries = newArray;
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}
&lt;/pre&gt;

&lt;p&gt;また、対多関係プロパティの場合、プロパティへの要素の追加・変更・削除では、先述のように KVC 準拠のために定義したメソッドを使わなければならない。&lt;/p&gt;

&lt;h4&gt;まとめ&lt;/h4&gt;

&lt;p&gt;以上が、ここ数日、悩まされてきた問題に対する一応の解答になる。まとめると、Cocoa Bindings を使ったアプリを作る上で肝心なことは、とにもかくにも KVC 準拠ということになる。&lt;/p&gt;

&lt;p&gt;そして、KVC 準拠でクラスを作ろうと思うなら徹底して KVC 準拠にすることだ。特定のプロパティだけを KVC 準拠にしようなんて中途半端なことを考えていると、抜けが出てくる。ひとつのクラスの一部は KVC 準拠で他は違うというような状態では、(プログラムが動かないとわかってから)準拠の抜けを探すのは難しい。デバッグで苦労するよりも、コーディング中に少し余分に気を使う方が良い。&lt;/p&gt;

&lt;p&gt;Cocoa Bidings を使ったアプリ (MVC アーキテクチャを想定) を作るなら、まずはモデルのクラスをを KVC 準拠で作ること。また、モデル内部の他のメソッドでも、モデルオブジェクトを扱うコントローラの実装でも、とにかくモデルのプロパティへのアクセスには KVC を使うこと。対多関係を使うなら(モデルが配列等を内部に抱えるなら)、内部の配列に直接アクセスするのではなく、insert や remove、replace といったメソッドを KVC 準拠の形式で定義すること。&lt;/p&gt;

&lt;p&gt;あとひとつ付け加えるとすれば、やはり Interface Builder に対する慣れだろうか。インスペクタで GUI 部品の設定をあちこちいじっていると、どこをどう変更したのかがわからなくなってくる。どうしても動かなくて困っていたところ、部品を一度削除して最初から設定し直すと動いた、なんてことが時々起こる。こればかりは慣れるしかないと思う。部品を汎用に作ろうとすれば設定項目を増やさざるを得ず、設定項目が増えればそのためのツールは複雑になるものだから。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html%23//apple_ref/doc/uid/10000107i"&gt;Key-Value Coding Programming Guide&lt;/a&gt; (Mac OS X Reference Library)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/Terminology.html%23//apple_ref/doc/uid/20001840-BAJEAIEE"&gt;Terminology&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/AccessorConventions.html%23//apple_ref/doc/uid/20002174-BAJEAIEE"&gt;Key-Value Coding Accessor Methods&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings.html"&gt;Cocoa Bindings がわからなくなってきた&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings_06.html"&gt;まだ Cocoa Bindings の謎が解けない&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6296928665949427050?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6296928665949427050/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings-kvc.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6296928665949427050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6296928665949427050'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings-kvc.html' title='Cocoa Bindings の肝は KVC 準拠だ'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6926563989953033939</id><published>2010-12-06T23:12:00.004+09:00</published><updated>2010-12-08T14:04:10.453+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>まだ Cocoa Bindings の謎が解けない</title><content type='html'>&lt;p&gt;前回の状態(→「&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings.html"&gt;Cocoa Bindings がわからなくなってきた&lt;/a&gt;」)からまだ抜け出せない。以下に、今、ハマっている状況を簡単に説明してみる。&lt;/p&gt;

&lt;p&gt;モデルとコントローラの構造と関係は以下のようになっている。Feed の entries は配列(NSMutableArray)で Entry のインスタンスを抱えている。Entry のプロパティは文字列(NSString)や日付(NSDate)といったオブジェクトだ。このモデルをアプリの主コントローラが抱えている、という形だ。Atom 形式のデータの構造を扱うアプリを想定している(MacBloggerGlass もその一つ)。&lt;/p&gt;

&lt;pre&gt;
+--------------+
| AppContoller |
+--------------+        +--------------+
| feed        ---------&gt;| Feed         |
| ...          |1     1 +--------------+       +--------------+
+--------------+        | entries     --------&gt;| Entry        |
                        | ...          |1    * +--------------+
                        +--------------+       | title        |
                                               | content      |
                                               | lastUpdate   |
                                               | ...          |
                                               +--------------+
&lt;/pre&gt;

&lt;p&gt;やりたいことは、Feed が抱える Entry オブジェクトのデータを表(NSTableView)に写し出すことだ。&lt;/p&gt;

&lt;p&gt;Cocoa Bindings を使わないなら、AppController を NSTableView のデータソースにすることになる。これは簡単に言うと、NSTableView から「この行のここの列に表示するデータはどれ？」という問い合わせに AppController が答える方式だ。行と列を指定して、配列に収められたオブジェクトのプロパティを取り出すコードを AppController に実装することになる。&lt;/p&gt;

&lt;p&gt;Cocoa Bindings を使えばそのコードを書かなくて済む。NSArrayController を介してモデルとビュー(NSTableView)を結びつけることでコードを書かずに同じことを実現できる……はずなのだが、なんだかうまくいかない。いや、うまく動くときもある。まったく動かないなら間違っていることだけは確実にわかる。けれど、同じように書いたり設定したりしたつもりのものが動いたり動かなかったりすると、かえってわかりにくくなる。&lt;/p&gt;

&lt;p&gt;どうやら、肝となるのはモデルとコントローラが KVC と KVO に準拠することらしい。で、KVC に準拠するにはどうすれば良いか、についてはわかってきた。だが、KVO への準拠のための施策が良くわからない。ドキュメント(&lt;a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i"&gt;Key-Value Observing Programming Guide&lt;/a&gt;)に Ensuring KVO Compliance という項目があるものの、そこに書かれているのは 2 つの項目だけ。しかもそのうちの 1 つは KVC に準拠しろ、というもの。残る一つがこれ。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/Concepts/KVOCompliance.html%23//apple_ref/doc/uid/20002178-BAJEAIEE"&gt;Key-Value Observing Programming Guide: Ensuring KVO Compliance&lt;/a&gt;」より)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The class must allow automatic observer notifications for the property, or implement manual key-value observing for the property.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;これだけじゃなあ。もう少し細かく書いて欲しいよ。(´・ω・｀)&lt;/p&gt;

&lt;p&gt;とにかく、今日も先には進めていない。明日も、今日の続きで Cocoa Bindings の謎と格闘することになる。&lt;/p&gt;

&lt;div class="postscript"&gt;
&lt;h5&gt;追記@2010-12-08&lt;/h5&gt;
&lt;p&gt;この記事に書いた問題はひとまず解決した。「&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings-kvc.html"&gt;Cocoa Bindings の肝は KVC 準拠だ&lt;/a&gt;」を参照のこと。&lt;/p&gt;
&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html#//apple_ref/doc/uid/10000107i"&gt;Kye-Value Coding Programming Guide&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i"&gt;Key-Value Observing Programming Guide&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings.html"&gt;Cocoa Bindings がわからなくなってきた&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6926563989953033939?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6926563989953033939/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings_06.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6926563989953033939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6926563989953033939'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings_06.html' title='まだ Cocoa Bindings の謎が解けない'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7027030851124055213</id><published>2010-12-05T21:28:00.001+09:00</published><updated>2010-12-08T14:06:55.668+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Bindings がわからなくなってきた</title><content type='html'>&lt;p&gt;この数日、Cocoa Bindings の使い方で悩んでいる。というより、混乱している。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass に環境設定パネルを作る際にモデルとビュー(パネルに配置した GUI 部品)を結びつけるために、Cocoa Bindings を使った。試行錯誤とドキュメントの流し読みで、どうにか動くものができた。その過程で Cocoa Bindings についても理解した、と思っていた。&lt;/p&gt;

&lt;p&gt;同じことをアプリのメインウィンドウでもやろうとした。環境設定パネルでブログの一覧をテーブルビューに表示させたように、メインウィンドウでも記事一覧をテーブルビューに表示させるのに Cocoa Bindings を使おうとしたのだ。これがうまくいかない。環境設定パネルの時と同じように書いたら動かない。&lt;/p&gt;

&lt;p&gt;試行錯誤を繰り返し、メインウィンドウでも表示できるようになったが、そのコードは環境設定パネルのものとは少し異なっていた。具体的には、プロパティの更新を KVO で通知する部分が違う。&lt;/p&gt;

&lt;p&gt;なにかおかしい。もちろん、おかしいのはわたしの理解の方だ。基本に立ち返って、KVC、KVO の理解を点検するところから始めようと思う。ここをクリアにしない限り、先には進めない。&lt;/p&gt;

&lt;div class="postscript"&gt;
&lt;h5&gt;追記@2010-12-08&lt;/h5&gt;
&lt;p&gt;この問題については、以下の後続記事を参照のこと。一応、解決している。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings_06.html"&gt;まだ Cocoa Bindings の謎が解けない&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/cocoa-bindings-kvc.html"&gt;Cocoa Bindings の肝は KVC 準拠だ&lt;/a&gt; (これが一応の解決編)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html#//apple_ref/doc/uid/10000107i"&gt;Kye-Value Coding Programming Guide&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i"&gt;Key-Value Observing Programming Guide&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7027030851124055213?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7027030851124055213/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7027030851124055213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7027030851124055213'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/cocoa-bindings.html' title='Cocoa Bindings がわからなくなってきた'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-3442875181777982039</id><published>2010-12-04T23:06:00.003+09:00</published><updated>2010-12-08T13:59:36.813+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><title type='text'>記事の表示機能を作る (MacBloggerGlass)</title><content type='html'>&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/j5lUg8ZoX30Wk227QFpGSQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TPepllMBstI/AAAAAAAABkw/0ARTzREBdlo/s288/mbg_main_window-proto_0.png" height="255" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;右のスクリーンショットは MacBloggerGlass のプロトタイプ #0 だが、アプリウィンドウの上部が記事一覧、下部が一覧で選んだ記事内容の表示となっている。これを見る限り、一応記事表示らしいことはできるが、&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;前にも書いた&lt;/a&gt;ように、これはフィードから切り出した/データをそのまま、WebView に流し込んでいるだけだ。言わば HTML の断片で、正しい HTML にするためには、いろいろと補わなければならない。&lt;/p&gt;

&lt;p&gt;というより、GAE 版のようにテンプレートを用意し、このデータを流し込むようにするべきだ。さらに言えば、テンプレートの HTML 的な構造は GAE 版と同じにしておきたい。そうすればスタイルシートがそのまま使える。&lt;/p&gt;

&lt;p&gt;GAE 版の Mac 用(記事表示画面)テンプレートの構造(の概略)は以下のようになっている。この div.content の部分に、フィードから切り出した記事のデータを(多少、記事タイトル等の付加情報を付けて)流し込んでいる。&lt;/p&gt;

&lt;pre&gt;
html
+-- head
+-- body
    +-- header
    +-- section
    |   +-- div.content
    |       ...
    +-- footer
&lt;/pre&gt;

&lt;p&gt;Objective-C でこれ(テンプレートの利用)をやるには、どうすれば良いか？ 手軽な方法は、テンプレートを div.content で 2 つに分けてしまうものだろう。そして、前半分、記事内容、後半分の 3 つを連結し、一時ファイルに保存するか、または文字列で直接、WebView にわたす。&lt;/p&gt;

&lt;p&gt;CSS や JavaScript もアプリの中に抱えることになる。HTML 中からそこへのリンクをどうするか。「file:///」でアクセスすることになるから問題はパス。一時ファイルに書き出すなら、その場所から MacBloggerGlass.app の中のファイルにリンクすることになる。つまり、CSS 等へのリンクは実行時に生成して、HTML に書き込まなければならない。&lt;/p&gt;

&lt;p&gt;いっそのこと、CSS 等の付属ファイルも一時ファイルの置き場所にまとめて書き出すか。それなら、リンクは相対表記になりあらかじめテンプレートに書き込んでおける。そうなると一時ファイルというか実行時の環境だな。&lt;/p&gt;

&lt;p&gt;そういえば、今、気付いたが、内部リンクをどうすれば良いのか。WebView に HTML としてわたしてしまったらただのリンク、クリックすれば(WebView は)ブラウザとして開くだろう。アプリには手の出しようがない。となると、記事内容はファイルとして書き出すしかないか。そうすれば内部リンクはローカルファイルの参照に書き換えられる。書き出す場所は &lt;span class="path"&gt;~/Library/MacBloggerGlass&lt;/span&gt; あたりに。&lt;/p&gt;

&lt;p&gt;ウェブアプリなら簡単に実現できること(内部リンクはアプリへのリクエストに書き換える)がローカルなアプリには難しい。そういうものがあるとはね。ちょっと意外だった。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html"&gt;設定変更を通知する - 環境設定パネルを作る #4 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/1-blogger-glass.html"&gt;内部リンクを置き換える #1 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="postscript"&gt;
&lt;h5&gt;追記@2010-12-08&lt;/h5&gt;
&lt;p&gt;↑のリストに「&lt;a href="http://logrepo.blogspot.com/2010/11/1-blogger-glass.html"&gt;内部リンクを置き換える #1 (Blogger Glass)&lt;/a&gt;」を追加。&lt;/p&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-3442875181777982039?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/3442875181777982039/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3442875181777982039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/3442875181777982039'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/macbloggerglass.html' title='記事の表示機能を作る (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_wobSevGXOh4/TPepllMBstI/AAAAAAAABkw/0ARTzREBdlo/s72-c/mbg_main_window-proto_0.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6555887005215900543</id><published>2010-12-03T14:55:00.002+09:00</published><updated>2010-12-03T14:56:49.177+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='1. GAEをプログラムする'/><title type='text'>Google App Engine SDK がアップデート (1.3.8 → 1.4.0)</title><content type='html'>&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/nF0VEK7p-QsQ0cutnWsr2A?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TPiF-gjqi0I/AAAAAAAABk8/fNI1hrmtQEc/s288/gae_launcher_1.4.0-about.png" height="288" width="273" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;2010-12-02 付けで Python 版 Google App Engine SDK がバージョンアップしている。iMac で作業をしていると、Google アプリのアップデータが起動して、GoogleAppEngineLauncher.app が更新されたと告げられた。そのままアップデートを実行。Java 版のことはわからないが、公式サイトからダウンロード可能な SDK のバージョンは Python 版と同じ 1.4.0 になっている。&lt;/p&gt;

&lt;p&gt;で、何が変わったかを調べようと公式ドキュメントのページから &lt;a href="http://code.google.com/p/googleappengine/wiki/SdkReleaseNotes"&gt;Release Note&lt;/a&gt; を開いた。以下に、ざっくりと超直訳してみる。誤解、曲解、いろいろふくまれている可能性がある。訳の品質に期待しすぎないように。&lt;/p&gt;

&lt;ul class="clear"&gt;
&lt;li&gt;Always On 機能はアプリが 3 つのインスタンスを常時実行状態のまま保持することのできる機能だ(課金対象)。アプリの遅延を著しく削減することができる。&lt;/li&gt;

&lt;li&gt;開発者は Warmup リクエストを有効にできるようになった。アプリの app.yaml 中でハンドラを指定すると、App Engine はアプリの新しいインスタンスがユーザからのリクエストを受け付け始める前に、その初期化のために Warmup Request を送ろうとする。これにより、エンドユーザがアプリの初期化にともない感じる遅延を削減できる。&lt;/li&gt;

&lt;li&gt;Channle API がすべてのユーザで利用できるようになった。&lt;/li&gt;

&lt;li&gt;Task Queue が公式にリリースされ、もはや実験的な機能ではなくなった。'labs' を使った API のインポートパスは非推奨となった。Task Queue で使うストレージはアプリ全般のストレージ割り当て量として計量され、課金の対象となる。&lt;/li&gt;

&lt;li&gt;Task Queue と Cron リクエストのデッドラインが 10 分に引き上げられた。それらのリクエスト中であっても、データストアと API のデッドラインは以前から変更されていない。&lt;/li&gt;

&lt;li&gt;Task Queue に対して、開発者は queue.yaml でタスクの retry_parameters を指定できる。&lt;/li&gt;

&lt;li&gt;課金を有効にしているアプリでは Task Queue API で待ち行列(queue)を 100 個まで使える。&lt;/li&gt;

&lt;li&gt;データストアに対して種別、名前空間および実体のプロパティを問い合わせるメタデータクエリが使えるようになった。&lt;/li&gt;

&lt;li&gt;URLFetch ではレスポンスのサイズとして 32 MB まで許されるようになった。リクエストのサイズは引き続き 1MB までとなっている。&lt;/li&gt;

&lt;li&gt;イメージ API に対するリクエストとレスポンスのサイズが 32BM に増やされた。Memcach のバッチ操作の合計サイズが 32 MB に増やされた。Memcache の個別のオブジェクトに対する 1MB の制限は引き続き適用される。&lt;/li&gt;

&lt;li&gt;送信メールへの添付のサイズが 1MB から 10MB に増やされた。受信メールのサイズ制限は引き続き 10MB となっている。&lt;/li&gt;

&lt;li&gt;データストア上のバッチ方式による get/put/delete の操作に対する大きさと数量の制限は取り除かれた。個別の実体は引き続き 1MB に制限されているが、データストア全体に対するデッドラインに余裕があれば、バッチ方式で望むだけ多くの実体を同時に get/pub/delete 処理することができる。&lt;/li&gt;

&lt;li&gt;クエリー結果をもとに反復する場合、データストアサービスは非同期に結果を先読みするようになった。これにより遅延を 10 - 15 % 削減できる場合がある。&lt;/li&gt;

&lt;li&gt;管理コンソールの Blacklist ページは拒絶された訪問者の上位の一覧を表示する。&lt;/li&gt;

&lt;li&gt;画像のサムネイルの自動生成サービスは 1600px までの任意の切り取りサイズをサポートする。&lt;/li&gt;

&lt;li&gt;管理コンソールに表示される全般的なインスタンス遅延の平均値は、インスタンスごとの QPS に応じた平均値になった。&lt;/li&gt;

&lt;li&gt;アプリのあるバージョンをアップロードした開発者は appcfg.py download_app コマンドを使ってそのバージョンのコードをダウンロードできる。この機能はアプリごとに管理コンソールの Permissions タブで無効にできる。一度無効にすると、この機能を再度有効にすることはできない。&lt;/li&gt;

&lt;li&gt;独自ドメインで Google Appes を使っているユーザに対して、カスタム管理コンソールのページが機能していなかった問題を修正。&lt;/li&gt;

&lt;li&gt;Python 実行環境では、リクエストハンドラが DeadlineExceededError を起こした場合は、インスタンスは強制終了された後に再開される。これは、Django を使っているときに SystemErrors が周期的に起きる問題と関連して修正されるべきだ。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=772"&gt;http://code.google.com/p/googleappengine/issues/detail?id=772&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;webapp.template とピュア Django を混在させたときに起きる Django のバージョンの不一致を避けるため、Django の初期化を appengine_config.py に移動できるようになった。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=1758"&gt;http://code.google.com/p/googleappengine/issues/detail?id=1758&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;SSL 上の OpenId の問題を修正。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=3393"&gt;http://code.google.com/p/googleappengine/issues/detail?id=3393&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;dev_appserver で login/logout のためのコードが Python 2.6 で動かない問題を修正。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=3566"&gt;http://code.google.com/p/googleappengine/issues/detail?id=3566&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;dev_appserver で get_serving_url が透明で長さの足りない(cropped) PNG に対して機能しない問題を修正。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=3887"&gt;http://code.google.com/p/googleappengine/issues/detail?id=3887&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;DatastoreFileSub の問題を修正。
&lt;br&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=3895"&gt;http://code.google.com/p/googleappengine/issues/detail?id=3895&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://bloggerglass.blogspot.com/"&gt;Blogger Glass&lt;/a&gt; に関係する変更としては、Task Queue が公式に GAE のサービスの一部としてリリースされたことぐらいのようだ。API が変わったとは書いていないから、今までのコードはそのまま動くってことだろう。とりあえず、taskqueue の import から labs のネームスペースを削除しておいた。appspot に配備ずみ。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/googleappengine/wiki/SdkReleaseNotes"&gt;Google App Engine Python SDK Release Notes&lt;/a&gt; (公式ドキュメントより)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/appengine/docs/python/taskqueue/overview.html"&gt;Task Queue Python API Overview&lt;/a&gt; (同上)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/3-blogger-glass.html"&gt;タスクキューを使う - 内部リンクを置き換える #3 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/09/google-app-engine.html"&gt;外部ファイルの置き場所としての Google App Engine&lt;/a&gt; (SDK のインストールについて書いてある)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6555887005215900543?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6555887005215900543/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/google-app-engine-sdk-138-140.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6555887005215900543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6555887005215900543'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/google-app-engine-sdk-138-140.html' title='Google App Engine SDK がアップデート (1.3.8 → 1.4.0)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_wobSevGXOh4/TPiF-gjqi0I/AAAAAAAABk8/fNI1hrmtQEc/s72-c/gae_launcher_1.4.0-about.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7339472440743463649</id><published>2010-12-02T23:47:00.004+09:00</published><updated>2010-12-02T23:59:18.276+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ヒレガス本'/><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>設定変更を通知する - 環境設定パネルを作る #4 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;今回の記事の内容は、環境設定パネルで Blog ID を保存したときに(「Save」ボタンを押す)、アプリウィンドウに表示されているブログ一覧が変更された ID のものに自動的に変わるようにするための実装について。このような動作を実装する意図については、前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/12/nsnotification-3-macbloggerglass.html"&gt;次は NSNotification に取り組む&lt;/a&gt;」)を参照してもらいたい。&lt;/p&gt;

&lt;h4&gt;変更点&lt;/h4&gt;

&lt;p&gt;実装の手順は以下の 4 つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(a) 通知の名前を文字列定数として定義する。&lt;/li&gt;
&lt;li&gt;(b) 通知を送るオブジェクトで、通知を送るコードを追加する。&lt;/li&gt;
&lt;li&gt;(c) 通知を受けるオブジェクトで、通知を処理するメソッド(通知ハンドラ)を定義する。&lt;/li&gt;
&lt;li&gt;(d) 通知を受けるオブジェクトで、監視の登録のためのコードを追加する。&lt;/li&gt;
&lt;li&gt;(e) 通知を受けるオブジェクトで、監視の登録解除のためのコードを追加する。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(a) はどこで定義しても構わないが、(b) と同じく通知を送るオブジェクトと一緒に定義する方が良いだろう。通知を受けるオブジェクトでは、送るオブジェクト(のクラス)のインタフェース部を #import することになる。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass の場合、通知を送るのが PreferenceController で、受けるのが AppController になる。&lt;/p&gt;

&lt;p&gt;(b) の通知を送るタイミングは、環境設定パネルで「Save」ボタンが押されたとき。つまり、ボタンに対応するアクションメソッド内になる。&lt;/p&gt;

&lt;p&gt;監視の登録 (d) については、少なくとも最初に環境設定パネルが開くより前でなければならない。確実なのは AppController が作られたときだ。AppController はアプリの MainMenu.xib にインスタンスとして登録されているから、アプリが起動した後は NIB がロードされる中で生成(というか復元)される。このタイミングは awakeFromNib で拾うことができる。一般にアプリの起動時に生成(初期化)されるオブジェクトなら init で登録すれば良い(&lt;a href="http://programmingmac.blogspot.com/2008/05/3rd-ed.html"&gt;ヒレガス本&lt;/a&gt; Chapter 14 の例はこちらになっている)。また、登録の解除 (e) は dealloc で行う(これはヒレガス本の例と同じ)&lt;/p&gt;

&lt;h4&gt;実装&lt;/h4&gt;

&lt;h5&gt;PreferenceController&lt;/h5&gt;

&lt;h6&gt;インタフェース部: PreferenceController.h&lt;/h6&gt;

&lt;p&gt;追加したのは 1 行。クラスのインタフェースにふくまれるものではないので &lt;code&gt;@interface {...}&lt;/code&gt; の外側に置かなければならない。こういう定数が増えたら、定数だけを独立させて別ファイルにすべきかも。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
extern NSString * const MBGBlogIDChangeNotification;
&lt;/pre&gt;

&lt;h6&gt;実装部: PreferenceController.m&lt;/h6&gt;

&lt;p&gt;&lt;a href="http://programmingmac.blogspot.com/2008/05/3rd-ed.html"&gt;ヒレガス本&lt;/a&gt;からそのまま引き写し。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (IBAction)saveSettings:(id)sender
{
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    // send notification
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:selectedBlog.blogID forKey:@&amp;quot;blogID&amp;quot;];
    [nc postNotificationName:MBGBlogIDChangeNotification
                      object:self
                    userInfo:userInfo];
    NSLog(@&amp;quot;Post notification with userInfo (%@)&amp;quot;, userInfo);

    [self close];
}
&lt;/pre&gt;

&lt;h5&gt;AppController&lt;/h5&gt;

&lt;h6&gt;実装部: AppController.m&lt;/h6&gt;

&lt;p&gt;通知ハンドラ handleBlogIDChange: は &lt;span class="path"&gt;AppController.m&lt;/span&gt; だけから参照できる AppController のプライベートメソッドとした(ファイルローカルメソッドと呼ぶべきか？)。これについては&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt;の「CHAPTER 09 カテゴリ」を参照のこと。&lt;/p&gt;

&lt;p&gt;fetchPosts は GData クライアントライブラリを使って記事のフィードをリクエストするもの。これまで getFeed: というアクションメソッドの一部だったものを今回独立させた。これは、getFeed: と通知ハンドラの両方から呼べるようにするため。もっとも、今回の変更にあわせて、メインウィンドウから Blog ID を入力するフィールドと記事一覧を取得するためのボタンを取り除いたため、getFeed: は無用になってしまったが。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
@interface AppController (PrivateMethods)
- (GDataServiceBase *)bloggerService;
- (void)fetchPosts;
- (void)renderHTML:(NSString *)content withBaseURL:(NSString *)baseURL;
- (void)handleBlogIDChange:(NSNotification *)note;
@end

@implementation AppController
&lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
- (void)awakeFromNib
{
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(handleBlogIDChange:) name:MBGBlogIDChangeNotification object:nil];
}
@end

@implementation AppController (PrivateMethods)
&lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
- (void)handleBlogIDChange:(NSNotification *)note
{
    NSLog(@"Received notification: %@", note);
    blogID = [[note userInfo] objectForKey:@"blogID"];

    [posts removeAllObjects];
    [self fetchPosts];
}
@end
&lt;/pre&gt;

&lt;h4&gt;プロトタイプ #0&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/j5lUg8ZoX30Wk227QFpGSQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TPepllMBstI/AAAAAAAABkw/0ARTzREBdlo/s288/mbg_main_window-proto_0.png" height="255" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;右のスクリーンショットが今の MacBloggerGlass でこのブログを表示させた様子だ。NSSplitView を使い、上半分で記事一覧、下半分でその内容を表示させている。記事の表示には WebView に対して、取得した記事のフィードのコンテンツをただ文字列として流し込んでいるだけ。つまり、HTML としてきちんとしたドキュメントの構造になっていないし、もちろんスタイルシートや JavaScript も付いていない。ま、GData クライアントライブラリを使った RSS リーダのサンプルアプリといったところか。&lt;/p&gt;

&lt;p&gt;今回のプロトタイプでは Blog ID の選択を環境設定に押し込めている。これは、ブログは高々 1 つしか見ることはないし、Blog ID を切り替える頻度は高くない、と想定したから。実際に動かしてみて思うことは、複数のブログを作っているなら他のブログも同時に見たいと思うかもしれない、ということ。つまりは、ブラウザのようにタブやウィンドウで複数のコンテンツを同時に開ける方が良いかもしれない。&lt;/p&gt;

&lt;p&gt;ま、それは 1 つのブログをきちんと表示できるようになってから考えよう。せめて、GAE 版と同等の表示ができるようにしてからだ。&lt;/p&gt;

&lt;h4 class="clear"&gt;おまけ&lt;/h4&gt;

&lt;p&gt;予告(→「&lt;a href="http://logrepo.blogspot.com/2010/12/nsnotification-3-macbloggerglass.html"&gt;次は NSNotification に取り組む」&lt;/a&gt;)では「通知」に関連するドキュメントも探すと書いたが、時間切れで本当に探すだけになってしまった。まだ読めていない。リンクだけは「関連リンク」のところに挙げておく。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/XwHeLrRUYcca0YR6pFvVVQ?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TPeYTHgukXI/AAAAAAAABkY/nZbo7Ne3kY0/s288/devpedia_cocoa-sample_shot.png" height="246" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;また、「通知」に関して書かれたドキュメントを検索していて便利そうなものを見つけた。Devpedia シリーズとでも呼べば良いのかな(URL の一部になっている)。右のスクリーンショットはそれを Safari で開いたところだ。&lt;/p&gt;

&lt;p&gt;何が便利かと言うと、たとえば「&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/AccessorMethod.html"&gt;Cocoa Core Competencies&lt;/a&gt;」なら、Cocoa アプリを作るために必要になるさまざまなトピックを一覧できること。また、それぞれについての簡単な説明(使い方をふくむ)が付いていて、より詳しいドキュメントへのリンクもある。&lt;a href="http://programmingmac.blogspot.com/2008/05/3rd-ed.html"&gt;ヒレガス本&lt;/a&gt;のように、アプリの作り方を順を追って説明するようなタイプのものではないが、実際にアプリを作るときにドキュメントを参照する出発点として役に立ちそう。&lt;/p&gt;

&lt;p class="clear"&gt;検索して見つかったのは以下の 3 つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/AccessorMethod.html"&gt;Cocoa Core Competencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/Devpedia-CocoaApp/Animation.html"&gt;Cocoa Application Competencies for iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/DataManagement/Devpedia-CoreData/coreDataOverview.html"&gt;Core Data Core Competencies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41pyKTm2fxL._SL160_.jpg" alt="Cocoa Programming for Mac OS X" title="Cocoa Programming for Mac OS X" width="121" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;Aaron Hillegass&lt;br /&gt;Addison-Wesley Professional ( 2008-05-15 )&lt;br /&gt;ISBN: 9780321503619&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html"&gt;Notification Programming Topics&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html%23//apple_ref/doc/uid/TP40002974-CH7-SW7"&gt;Cocoa Funddamentals Guide: Communicating with Objects: Notifications&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaPerformance/Articles/Notifications.html%23//apple_ref/doc/uid/TP40001446-BCIDEFII"&gt;Cocoa Performance Guidelines: Notifications&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/AccessorMethod.html"&gt;Cocoa Core Competencies&lt;/a&gt; (同上; Devpedia シリーズ)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/General/Conceptual/Devpedia-CocoaApp/Animation.html"&gt;Cocoa Application Competencies for iOS&lt;/a&gt; (同上; Devpedia シリーズ)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/DataManagement/Devpedia-CoreData/coreDataOverview.html"&gt;Core Data Core Competencies&lt;/a&gt; (同上; Devpedia シリーズ)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/12/nsnotification-3-macbloggerglass.html"&gt;次は NSNotification に取り組む - 環境設定パネルを作る #3 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題 - 環境設定パネルを作る #2 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る #1 (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7339472440743463649?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7339472440743463649/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7339472440743463649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7339472440743463649'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/4-macbloggerglass.html' title='設定変更を通知する - 環境設定パネルを作る #4 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_wobSevGXOh4/TPepllMBstI/AAAAAAAABkw/0ARTzREBdlo/s72-c/mbg_main_window-proto_0.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4710641813787125943</id><published>2010-12-01T23:37:00.002+09:00</published><updated>2010-12-01T23:37:44.108+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>次は NSNotification に取り組む - 環境設定パネルを作る #3 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;今回は明日の作業の予告だけ。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass の環境設定パネルについて「設定の保存にまつわる問題」で挙げた 3 つの問題は、&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-3.html"&gt;NSTableView の選択を Cocoa Bindings でモデルと結びつける方法&lt;/a&gt;がわかったことですべて解決できた。明日は、本筋の&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;環境パネルの実装&lt;/a&gt;に戻ることにする。&lt;/p&gt;

&lt;p&gt;以前に挙げた「ざっくり手順」(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;」を参照)で言えば、残りは「User Defaults の値を参照するように、メインンアプリのコードを変更する」になる。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/sbWEIDVHzgqLRI66LH6NIQ?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TOkXh3q5haI/AAAAAAAABgE/tHz28KW0u3o/s288/macbloggerglass_listview.png" height="229" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;とは言っても、単純にメインウィンドウのアクション(右のスクリーンショットの Read ボタン)に組み込むことは難しくない。アクションメソッドの冒頭でテキストフィールドから読み取っている Blog ID を User Defaults から読むだけ。&lt;/p&gt;

&lt;p class="clear"&gt;今ある動作確認用の一時コードは以下のようになっている(Blog ID を入力するフィールドは削除する予定だから、以下はあくまで環境設定パネルの動作確認用)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (IBAction)getFeed:(id)sender
{
    blogID = [blogIdField stringValue];
    if ([blogID length] == 0) {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        blogID = [defaults objectForKey:MBGSelectedBlogID];
    }
    NSLog(@"Blog ID: %@", blogID);
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
}
&lt;/pre&gt;

&lt;p&gt;これだけで「メインアプリのコードを変更する」ことが完了なら、もう終わっていることになる。もちろん、これだけではない。環境設定パネルで表示するブログを選択(変更)し「Save」ボタンを押したときに何が起きるべきかを考えてみよう。&lt;/p&gt;

&lt;p&gt;ユーザは表示するブログを変更するために環境設定パネルを開く。その中でブログを変更したなら、アプリウィンドウに戻ってきたときに何を期待するだろうか？ もう一度「Read」ボタンを押そうと思うだろうか？ ユーザが環境設定パネルで「Save」ボタンを押したときの期待は (a) パネル内の変更が User Defaults として保存されること、(b) その変更がアプリに&lt;b&gt;直ちに&lt;/b&gt;反映されること、の 2 つのはずだ。&lt;/p&gt;

&lt;p&gt;そこで NSNotification の出番となる(&lt;a href="http://programmingmac.blogspot.com/2008/05/3rd-ed.html"&gt;ヒレガス本&lt;/a&gt;の Chapter 14 の内容)。これは、言わばアプリ内のオブジェクト間通信で、あらかじめ取り決めた名前を媒介にして、あるオブジェクトから別のオブジェクトに情報を送る仕組みだ。&lt;/p&gt;

&lt;p&gt;MacBloggerGlass の場合で言えば、PreferenceController から saveSettings: の中で通知を送り、それを AppController が受け取ることになる。通知には任意のオブジェクトを同封できるため、新しい Blog ID を (NSString) として付ければ良い。受け取った側では、送られてきた Blog ID が表示中のものと変わっていたら、記事一覧を新たに取得する、となる。&lt;/p&gt;

&lt;p&gt;明日は、コードの実装に加えて、関連するドキュメントも探すつもり。&lt;/p&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-3.html"&gt;Cocoa Bindings の使い方 #3 - 選択を結びつける&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4710641813787125943?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4710641813787125943/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/nsnotification-3-macbloggerglass.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4710641813787125943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4710641813787125943'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/nsnotification-3-macbloggerglass.html' title='次は NSNotification に取り組む - 環境設定パネルを作る #3 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_wobSevGXOh4/TOkXh3q5haI/AAAAAAAABgE/tHz28KW0u3o/s72-c/macbloggerglass_listview.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6672347358430966076</id><published>2010-12-01T07:34:00.002+09:00</published><updated>2010-12-01T22:21:21.027+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><title type='text'>twitter より (2010-11-30)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/9619851056254976"&gt;23:48&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;ペーパークラフト、すげえ (； ゜д゜) → &lt;a href="http://jp.makezine.com/blog/2010/11/papercraft_battleship.html"&gt;http://jp.makezine.com/blog/2010/11/papercraft_battleship.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6672347358430966076?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6672347358430966076/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-11-30.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6672347358430966076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6672347358430966076'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/12/twitter-2010-11-30.html' title='twitter より (2010-11-30)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2816968499939204470</id><published>2010-11-30T23:20:00.000+09:00</published><updated>2010-11-30T23:20:09.063+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Bindings の使い方 #3 - 選択を結びつける</title><content type='html'>&lt;p&gt;前回の記事で(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;」)、環境設定パネルの表示状態を保存・復元するためには、表部分の選択もモデルとして保持しなければならない、と書いた。&lt;/p&gt;

&lt;p&gt;そこで、今回の内容は、NSTableView で選択されるべき行をモデルに格納する方法。もちろん、両者を結びつけるには Cocoa Bindings を使う。&lt;/p&gt;

&lt;p&gt;まず、考えるべきは表の選択(に関する情報)を、Cocoa Bindings ではどう扱えば良いのか？ 表 (NSTableView) とモデルを結びつけているのは NSArrayController だ。ならば、選択のための結びつきも NSArrayController で用意されているはず。&lt;/p&gt;

&lt;h4&gt;NSArrayController の Selection Indexes&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/lXppKDsalBjL4Jyu2ohxSg?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TPT5JWqtfxI/AAAAAAAABkA/JEjV1liDPQM/s800/interfacebuilder_bindings_for_nstableview_selection.png" height="428" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;NSArrayController の「Bindings」パネルを見てみると、それらしい項目が見つかる。それが「Selection Indexes」だ。右のスクリーンショットは「Selection Indexes」を開いたところ。この例では結びつける先は「File's Owner」(この NIB をロードするオブジェクト)の selectionIndex というプロパティになっている。&lt;/p&gt;

&lt;p&gt;さて、これで選択のための結びつきを作れることはわかった。次の問題は選択を表現するデータの型は何かということ。NSTableView で選択された行は selectedRow メソッドで取り出すことができるが、この型は NSInteger で、これはつまり int だ。では、上記の結びつきの先となるプロパティは int (あるいは NSInteger) で良いのか？&lt;/p&gt;

&lt;p&gt;Interface Builder 上で Selection Indexes のところにマウスを持っていき、しばらくじっとしているとツールチップが表示される。そこには NSIndexSet のインスタンスが選択された行を指定すると書かれている。もっと詳しく知りたければ「&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CocoaBindingsRef/CocoaBindingsRef.html"&gt;Cocoa Bindings Reference&lt;/a&gt;」の NSArrayController &amp;gt; Controller Content Parameters Bindings &amp;gt; selectionIndexes を見ること。&lt;/p&gt;

&lt;h4&gt;サンプルコード&lt;/h4&gt;

&lt;p&gt;NSTableView の選択を Cocoa Bindings を使ってモデルと結びつける実例として、&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;MacBloggerGlass&lt;/a&gt; の環境設定パネルを実現するコードからの抜粋を示す。&lt;/p&gt;

&lt;p&gt;まず、モデルとしての NSIndexSet は PreferenceController オブジェクトに抱えさせている。プロパティ名は先のスクリーンショットにも出ていたように selectionIndex だ。&lt;/p&gt;

&lt;p&gt;PreferenceController の実装で selectionIndex にアクセスするのは(解放する dealloc を除いて) 3 ヶ所。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;プロパティとしてのアクセサ(readonly)&lt;/li&gt;
  &lt;li&gt;windowDidLoad 内でインスタンスの確保と初期化(空選択として)&lt;/li&gt;
  &lt;li&gt;ブログ一覧を取得した際に実行される blogListTicket:finishedWithFeed:error で User Defaults に保存された選択の復元&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NSIndexSet にはインスタンス生成用のクラスメソッドが用意されていて、空選択の場合には +indexSet を用いる。&lt;/p&gt;

&lt;p&gt;保存された選択の復元を行うコードを以下に示す。&lt;/p&gt;

&lt;p&gt;処理の内容は、(a) User Defaults から「選択された Blog ID」を取り出し、(b) ブログ一覧の各項目の ID と比較し、(c) 一致するものが見つかれば、(d) それを選択する、となっている。&lt;/p&gt;

&lt;pre class="prettyprint lang-m"&gt;
- (void)blogListTicket:(GDataServiceTicket *)ticket
      finishedWithFeed:(GDataFeedBlog *)aFeed
                 error:(NSError *)error
{
    &lt;span class="snip nocode"&gt;[...snip...]&lt;/span&gt;
    // selectedIndex
    NSString *blogID = [self selectedBlogID]; // ........................ (a)
    int index = 0;
    EntryBlog *entry;
    for (entry in feed.entries) {
        if ([entry.blogID compare:blogID] == NSOrderedSame) break; // ... (b)
        index++;
    }
    if (index &amp;lt; [feed.entries count]) { // ........................... (c)
        [self willChangeValueForKey:@&amp;quot;selectionIndex&amp;quot;];
        [selectionIndex initWithIndex:index]; // ........................ (d)
        [self didChangeValueForKey:@&amp;quot;selectionIndex&amp;quot;];
    } else {
        [selectionIndex init]; // empty NSIndexSet
    }
}
&lt;/pre&gt;

&lt;p&gt;(d) の前後にある willChangeValueForKey: と didChangeValueForKey は KVO の通知を送るためのお作法の 1 つ(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;」を参照)。&lt;/p&gt;

&lt;p&gt;今さらなんだけど、NSString って == で(文字列としての)比較はできないのな。Ruby や Python に慣れていると、つい == で比較してしまうよ。&lt;/p&gt;

&lt;h4 class="clear"&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSIndexSet_Class/Reference/Reference.html"&gt;NSIndexSet Class Reference&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CocoaBindingsRef/CocoaBindingsRef.html"&gt;Cocoa Bindings Reference&lt;/a&gt; (同上; &amp;quot;Bindings&amp;quot; には Cocoa のクラスごとにどのような結びつきが作れるかの完全なリストと説明がある)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html"&gt;Cocoa Bindings の使い方 #2 - Interface Builder 編&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-touch-cocoa-bindings.html"&gt;Cocoa Touch では Cocoa Bindings は使えない&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html"&gt;設定の保存にまつわる問題 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2816968499939204470?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2816968499939204470/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings-3.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2816968499939204470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2816968499939204470'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings-3.html' title='Cocoa Bindings の使い方 #3 - 選択を結びつける'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_wobSevGXOh4/TPT5JWqtfxI/AAAAAAAABkA/JEjV1liDPQM/s72-c/interfacebuilder_bindings_for_nstableview_selection.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-7272834390102197877</id><published>2010-11-29T22:40:00.003+09:00</published><updated>2010-12-12T18:03:26.278+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='ユーザ体験'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>設定の保存にまつわる問題 - 環境設定パネルを作る #2 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;設定を User Defaults に書き込むことは難しくない。設定値が NSString であるならなおさらだ。PreferenceController の実装部に以下のようなコードを埋め込むだけだ。&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *accountID = [accountField stringValue];
    [defaults setObject:accountID forKey:@&amp;quot;AccountID&amp;quot;];
&lt;/pre&gt;

&lt;p&gt;また参照する側 (AppController) では、以下のようにすれば良い。&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
    blogID = [blogIdField stringValue];
    if ([blogID length] == 0) {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        blogID = [defaults objectForKey:@&amp;quot;blogID&amp;quot;];
    }
&lt;/pre&gt;

&lt;p&gt;今回の記事の内容は、設定を保存するコードではなく、設定の保存を中心としたユーザ体験上の問題について。&lt;/p&gt;

&lt;p&gt;UI が提供するユーザ体験は、実際に動かしてみると、デザイン段階での「あら」があちこちに浮かび上がってくる。環境設定パネルでもアプリ本体とつないで見ると、いくつかの不備が見えてきた。今日、「Save」ボタンの機能を実装してみて、(そして動かしてみて)以下の 3 点について気付いた。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;パスワードを User Defaults に保存するのはマズい。&lt;/li&gt;
&lt;li&gt;「Save」ボタンを押した後のフィードバックがない。&lt;/li&gt;
&lt;li&gt;ブログのリストと選択の状態が保存、復元されない。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;設定を保存すると &lt;span class="path"&gt;~/Library/Preferences&lt;/span&gt; に置かれるファイルに書き込まれる。テキスト形式は言うに及ばず、バイナリ形式にしても、そこにパスワードを平文で書き込むのはマズい。パスワードを保存する場合には、キーチェーンと連携させるのが Mac アプリの作法だろう。とりあえず、パスワードは保存の対象から外しておく。キーチェーンとの連携は棚上げ。&lt;/p&gt;

&lt;p&gt;アプリを使う際にさまざまな処理の結果がユーザに対して適切にフィードバックされるかどうかはユーザ体験に大きく影響する。「ボタンを押す」というユーザのアクションに対してユーザの目に見える反応が何も起こらないと、ユーザに不安を感じさせることになる。ありがちなフィードバックとしては「完了しましたダイアログ」を出すことだが、これは使いどころを間違えると、ただわずらわしいだけになる。&lt;/p&gt;

&lt;p&gt;今回の環境設定パネルの場合、「Save」が押されたらパネル自体を消すというのが良いように思う。現状ではこのパネルの役割はアプリのメインウィンドウで表示するブログの選択のみだ。そこで「Save」ボタンが押されたのだから、保存処理が完了したのなら何も仕事は残っていない。だから、パネルを消せば処理が(成功のうちに)完了したことは十分に伝わるし、(わずらわしいダイアログのような)余分な UI も増えない。&lt;/p&gt;

&lt;p&gt;今、悩んでいるのは、最後の環境設定パネルを(設定を保存し)一度閉じて再び開いたときに、元の状態を復元させる仕組みだ。必要な情報はブログ(タイトル)の一覧と選択された項目へのインデックス。ここで、問題が 2 つほど出てくる。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/8gEO1q_hJGi4KUVta8n_CA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TPIoaUSMk7I/AAAAAAAABjI/l1El-Nl8sy0/s800/mbg_preferences_panel.png" height="387" width="374" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;1 つは、「ブログ(タイトル)の一覧の保存」に関係する。前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択&lt;/a&gt;」)で書いたように、環境設定パネル(右図)の表部分 (NSTableView) は、Cocoa Bindings によって(NSObjectController と NSArrayController を介して)モデルのプロパティに結びついている。表に出てくる項目は EntryBlog のプロパティであり、これを保存することは EntryBlog を保存することになる。Cocoa Bindings で結びついている以上、ブログのタイトルだけを NSStrings を抱えた NSMutableArray として保存しても意味はない。&lt;/p&gt;

&lt;p&gt;そう、1 つ目の問題は環境設定パネルにとってのモデルである FeedBlog と EntryBlog の保存(永続化)の仕組みが必要だ、というものだ。&lt;/p&gt;

&lt;div class="clear right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/vgBDKv7aU1_hspGZFZvIjw?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_wobSevGXOh4/TPOpn4InLAI/AAAAAAAABjo/hvqsa8rXvbM/s800/interfacebuilder_bindings_for_nstableview.png" height="272" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;2 つ目の問題は、選択の状態をどうやって復元するか、というもの。何度も書いているようにパネルの表(ビュー)は Cocoa Bindings によってモデルと結びついている。だから、その選択の状態も Cocoa Bindings でどこかの何かと結びつけなければならない。Interface Builder で NSTableView の Bindings パネルを開くと Selection Indexes という「結びつき」を設定する項目がある。これを使えば選択の状態を Cocoa Bindings による結びつきで伝えられそうなんだが、問題はそれを「どこ」の「何」というプロパティにするか。&lt;/p&gt;

&lt;p&gt;保存すべき情報という意味でこれもモデルの一部と言える。FeedBlog の一部にすることもできるが、抽象化の障壁に穴を空けてしまうようでイヤな感じだ。別のモデルオブジェクトとして独立させた方が良い。と言っても、情報自体は整数値 1 つなので、実際には PreferenceController に int のプロパティを増やすことになるか。&lt;/p&gt;

&lt;p&gt;次回は表項目の「選択」のための結びつきを Cocoa Bindings で作ることを試してみよう。&lt;/p&gt;

&lt;p&gt;まあ、ユーザ体験うんぬん以前に、ウィンドウの UI を考えるときに状態の保存と復元について考えていないのがそもそも悪い。経験不足だと切って捨ててしまえばそれまでだけど、Cocoa Bindings を使ってモデルとビューを同期させることに注意が引きつけられていたせいもあるだろう。&lt;/p&gt;

&lt;h4 class="clear"&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html"&gt;表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-7272834390102197877?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/7272834390102197877/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7272834390102197877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/7272834390102197877'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/macbloggerglass_29.html' title='設定の保存にまつわる問題 - 環境設定パネルを作る #2 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_wobSevGXOh4/TPIoaUSMk7I/AAAAAAAABjI/l1El-Nl8sy0/s72-c/mbg_preferences_panel.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-2139728963263009046</id><published>2010-11-28T22:40:00.003+09:00</published><updated>2010-12-01T23:38:58.639+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ヒレガス本'/><category scheme='http://www.blogger.com/atom/ns#' term='bloggerglass'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>表示するブログの選択 - 環境設定パネルを作る #1 (MacBloggerGlass)</title><content type='html'>&lt;p&gt;GAE 版 Blogger Glass では表示するブログの Blog ID を &lt;span class="path"&gt;config.yaml&lt;/span&gt; という設定ファイルで指定するようになっている。また、実行時にユーザが(Google アカウント)でログインすることで、ユーザごとに Blog ID を指定できるように「Settings」画面も用意した(→「&lt;a href="http://logrepo.blogspot.com/2010/10/gae-5.html"&gt;GAE アプリでユーザごとの設定を可能にする #5&lt;/a&gt;」)。&lt;/p&gt;

&lt;p&gt;Mac 版では表示するブログの選択は「環境設定パネル(Preferences)」を開いて行う。GAE 版のように Blog ID を直接指定するのはメンドウなので、Google アカウントの ID をパスワードを入力することで、そのアカウントで作ったブログの一覧から選べるようにしたい。&lt;/p&gt;

&lt;p&gt;今回は、そんな「環境設定パネル」でブログ一覧を取得し、表示するところまでを作り込む。&lt;/p&gt;

&lt;h4&gt;環境設定パネルを追加する&lt;/h4&gt;

&lt;p&gt;Cocoa アプリに対する「環境設定パネル」の設置手順は「&lt;a href="http://programmingmac.blogspot.com/2008/05/3rd-ed.html"&gt;ヒレガス本&lt;/a&gt;」の Chapter 12 〜 14 の内容となる。&lt;/p&gt;

&lt;p&gt;ざっくり手順を並べると以下のようになる。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;「環境設定パネル」を制御するコントローラを、NSWindowController を継承して作る。&lt;/li&gt;
&lt;li&gt;適当な場所(AppContoller みたいなものを作っていればそこ)に「環境設定パネル」を表示させるアクションを作る。&lt;/li&gt;
&lt;li&gt;Interface Builder でメインメニューのメニュー項目(「アプリ名」&amp;gt;「Preferences...」)に「環境設定パネル」を表示させるアクションを接続する。&lt;/li&gt;
&lt;li&gt;「環境設定パネル」用の NIB ファイルを作る(実際には XIB ファイル)。この NIB の File's Owner は「環境設定パネル」を制御するコントローラにする。&lt;/li&gt;
&lt;li&gt;Interface Builder でパネルを作る。&lt;/li&gt;
&lt;li&gt;「環境設定パネル」を制御するコントローラに、パネルで使うアクション等を作り込む。&lt;/li&gt;
&lt;li&gt;「環境設定パネル」の設定を User Defaults に書き込むようにする。&lt;/li&gt;
&lt;li&gt;User Defaults の値を参照するように、メインのアプリウィンドウのコードを変更する。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;今回の記事の内容は、このうちの 6. までとなっている。7. と 8. は次回に持ち越し。&lt;/p&gt;

&lt;h4&gt;デザイン (意匠)&lt;/h4&gt;

&lt;p&gt;環境設定パネルの外観と簡単な使い方を以下に示す。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;MacBloggerGlass ウィンドウデザイン&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;環境設定パネル&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/8gEO1q_hJGi4KUVta8n_CA?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TPIoaUSMk7I/AAAAAAAABjI/l1El-Nl8sy0/s800/mbg_preferences_panel.png" height="387" width="374" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;Google Account の ID (E-mailアドレス) とパスワードを入力し「Get Blog List」を押すと、下部の Blog for viewing にその Google アカウントで Blogger に作ったブログのタイトル一覧が現れる。アプリで表示させたいブログを選択し、「Save」ボタンを押して保存する。パネルを閉じるには左上のクローズボタンを押すか、ESC キーを叩く。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;デザイン (設計)&lt;/h4&gt;

&lt;p&gt;Google アカウントの ID とパスワードを入力させて、ブログの一覧を取得するという機能は、GData ライブラリのパッケージにふくまれている Blogger 用サンプルにほぼそのままの形で作り込まれている。それをのまま流用しても良かったが(実際、GData を使った Google Data API へのアクセスの部分は流用している)、ここでは &lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings&lt;/a&gt; を使ってみたい。つまり、「環境設定パネル」のコントローラ(PreferenceController クラスとした)を NSTableView のデータソースとするのではなく、モデルとなるオブジェクトを NSObjectController (あるいはその派生クラスたち)を介して、NSTableView と結びつけるようにしたい、ということ。&lt;/p&gt;

&lt;p&gt;後述するように、モデルは、ブログごとの情報を抱えるオブジェクトと、その配列を抱えるオブジェクトの二段構えになっている。これは、Google Data API を経由してユーザのブログ一覧を取得した際に、GData ライブラリが生成するオブジェクトの構成をそのまま写し取ったものだ。&lt;/p&gt;

&lt;pre&gt;(GData ライブラリによるブログ一覧)
+---------------+
| GDataFeedBlog |
|---------------|            +----------------+
|- entries    --|-----------&gt;| GDataEntryBlog |
|  ...          | 1        * |----------------|
|               |            |  ...           |
+---------------+            +----------------+
&lt;/pre&gt;

&lt;p&gt;GDataEntryBlog は個々のブログの ID やらタイトルやらといった情報を保持している。&lt;/p&gt;

&lt;p&gt;この二段構えのモデル構造は以前の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;」)でダミーとして作ったモデルの構造と同じだ。あのサンプルアプリでは、二段構えモデルに対応するため、Cocoa Bindings のためのコントローラも NSObjectController と NSArrayController の二段構えとした。その構成をそのまま、今回の「環境設定パネル」でも用いる。&lt;/p&gt;

&lt;h4&gt;実装&lt;/h4&gt;

&lt;h5&gt;モデル&lt;/h5&gt;

&lt;p&gt;モデルクラスは 2 つ。FeedBlog クラスは GData ライブラリが提供する GDataFeedBlog に対応するもので、EntryBlog クラスは同じく GDataEntryBlog に対応するものだ。どちらも対応する GData ライブラリのクラスを内部に抱える(has-a の関係)とともに、ビューで表示するための情報を抽出するためのクラスとして作った。抽出した情報(例: ブログのタイトル)は、主にビューから Cocoa Bindings の結びつきを介してアクセスするために用いる。&lt;/p&gt;

&lt;p&gt;どちらのクラスにも、対応する GData ライブラリのオブジェクトをセットするためのメソッドを用意していて、そこで情報の抽出も行っている。&lt;/p&gt;

&lt;h6&gt;FeedBlog&lt;/h6&gt;

&lt;pre class="prettyprint lang-m"&gt;
//
//  FeedBlog.h
//  MacBloggerGlass

#import &amp;lt;Cocoa/Cocoa.h&amp;gt;
@class GDataFeedBlog;

@interface FeedBlog : NSObject {
    NSMutableArray *entries;
    GDataFeedBlog *gDataFeed;
}

@property (readonly) NSMutableArray *entries;
@property (readwrite, retain) GDataFeedBlog *gDataFeed;

@end
&lt;/pre&gt;

&lt;pre class="prettyprint lang-m"&gt;
//
//  FeedBlog.m
//  MacBloggerGlass

#import &amp;quot;GData/GDataBlogger.h&amp;quot;
#import &amp;quot;FeedBlog.h&amp;quot;
#import &amp;quot;EntryBlog.h&amp;quot;

@implementation FeedBlog
- (id)init
{
    [super init];
    entries = [[NSMutableArray alloc] init];
    return self;
}

- (void)dealloc
{
    [gDataFeed release];
    [entries release];
    [super dealloc];
}

@synthesize entries;
@synthesize gDataFeed;
- (void)setGDataFeed:(GDataFeedBlog *)aFeed
{
    if (aFeed == gDataFeed) return;
    [gDataFeed release];
    gDataFeed = [aFeed retain];

    [entries removeAllObjects];

    EntryBlog *blog;
    GDataEntryBlog *entry;
    for (entry in [gDataFeed entries]) {
        blog = [[EntryBlog alloc] init];
        blog.gDataEntry = entry;        
        [entries addObject:blog];
    }
    NSLog(@&amp;quot;%d blogs was found.&amp;quot;, [entries count]);
}
@end
&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;setGDataFeed:&lt;/code&gt; で GDataFeedBlog の抱える配列から GDataEntryBlog の要素を取り出している &lt;code&gt;for (var in array) { ... }&lt;/code&gt; という書き方が Objective-C 2.0 から導入された高速列挙 (fast enumeration) だ。Ruby や Python ではお馴染みのもの。配列の添字でループし、&lt;code&gt;objectAtIndex:&lt;/code&gt; で配列にアクセスするよりもスッキリと書ける。array の部分には配列(NSArray)以外の集合(NSSet)や辞書(NSDictionary)も指定できる。詳しくは&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt; CHAPTER 08 の p.202 〜 206 あたりを参照のこと。&lt;/p&gt;

&lt;h6&gt;EntryBlog&lt;/h6&gt;

&lt;pre class="prettyprint lang-m"&gt;
//
//  EntryBlog.h
//  MacBloggerGlass

#import &amp;lt;Cocoa/Cocoa.h&amp;gt;
@class GDataEntryBlog;

@interface EntryBlog : NSObject {
    NSString *title;
    NSString *blogID;
    GDataEntryBlog *gDataEntry;
}

@property (readonly) NSString *title, *blogID;
@property (readwrite, retain) GDataEntryBlog *gDataEntry;

@end
&lt;/pre&gt;

&lt;pre class="prettyprint lang-m"&gt;
//
//  EntryBlog.m
//  MacBloggerGlass

#import &amp;quot;GData/GDataBlogger.h&amp;quot;
#import &amp;quot;EntryBlog.h&amp;quot;

@interface EntryBlog (PrivateMethods)
- (NSString *)extractBlogID:(NSString *)identifier;
@end


@implementation EntryBlog
- (void)dealloc
{
    [gDataEntry release];
    [blogID release];
    [title release];
    [super dealloc];
}

@synthesize title, blogID;
@synthesize gDataEntry;
- (void)setGDataEntry:(GDataEntryBlog *)aEntry
{
    if (aEntry == gDataEntry) return;
    [gDataEntry release];
    gDataEntry = [aEntry retain];

    title = [[[gDataEntry title] stringValue] retain];
    blogID = [[self extractBlogID:[gDataEntry identifier]] retain];
}
@end

@implementation EntryBlog (PrivateMethods)

#pragma mark -
#pragma mark PrivateMethods
- (NSString *)extractBlogID:(NSString *)identifier
{
    if (!identifier || [identifier length] == 0) return nil;

    // GDataEntryBlog.identifier is in the format
    // &amp;quot;tag:blogger.com,1999:user-&amp;lt;user id&amp;gt;.blog-&amp;lt;blog id&amp;gt;&amp;quot;
    NSRange rangeBlog = [identifier rangeOfString:@&amp;quot;blog-&amp;quot;];
    int startPos = rangeBlog.location + rangeBlog.length;

    return [identifier substringFromIndex:startPos];
}

@end
&lt;/pre&gt;

&lt;p&gt;&lt;span class="path"&gt;EntryBlog.m&lt;/span&gt; の方で &lt;code&gt;@interface EntryBlog (PrivateMethods) ... @end&lt;/code&gt; として宣言しているのは、このファイルからのみ参照するためのメソッドの定義だ。C でいう static 関数のようなもの。カテゴリの使い方の 1 つ。これも詳しくは&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt; CHAPTER 09 の p.227 〜 228 を参照のこと。&lt;/p&gt;

&lt;h5&gt;コントローラ&lt;/h5&gt;

&lt;div class="left"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/Po_WtXZzYZeuySDJOoVcxw?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TPIobPNCcYI/AAAAAAAABjM/RJBoyvkDXgQ/s288/mbg_ib_docs_for_preferences.png" height="288" width="270" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;先に述べたように Cocoa Bindings のためのコントローラには NSObjectController と NSArrayController の 2 つを組み合わせて使う。左のスクリーンショットで Feed Observer となっているものが NSObjectController (のインスタンス)、Array Observer となっているものが NSArrayController (のインスタンス) だ。インスタンス名を Observer としているのは、どちらも KVO (Key-Value Observing) のため(だけ)に使っているからだ。&lt;/p&gt;

&lt;h6 class="clear"&gt;PreferenceController&lt;/h6&gt;

&lt;p&gt;環境設定パネルを表示するためのクラスで、NSWindowController を継承している。&lt;/p&gt;

&lt;p&gt;GData を使ってブログ一覧を取得する部分は以前の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;記事一覧を取得する」)&lt;/a&gt;で示したものとほとんど同じなので省略する。&lt;/p&gt;

&lt;p&gt;以下に示すのは、ブログ一覧を取得するために GData ライブラリのサービスを呼び出した際に指定するコールバックメソッドだ。注意してほしいのは 102 と 104 行目。これは、KVO で キーに対応する変更通知を発行するための「作法」の 1 つ。103 行目で、&lt;code&gt;feed&lt;/code&gt; が保持するオブジェクト(前述の FeedBlog のインスタンス)が抱える配列の内容が変更される。そのことを、PreferenceController に(「feed」というキーで)結びついているオブジェクト(今回の場合、それは Feed Observer という名前の NSObjectController のインスタンス)に通知している。&lt;/p&gt;

&lt;pre class="prettyprint lang-m linenums:94"&gt;
- (void)blogListTicket:(GDataServiceTicket *)ticket
      finishedWithFeed:(GDataFeedBlog *)aFeed
                 error:(NSError *)error
{
    if (error) {
        NSLog(@&amp;quot;ERROR in fetching the blog list: %@&amp;quot;, error);
        return;
    }
    [self willChangeValueForKey:@&amp;quot;feed&amp;quot;];
    feed.gDataFeed = aFeed;
    [self didChangeValueForKey:@&amp;quot;feed&amp;quot;];
}
&lt;/pre&gt;

&lt;p&gt;監視するオブジェクトの変更通知を受け取った Feed Observer (NSObjectController) は、今度は自分自身に(「entries」というキーで)結びついているオブジェクト(Array Observer という名前の NSArrayController のインスタンス)に変更通知を送る。それを受け取った Array Observer は、NSTableView (の中の NSTableColumn) に変更通知を送る。NSTableView は KVC によって結びついたオブジェクトのプロパティ(EntryBlog の title)を取得して、表示を更新する。このようにして、モデルの変更が Cocoa Bindings によって結びついたビューに伝播してゆくのだ。&lt;/p&gt;

&lt;h4 class="clear"&gt;次の展開&lt;/h4&gt;

&lt;p&gt;ブログ一覧を取得して表示できるようになっているが、まだ肝心の「Save」ボタンが機能しない。当然、メインウィンドウ(アプリ本体)とも連携していない。現段階では環境設定のためのパネルが単独で動いているだけ。&lt;/p&gt;

&lt;p&gt;なので、次は「Save」ボタン(に対応するアクション)の実装と、アプリ本体とのつなぎ部分を実装することになる。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41pyKTm2fxL._SL160_.jpg" alt="Cocoa Programming for Mac OS X" title="Cocoa Programming for Mac OS X" width="121" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;Aaron Hillegass&lt;br /&gt;Addison-Wesley Professional ( 2008-05-15 )&lt;br /&gt;ISBN: 9780321503619&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;記事一覧を取得する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/gdata-objective-c-client-library.html"&gt;GData Objective-C Client Library を組み込む (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-2139728963263009046?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/2139728963263009046/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2139728963263009046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/2139728963263009046'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/macbloggerglass_28.html' title='表示するブログの選択 - 環境設定パネルを作る #1 (MacBloggerGlass)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_wobSevGXOh4/TPIoaUSMk7I/AAAAAAAABjI/l1El-Nl8sy0/s72-c/mbg_preferences_panel.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-8588044627349193787</id><published>2010-11-27T22:37:00.000+09:00</published><updated>2010-11-27T22:37:11.327+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cocoatouch'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Touch では Cocoa Bindings は使えない</title><content type='html'>&lt;p&gt;「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Interface Builder User Guide: Connections and Bindings&lt;/a&gt;」は「About Connections and Bindings」という節で始まっている。その冒頭では Mac OS X (と iOS) アプリで用いる「接続(connection)」の型についての説明がされている。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Interface Builder User Guide: Connections and Bindings&lt;/a&gt;」より)&lt;br&gt;
There are four fundamental types of connections you can create in a Mac OS X and iOS application:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Outlet connections&lt;/li&gt;
&lt;li&gt;Action connections (Mac OS X only)&lt;/li&gt;
&lt;li&gt;Event connections (iOS only)&lt;/li&gt;
&lt;li&gt;Bindings (Mac OS X only)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;action と event がそれぞれ、Mac OS X 専用、iOS 専用となっているのは良い。iOS アプリで action の代わりを務めるのが event だから。気になるのは最後の Bindings が Mac OS X 専用となっていること。そう、Cocoa Bindings は iOS アプリでは使えない技術なのだ(少なくとも今のところ)。&lt;/p&gt;

&lt;h4&gt;Interface Builder で Inspector を見てみる&lt;/h4&gt;

&lt;p&gt;前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html"&gt;Cocoa Bindings の使い方 #2&lt;/a&gt;」)で、Cocoa Bindings を設定するための Bindings パネルのスクリーンショットを載せた。このパネルは Interface Builder でインスタンスのさまざまな設定を行うための「Inspector」とツールの一部として表示されるものだ(「Inspector」は Interface Builder のメニューから「Tools」&amp;gt;「Inspector」で表示できる)。この「Inspector」の中には、他にも Attributes や Size を設定するためのパネルが並んでいる。&lt;/p&gt;

&lt;p&gt;この「Inspector」を Cocoa アプリの部品と Cocoa Touch の部品でそれぞれ表示させ、そこに並んでいるパネルの数と種類を比べてみた。改めて見ると、確かに違う。Cocoa の部品のパネルに比べて、Cocoa Touch の部品のそれは明らかに数が少ない。&lt;/p&gt;

&lt;table class="chart"&gt;
&lt;caption&gt;Interface Builder が Inspector に表示するパネル数の差&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;th&gt;Cocoa の Table View (NSTableView)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/xWV336bfsE5YNTn_bPuxnQ?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TPD6pDjsYwI/AAAAAAAABis/U1G9ro5mTns/s800/cocoa_info_panels_header.png" height="164" width="287" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;
    パネルは左から順に、Attributes、Effects、Size、Bindings、Connections、Identity となっている。
  &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;Cocoa Touch の Table View (UITableView)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/C_AxFeao4SvFSKxT4pgCJg?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_wobSevGXOh4/TPD6oXZjASI/AAAAAAAABio/MncDq-k0DO0/s800/cocoa_touch_info_panels_header.png" height="186" width="287" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class="description"&gt;
    パネルは左から順に、Attributes、Connections、Size、、Identity となっている。
  &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;設定するためのパネルがない以上、Cocoa Touch の部品では(つまり iOS アプリでは) Cocoa Bindings が使えないことは確かなようだ。&lt;/p&gt;

&lt;h4&gt;Cocoa アプリと Cocoa Touch アプリ&lt;/h4&gt;

&lt;p&gt;以前の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0&lt;/a&gt;」)で、Mac 版と iPhone アプリでなるべく設計と実装を共通化したいと書いた。そのためにも、ビュー (V) とモデル (M) の分離が明確になる Cocoa Bindings はおもしろそうだと調べ始めたんだが、Cocoa Touch で使えないとは思わなかった (´・ω・｀)。まあ、iPhone アプリのコントローラ (C) が少し大きくなる程度の差で済むと思うがね。&lt;/p&gt;

&lt;p&gt;この制約はどうしてだろう？ パフォーマンス？ メモリ？ KVC と KVO は iOS アプリでも使えるっぽいんだけどな(iOS Reference Library に両者の Programming Guide がある)。そう言えば、ガベージコレクションも iOS アプリでは使えないんだった。デバイスに差がある(あり過ぎる)以上、しかたがないんだろうな。&lt;/p&gt;

&lt;p&gt;どうでもいいことだけど garbage collection のカタカナ表記としては「ガベージコレクション」と「ガーベジコレクション」のどちらが適切か？ ググってみると前者は 130,000 件で後者が 67,100 件。ま、根拠にするには弱いけど、どちらかと言えば前者の方がポピュラーってことか。&lt;/p&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Interface Builder User Guide: Connections and Bindings&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-0-blogger-glass.html"&gt;欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html"&gt;Cocoa Bindings の使い方 #2 - Interface Builder 編&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-8588044627349193787?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/8588044627349193787/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-touch-cocoa-bindings.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8588044627349193787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/8588044627349193787'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-touch-cocoa-bindings.html' title='Cocoa Touch では Cocoa Bindings は使えない'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_wobSevGXOh4/TPD6pDjsYwI/AAAAAAAABis/U1G9ro5mTns/s72-c/cocoa_info_panels_header.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6038827868255026272</id><published>2010-11-27T07:33:00.002+09:00</published><updated>2010-11-27T21:01:23.702+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><title type='text'>twitter より (2010-11-26)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/8112996945494016"&gt;20:01&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;とりあえず、ミウラを手に入れるまではガンバロ。15億、貯めるのはキツいけど。ミウラでニュルをドライブするのが今回の目標だな。 → &lt;a href="http://www.gran-turismo.com/jp/news/d11077.html"&gt;http://www.gran-turismo.com/jp/news/d11077.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6038827868255026272?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6038827868255026272/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/twitter-2010-11-26.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6038827868255026272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6038827868255026272'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/twitter-2010-11-26.html' title='twitter より (2010-11-26)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-4592915955415467317</id><published>2010-11-26T23:23:00.002+09:00</published><updated>2010-11-26T23:23:23.038+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ヒレガス本'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Bindings の使い方 #2 - Interface Builder 編</title><content type='html'>&lt;p&gt;前回の記事(→「&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;」)では、Cocoa Bindings を使うことで、MVC のうち M (モデル) となるコードを書くだけでアプリが作れると書いた(標準部品とその振る舞いだけを使うアプリの場合)。ただし、その設定には Interface Builder に対する慣れが必要だ、とも書いた。&lt;/p&gt;

&lt;p&gt;今回は、Interface Builder を使って Cocoa Bindings を設定する手順について書く。ネタ元は公式ドキュメント(Mac OS X Reference Library)の「Interface Builder User Guide」にある「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Connections and Bindings&lt;/a&gt;」だ。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619"&gt;ヒレガス本&lt;/a&gt;の Chapter 8 でも、&lt;code&gt;NSTableView&lt;/code&gt; を &lt;code&gt;NSArrayController&lt;/code&gt; に結びつける手順が説明されている。サンプルアプリを作る手順をたどるハンズオンの資料としてはわかりやすいが、ツールについての説明が必要最小限にとどめられているため、自分でアプリを作ろうとすると情報が不足していることに気付く。上記のドキュメントはその足りない部分を補完してくれる。&lt;/p&gt;

&lt;h4&gt;「結びつき(Binding)」を作る&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/2Oa46sHpcNEZVYQsz1Oagg?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_wobSevGXOh4/TO-uYaMfgPI/AAAAAAAABiA/FYTKMeloSJE/s800/interfacebuilder_bindings_panel.png" height="563" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Connections and Bindings&lt;/a&gt;」の説明用に貼られているパネルのスクリーンショットは、(説明に使う)一部だけを開いてあるためダマされてしまうが、このパネルには似たような設定項目を持つセクションがいくつも並んでいる。&lt;/p&gt;

&lt;p&gt;たとえば、右のスクリーンショットは、前回の記事で紹介したサンプルアプリの設定をしたときのものだが、複数のセクション(「Availability」とか「Controller Content Parameters」とか)に、右向きの三角が付いた項目がいくつもあることがわかる(「Availability」に「Editable」、「Content Controller Parameter」には「Filter Predicate」や「Selection Indexes」)。この右向き三角を開くとそれぞれに「Content Array」に表示されているのと同様な設定(「Bind to:」、「Controller Key」や「Model KeyPath」など)がある。&lt;/p&gt;

&lt;p&gt;それぞれのセクションや項目がどういった「結びつき」を作るためのものなのかを知らないと、うっかり他の項目に「結びつき」を作ってしまうことになりかねない(前回のサンプルアプリでは実際そういう間違いをしてしまった)。これらの項目はオブジェクトのクラスによって変わるため、それぞれの項目がどういう振る舞いのためのものかは個別のクラスのリファレンスを参照するしかないようだ。一方で、それぞれの項目に対する設定のうち共通なものについては、「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Connections and Bindings&lt;/a&gt;」の Table 7-1 に「Common binding attributes」としてまとめられている。&lt;/p&gt;

&lt;p class="clear"&gt;どの項目であれ、最低限、設定しなければならないのは「Bind to:」と「Model Key Path」の 2 つ。右のスクリーンショットでは、それぞれ Feed Controller、entries となっているものだ(この例では結びつける相手が &lt;code&gt;NSObjectController&lt;/code&gt; のため「Controller Key」も指定している)。&lt;/p&gt;

&lt;div class="left"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/Khhe6o8AVSZtln1LmPrC8Q?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TO-1R5eQTwI/AAAAAAAABiQ/0GUeddqAROY/s288/interfacebuilder_document_window.png" height="288" width="270" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;「Bind to:」は文字通り、どのオブジェクトに結びつけるかを指定するもので、ドロップダウンリストを開くと Interface Builder のドキュメントウィンドウ(左のスクリーンショット)にあるオブジェクトの一覧が出る。先の例ではこの中の Feed Controller が選ばれている。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;前回の記事&lt;/a&gt;に書いたとおり、このサンプルアプリでは &lt;code&gt;NSArrayController&lt;/code&gt; が制御する配列(&lt;code&gt;NSMutableArray&lt;/code&gt;)は別のコントローラ(&lt;code&gt;NSObjectController&lt;/code&gt; のインスタンスで Feed Controller という名前にしてある)が制御するモデルオブジェクトが抱えるものだ。この配列に対する結びつきを指定しているのがこのパネルでの設定になる。&lt;/p&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/5IpL8Rs915_Hk_55R_dung?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TO-50AeXDgI/AAAAAAAABic/hfBkl2SlgN8/s800/interfacebuilder_attributes_panel.png" height="463" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;一方、Feed Controller 側と(配列を抱える)モデルオブジェクトとの結びつきも Interface Builder で設定している。右のスクリーンショットがその設定となる。ただ、こちらの設定は Cocoa Bindings とは異なり、&lt;code&gt;NSObjectController&lt;/code&gt; が直接モデルオブジェクトを持つための設定だ(と思う)。ただ、そのアクセスに KVC が使われている。&lt;/p&gt;

&lt;h4 class="clear"&gt;「結びつき」のパターン&lt;/h4&gt;

&lt;p&gt;「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Connections and Bindings&lt;/a&gt;」では、結びつきのタイプを以下の 3 つのパターンに分類している。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binding Directory to the Value (値に直接結びつける) [Figure 7-6]&lt;/li&gt;
&lt;li&gt;Binding Through an Intermediate Object (中間オブジェクトを介して結びつける)&lt;/li&gt;
&lt;li&gt;Binding to a Collection Objects (オブジェクトの集まりに結びつける) [Figure 7-9]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;「中間オブジェクト」パターンについては、さらに 2 つに分けている。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binding through another data object (別のデータオブジェクトを介して結びつける) [Figure 7-7]&lt;/li&gt;
&lt;li&gt;Binding through an object controller (別のコントローラを介して結びつける) [Figure 7-8]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;この分類にしたがえば、&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;前回&lt;/a&gt;のサンプルアプリは、「別のコントローラを介して結びつける」と「オブジェクトの集まりに結びつける」の複合方式ということになる。&lt;/p&gt;

&lt;h4&gt;まとめ&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;前回&lt;/a&gt;のサンプルアプリは&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619"&gt;ヒレガス本&lt;/a&gt;の手順を真似て、さらに「結びつき」を作るための試行錯誤を繰り返して作った。そうして動くモノができたものの、すっきりしない感じが残った。そのモヤモヤが「&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Connections and Bindings&lt;/a&gt;」を読むことで氷解した。とはいえ、最初にこのドキュメントに取り組んでいたとしても、すんなりと理解はできなかったに違いない。試行錯誤の後だからこその理解だと思う。&lt;/p&gt;

&lt;p&gt;馴染みの薄い概念がふくまれていると、ドキュメントを読んでも表面的な理解で終わってしまう。そこが難しいところだよね。&lt;/p&gt;

&lt;p&gt;ちなみに、この記事では「バインディング」「バインドする」とカタカナ表記で書くのが嫌で「結びつき」とか「結びつける」と書いてみた。Lisp 界隈で bind の訳語として定着している「束縛」を使わなかったのは、以前からなんというか語感が堅いと感じていたから。&lt;/p&gt;

&lt;p&gt;ま、自分では、脳内で「bind」に変換して読んでいるんだから、どっちでも良いんだがね。他の人はどう感じるかな (・ω・)？&lt;/p&gt;

&lt;h4 class="clear"&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41pyKTm2fxL._SL160_.jpg" alt="Cocoa Programming for Mac OS X" title="Cocoa Programming for Mac OS X" width="121" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;Aaron Hillegass&lt;br /&gt;Addison-Wesley Professional ( 2008-05-15 )&lt;br /&gt;ISBN: 9780321503619&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/ConnectionsandBindings/ConnectionsandBindings.html%23//apple_ref/doc/uid/TP40005344-CH7-SW1"&gt;Interface Builder User Guide: Connections and Bindings&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings.html"&gt;Cocoa Bindings の使い方&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-4592915955415467317?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/4592915955415467317/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4592915955415467317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/4592915955415467317'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html' title='Cocoa Bindings の使い方 #2 - Interface Builder 編'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_wobSevGXOh4/TO-uYaMfgPI/AAAAAAAABiA/FYTKMeloSJE/s72-c/interfacebuilder_bindings_panel.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-333676037872590035</id><published>2010-11-25T23:58:00.006+09:00</published><updated>2010-11-30T23:44:11.001+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ヒレガス本'/><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Cocoa Bindings の使い方</title><content type='html'>&lt;div class="postscript"&gt;
&lt;h5&gt;追記@2010-11-30&lt;/h5&gt;
&lt;p&gt;Cocoa Bindings の使い方については以下の後続記事も参照のこと。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-2-interface-builder.html"&gt;Cocoa Bindings の使い方 #2 - Interface Builder 編&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-bindings-3.html"&gt;Cocoa Bindings の使い方 #3 - 選択を結びつける&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/cocoa-touch-cocoa-bindings.html"&gt;Cocoa Touch では Cocoa Bindings は使えない&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;ブログの記事一覧を表示する&lt;/a&gt;ようなアプリでは、記事一覧のデータは、配列(のような何か)に蓄えることになるし、画面の表示は表形式(に似た何か)を使うことになるだろう。&lt;/p&gt;

&lt;p&gt;Cocoa アプリでこれをシンプルに実現すると、配列 (&lt;code&gt;NSMutableArray&lt;/code&gt;) に蓄えられたデータをテーブルビュー (&lt;code&gt;NSTableView&lt;/code&gt;) に表示するものになる。MVC アーキテクチャで言うなら、配列がモデル(正確には配列に蓄えられたオブジェクトがモデル)で、テーブルビューが(その名の通り)ビューになる。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NSTableView&lt;/code&gt; にデータを表示させる場合、二通りの方法がある。1 つは、&lt;code&gt;NSTableView&lt;/code&gt; のデータソースとなるクラスを定義する方法。具体的には、&lt;code&gt;AppController&lt;/code&gt; というようなクラスを定義し(MVC の C)、そこで以下の 3 つのメソッドを定義するというもの。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;- numberOfRowsInTableView:&lt;/li&gt;
&lt;li&gt;- tableView:objectValueForTableColumn:row:&lt;/li&gt;
&lt;li&gt;- tableView:setObjectValue:forTableColumn:row:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3 つ目のメソッドは表示のみのアプリなら必要ない。&lt;/p&gt;

&lt;p&gt;この方法の欠点は、&lt;code&gt;NSMutableArray&lt;/code&gt; の内容を &lt;code&gt;NSTableView&lt;/code&gt; における表示と同期させるコードは、どれも良く似ているものになることだ。&lt;/p&gt;

&lt;p&gt;良く似たものになるなら、その仕組みを共通化してフレームワークが提供してくれればありがたい。もちろん、そのためにはある種の「抽象化」が必要になり、利用に際しては一定の「作法」に従わなければならない。この「共通化された仕組み」が Cocoa Bindings であり、「抽象化」が KVC (Key-Value Coding)、「作法」が KVO (Key-Value Observing) ということになる。&lt;/p&gt;

&lt;h4&gt;サンプルアプリ&lt;/h4&gt;

&lt;div class="right"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/cNzhUoiDgdtcYFu-RCtYIg?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_wobSevGXOh4/TO5q5LFYd6I/AAAAAAAABhQ/TzrnipbGCxo/s288/cocoaapp.png" height="205" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;今回、サンプルとして作ったアプリのスクショがこれ(→)だ。機能的には(意匠的にも)&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619"&gt;ヒレガス本&lt;/a&gt;の Chapter 8 で作るもの(→ 「&lt;a href="http://logrepo.blogspot.com/2009/11/mvc-cocoa.html"&gt;MVC と Cocoaバインディング&lt;/a&gt;」参照)と同等だが、こちらは、コードとして書いたのはモデルとなる 2 つのクラスのみで、ビューは当然として、コントローラも Cocoa フレームワークの提供するものだけで作られている。&lt;/p&gt;

&lt;h5 class="clear"&gt;モデル&lt;/h5&gt;

&lt;p&gt;まずはモデルを構成する 2 つのクラスを示す。これら 2 つは、Atom 形式のフィードから生成することを想定したものだ。&lt;code&gt;Feed&lt;/code&gt; オブジェクトが &lt;code&gt;Entry&lt;/code&gt; オブジェクトの配列を抱えるようになっている。&lt;/p&gt;

&lt;h6&gt;Entry の定義&lt;/h6&gt;

&lt;pre class="prettyprint lang-m"&gt;
//  Entry.h
#import &amp;lt;Cocoa/Cocoa.h&amp;gt;

@interface Entry : NSObject {
    NSString *title;
    NSDate *updated;
}

@property (readwrite, retain) NSString *title;
@property (readwrite, retain) NSDate *updated;

@end
&lt;/pre&gt;

&lt;pre class="prettyprint lang-m"&gt;
//  Entry.m
#import &amp;quot;Entry.h&amp;quot;

@implementation Entry
- (id)init
{
    [super init];
    title = @&amp;quot;Hi!&amp;quot;;
    updated = [[NSDate date] retain];
    return self;
}

- (void)dealloc
{
    [updated release];
    [title release];
    [super dealloc];
}

@synthesize title, updated;

- (void)setTitle:(NSString *)newTitle
{
    if (newTitle == title) return;
    [title release];
    title = [newTitle retain];
    self.updated = [[NSDate date] retain];
}

@end
&lt;/pre&gt;

&lt;h6&gt;Feed の定義&lt;/h6&gt;

&lt;pre class="prettyprint lang-m"&gt;
//  Feed.h
#import &amp;lt;Cocoa/Cocoa.h&amp;gt;

@interface Feed : NSObject {
    NSString *title;
    NSDate *updated;
    NSMutableArray *entries;
    int count;
}

@property (readwrite, retain) NSString *title;
@property (readwrite, retain) NSDate *updated;
@property (readwrite, retain) NSMutableArray *entries;
@property (readwrite) int count;


@end
&lt;/pre&gt;

&lt;pre class="prettyprint lang-m"&gt;
//  Feed.m
#import &amp;quot;Feed.h&amp;quot;
#import &amp;quot;Entry.h&amp;quot;

@implementation Feed
- (id)init
{
    [super init];
    title = @&amp;quot;My Feed&amp;quot;;
    updated = [[NSDate date] retain];
    entries = [[NSMutableArray alloc] init];
    [entries addObject:[[Entry alloc] init]];
    count = [entries count];
    return self;
}

- (void)dealloc
{
    [entries release];
    [updated release];
    [title release];
    [super dealloc];
}

@synthesize title, updated, entries;

- (void)setEntries:(NSMutableArray *)newArray
{
    if (newArray == entries) return;
    [entries release];
    entries = [newArray retain];
    self.count = [entries count];
}

@synthesize count;
@end
&lt;/pre&gt;

&lt;h5&gt;コントローラ&lt;/h5&gt;

&lt;p&gt;コントローラには &lt;code&gt;NSObjectController&lt;/code&gt; と &lt;code&gt;NSArrayController&lt;/code&gt; を 1 つずつ使っている。これらのコントローラは Interface Builder から NIB (今は XIB)ファイルの中にインスタンス化し、設定を変更しているだけだ。コードは一切、書いていない。&lt;/p&gt;

&lt;p&gt;これらコントローラの設定で肝となるのは、それぞれのコントローラが保持することになるモデルクラスとの対応づけだ。&lt;code&gt;NSObjectController&lt;/code&gt; は 上述の&lt;code&gt;Feed&lt;/code&gt; クラスのオブジェクトを保持する。&lt;code&gt;NSArrayController&lt;/code&gt; は &lt;code&gt;Entry&lt;/code&gt; クラスのオブジェクトを生成し(Add ボタンのアクション)、消滅させる(Delete ボタンのアクション)。ただし、&lt;code&gt;Entry&lt;/code&gt; オブジェクトたちを保持するのは &lt;code&gt;Feed&lt;/code&gt; が抱えた配列だ。このため、&lt;code&gt;NSArrayController&lt;/code&gt; は &lt;code&gt;Feed&lt;/code&gt; オブジェクトに対して要素となる &lt;code&gt;Entry&lt;/code&gt; オブジェクトの追加、編集、削除を指示する必要がある。このとき用いられるのが Cocoa Bindings だ。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NSArrayController&lt;/code&gt; は配列を抱えた &lt;code&gt;NSObjectController&lt;/code&gt; にバインドされることになる。&lt;/p&gt;

&lt;h5&gt;ビュー&lt;/h5&gt;

&lt;p&gt;スクショを見ればわかるように、いくつかのテキストフィールド(いずれも編集不可にしてある)とテーブルビュー(タイトル欄のみ編集可)、および 2 つのボタンから構成されている。いずれも Cocoa フレームワーク標準のビューたちだ。&lt;/p&gt;

&lt;p&gt;テキストフィールドおよびテーブルビュー(のセル)は、Cocoa Bindings によって、2 つのコントローラのプロパティを経由してモデルと結びつけられている。このため、モデル(のプロパティ)が変更されれば、KVO の通知により自動的に変更がビューに反映されることになり、ビューによる変更はコントローラを経由してモデルに反映されるようになっている。&lt;/p&gt;

&lt;h4&gt;まとめ&lt;/h4&gt;

&lt;p&gt;MVC のうちモデルとなるクラスを書くだけで後は、Interface Builder の操作でアプリが作れてしまう。標準の部品(ビューとコントローラ)を使うアプリであれば簡単にできる。ただし、Interface Builder を使って Cocoa Bindings の設定するのには、ちょっと慣れが必要だ。似たような設定項目が多く、設定する場所を間違えたら、まったく動かない。今日もそれでかなり苦労した。&lt;/p&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41pyKTm2fxL._SL160_.jpg" alt="Cocoa Programming for Mac OS X" title="Cocoa Programming for Mac OS X" width="121" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0321503619/mnbi-22/ref=nosim" target="_blank"&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;Aaron Hillegass&lt;br /&gt;Addison-Wesley Professional ( 2008-05-15 )&lt;br /&gt;ISBN: 9780321503619&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=0321503619" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaBindings/CocoaBindings.html"&gt;Cocoa Bindings Programming Topics&lt;/a&gt; (Mac OS X Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;記事一覧を取得する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2009/11/mvc-cocoa.html"&gt;MVC と Cocoaバインディング&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-333676037872590035?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/333676037872590035/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/333676037872590035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/333676037872590035'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/cocoa-bindings.html' title='Cocoa Bindings の使い方'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_wobSevGXOh4/TO5q5LFYd6I/AAAAAAAABhQ/TzrnipbGCxo/s72-c/cocoaapp.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-6710877451723107800</id><published>2010-11-25T07:34:00.002+09:00</published><updated>2010-11-25T10:47:58.616+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='appletv'/><category scheme='http://www.blogger.com/atom/ns#' term='3. twitterより'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><title type='text'>twitter より (2010-11-24)</title><content type='html'>&lt;ul&gt;&lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/7333491939217409"&gt;16:23&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;そうか。何気なくアップデートしたけど、&amp;#xf8fftv は iOS デバイスでありながら母艦を必要としないんだ。 → &lt;a href="http://ipodtouchlab.com/2010/11/apple-tv-os41.html"&gt;http://ipodtouchlab.com/2010/11/apple-tv-os41.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;li class="twtr2src"&gt;&lt;span class="twtr2src_time"&gt;&lt;a href="http://twitter.com/mnbi/statuses/7335412745895936"&gt;16:31&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;span class="twtr2src_text"&gt;インタラクティブっていうのはデジタルコンテンツの得意とするところだものな。電子書籍もこっちの方向に進むかね。ただ、子どもに使わせるなら今の iPad はちょっと重すぎるだろう。 → &lt;a href="http://bit.ly/gMJsry"&gt;http://bit.ly/gMJsry&lt;/a&gt;&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt;&lt;!-- You can remove this line. --&gt; Powered by &lt;a href="http://twtr2src.ogaoga.org/users/mnbi"&gt;twtr2src&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-6710877451723107800?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/6710877451723107800/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/twitter-2010-11-24.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6710877451723107800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/6710877451723107800'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/twitter-2010-11-24.html' title='twitter より (2010-11-24)'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-1824916590222789299</id><published>2010-11-24T22:34:00.003+09:00</published><updated>2010-11-24T22:48:12.035+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='2. アプリの断片'/><category scheme='http://www.blogger.com/atom/ns#' term='1. Macをプログラムする'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>日付文字列の変換 - RFC3339 から NSDate へ</title><content type='html'>&lt;h4&gt;実装&lt;/h4&gt;

&lt;p&gt;まずは、今回作った Objective-C による実装から示す。関数の機能は単純で、RFC3399 形式で表現された日時を元に &lt;code&gt;NSDate&lt;/code&gt; のオブジェクトを生成する。関数内で &lt;code&gt;NSDate&lt;/code&gt; オブジェクトを &lt;code&gt;alloc&lt;/code&gt; しているが、返す前に &lt;code&gt;autorelease&lt;/code&gt; しているため、&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt; (p.96) で言う「一時的なオブジェクト」となっている。また、関数を呼び出す側で自動解放プールを用意する必要がある。通常の Cocoa アプリの場合は、&lt;code&gt;NSApplication&lt;/code&gt; がイベントループの管理とともに自動解放プールも用意してくれる(→ 「&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt;」p.98)。&lt;/p&gt;

&lt;div class="code-from-gist"&gt;
&lt;script src="https://gist.github.com/712238.js?file=convertDate.m"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;div&gt;-&amp;gt; &lt;a href="https://gist.github.com/712238"&gt;convertDate.m&lt;/a&gt;&lt;/div&gt;&lt;/noscript&gt;
&lt;/div&gt;

&lt;p&gt;この実装では NSString の機能のうち、以下のメソッドを多用している。&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;aSet&lt;/code&gt; で渡された文字集合中の文字が最初に見つかった場所を返す。&lt;/dd&gt;
&lt;dt&gt;- (NSString*)substringToIndex:(NSUInteger)aIndex&lt;/dt&gt;
&lt;dd&gt;先頭から位置 &lt;code&gt;aIndex&lt;/code&gt; までの部分文字列を返す。&lt;/dd&gt;
&lt;dt&gt;- (NSString*)substringWithRange:(NSRange)aRange&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;aRange.location&lt;/code&gt; を開始位置とし、&lt;code&gt;aRange.length&lt;/code&gt; を長さとする部分文字列を返す。&lt;/dd&gt;
&lt;dt&gt;- (NSString*)substringFromIndex:(NSUInteger)&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;aIndex&lt;/code&gt; を開始位置としする末尾までの部分文字列を返す。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;上記の &lt;code&gt;substring...&lt;/code&gt;の戻り値はいずれも一時的なオブジェクトになっている。&lt;/p&gt;

&lt;h4&gt;参照仕様&lt;/h4&gt;

&lt;p&gt;GData API の &lt;a href="http://code.google.com/apis/gdata/docs/2.0/reference.html"&gt;Protocl Reference&lt;/a&gt; によれば、各種日付には &lt;a href="http://www.faqs.org/rfcs/rfc3339.html"&gt;RFC3339 フォーマット&lt;/a&gt;が使われる、とある。これは GData API が使うデータフォーマット(Atom 形式; &lt;a href="http://www.faqs.org/rfcs/rfc4287.html"&gt;RFC 4287&lt;/a&gt; で定義されている)の仕様によるものだ。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;(「&lt;a href="http://www.faqs.org/rfcs/rfc4287.html"&gt;RFC 4287&lt;/a&gt;」より)&lt;br&gt;
A Date construct is an element whose content MUST conform to the
"date-time" production in [&lt;a href="http://www.faqs.org/rfcs/rfc3339.html"&gt;RFC3339&lt;/a&gt;].  In addition, an uppercase "T"
character MUST be used to separate date and time, and an uppercase
"Z" character MUST be present in the absence of a numeric time zone
offset.&lt;/p&gt;
&lt;p&gt;&lt;span class="snip"&gt;[...snip...]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Example Date constructs:&lt;br&gt;
&amp;lt;updated&amp;gt;2003-12-13T18:30:02Z&amp;lt;/updated&amp;gt;&lt;br&gt;
&amp;lt;updated&amp;gt;2003-12-13T18:30:02.25Z&amp;lt;/updated&amp;gt;&lt;br&gt;
&amp;lt;updated&amp;gt;2003-12-13T18:30:02+01:00&amp;lt;/updated&amp;gt;&lt;br&gt;
&amp;lt;updated&amp;gt;2003-12-13T18:30:02.25+01:00&amp;lt;/updated&amp;gt;&lt;br&gt;
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;上記の中で例として挙げられている 4 つのパターンを見れば書式のおよそは理解できる。&lt;/p&gt;

&lt;p&gt;より詳細な 書式の定義は &lt;a href="http://www.faqs.org/rfcs/rfc3339.html"&gt;RFC 3339&lt;/a&gt; にある。&lt;/p&gt;

&lt;pre&gt;
date-fullyear   = 4DIGIT
date-month      = 2DIGIT  ; 01-12
date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
                          ; month/year
time-hour       = 2DIGIT  ; 00-23
time-minute     = 2DIGIT  ; 00-59
time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second
                          ; rules
time-secfrac    = &amp;quot;.&amp;quot; 1*DIGIT
time-numoffset  = (&amp;quot;+&amp;quot; / &amp;quot;-&amp;quot;) time-hour &amp;quot;:&amp;quot; time-minute
time-offset     = &amp;quot;Z&amp;quot; / time-numoffset

partial-time    = time-hour &amp;quot;:&amp;quot; time-minute &amp;quot;:&amp;quot; time-second
                  [time-secfrac]
full-date       = date-fullyear &amp;quot;-&amp;quot; date-month &amp;quot;-&amp;quot; date-mday
full-time       = partial-time time-offset

date-time       = full-date &amp;quot;T&amp;quot; full-time
&lt;/pre&gt;

&lt;p&gt;この定義から、日時の表現は「日付」と「時刻」に二分され、その区切りが文字「T」であること、また「時刻」は「時刻本体」と「時差」に分かれることが読みとれる。&lt;/p&gt;

&lt;p&gt;「時差」については、時刻による差分で表現したもの(「+09:00」や「-5:00」など)か、あるいは文字「Z」を用いる(時差として文字「Z」を指定した場合、その時刻は UTC を意味する)。&lt;/p&gt;

&lt;p&gt;さらに「時刻本体」の表現については、「時」「分」「秒」をそれぞれ 2 桁の十進数で表現する(1 桁の場合は先頭に「0」を付加する)。また「秒」については 1 秒以下を小数表現で「秒」に付けても良い、となっている。&lt;/p&gt;

&lt;h4&gt;GData API での時刻の取扱い&lt;/h4&gt;

&lt;p&gt;実際に GData API が返すフィードを見ると、&lt;code&gt;updated&lt;/code&gt; 要素等は以下のようになっていて、確かに上記の書式に従っていることがわかる。&lt;/p&gt;

&lt;pre class="prettyprint lang-xml"&gt;
&amp;lt;published&amp;gt;2010-10-03T21:18:00.000+09:00&amp;lt;/published&amp;gt;
&amp;lt;updated&amp;gt;2010-10-03T21:18:00.840+09:00&amp;lt;/updated&amp;gt;
&lt;/pre&gt;

&lt;p&gt;一方、GData Objective-C Library が返す時刻データは &lt;code&gt;GDataDateTime&lt;/code&gt; クラスのインスタンスになっていて、そこから文字列データを取り出すと RFC3339 フォーマットの文字列となる。ただし、それを作る部分は以下のようになっているため、「秒数」の小数点以下が無視されている(GData Objective-C Client ver. 1.10.0)。これは、Cocoa の NSDateComponents が小数点以下の秒数をサポートしていないことによるものだと思われる(以下のコード中の &lt;code&gt;dateComponents&lt;/code&gt; は NSDateComponents のインスタンスを保持している)。&lt;/p&gt;

&lt;pre class="prettyprint lang-m linenums:172"&gt;
- (NSString *)stringValue {
  return [self RFC3339String];
}

- (NSString *)RFC3339String {
  NSDateComponents *dateComponents = [self dateComponents];
  NSInteger offset = [self offsetSeconds];

  NSString *timeString = @&amp;quot;&amp;quot;; // timeString like &amp;quot;T15:10:46-08:00&amp;quot;

  if ([self hasTime]) {

    NSString *timeOffsetString; // timeOffsetString like &amp;quot;-08:00&amp;quot;

    if ([self isUniversalTime]) {
     timeOffsetString = @&amp;quot;Z&amp;quot;;
    } else if (offset == NSUndefinedDateComponent) {
      // unknown offset is rendered as -00:00 per
      // http://www.ietf.org/rfc/rfc3339.txt section 4.3
      timeOffsetString = @&amp;quot;-00:00&amp;quot;;
    } else {
      NSString *sign = @&amp;quot;+&amp;quot;;
      if (offset &amp;lt; 0) {
        sign = @&amp;quot;-&amp;quot;;
        offset = -offset;
      }
      timeOffsetString = [NSString stringWithFormat:@&amp;quot;%@%02ld:%02ld&amp;quot;,
        sign, (long)(offset/(60*60)) % 24, (long)(offset / 60) % 60];
    }
    timeString = [NSString stringWithFormat:@&amp;quot;T%02ld:%02ld:%0l2d%@&amp;quot;,
      (long)[dateComponents hour], (long)[dateComponents minute],
      (long)[dateComponents second], timeOffsetString];
  }

  // full dateString like &amp;quot;2006-11-17T15:10:46-08:00&amp;quot;
  NSString *dateString = [NSString stringWithFormat:@&amp;quot;%04ld-%02ld-%02ld%@&amp;quot;,
    (long)[dateComponents year], (long)[dateComponents month],
    (long)[dateComponents day], timeString];

  return dateString;
}
&lt;/pre&gt;

&lt;h4&gt;おまけ&lt;/h4&gt;

&lt;p&gt;最初に挙げたプログラムを Xcode で Command Line Tool としてビルドし、実行するとコンソールには以下のように出力される。水平線で区切られた 4 行が、&lt;code&gt;testGetDateObject&lt;/code&gt; の一回の呼び出しによるもので、最初が引数として与えられた RFC3339 形式の日時文字列、2 行目が &lt;code&gt;getDateObject&lt;/code&gt; による変換結果(の &lt;code&gt;NSDate&lt;/code&gt; のインスタンス)、3 行目がそのインスタンスをデフォルトのタイムゾーン(ウチではもちろん JST)で評価した結果、最後の 4 行目は同インスタンスを UTC で評価した結果となっている。&lt;/p&gt;

&lt;pre class="terminal"&gt;
2010-11-24 22:06:14.819 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.821 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56Z
2010-11-24 22:06:14.824 ExpApp[24929:a0b] NSDate object: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.824 ExpApp[24929:a0b] Default TZ: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.825 ExpApp[24929:a0b] UTC: 2010-11-22 12:34:56 +0000
2010-11-24 22:06:14.825 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.825 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22t12:34:56z
2010-11-24 22:06:14.826 ExpApp[24929:a0b] NSDate object: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.826 ExpApp[24929:a0b] Default TZ: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.826 ExpApp[24929:a0b] UTC: 2010-11-22 12:34:56 +0000
2010-11-24 22:06:14.827 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.827 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56.123Z
2010-11-24 22:06:14.827 ExpApp[24929:a0b] NSDate object: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.828 ExpApp[24929:a0b] Default TZ: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.828 ExpApp[24929:a0b] UTC: 2010-11-22 12:34:56 +0000
2010-11-24 22:06:14.828 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.829 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22t12:34:56.123z
2010-11-24 22:06:14.829 ExpApp[24929:a0b] NSDate object: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.829 ExpApp[24929:a0b] Default TZ: 2010-11-22 21:34:56 +0900
2010-11-24 22:06:14.830 ExpApp[24929:a0b] UTC: 2010-11-22 12:34:56 +0000
2010-11-24 22:06:14.830 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.830 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56+09:00
2010-11-24 22:06:14.831 ExpApp[24929:a0b] NSDate object: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.831 ExpApp[24929:a0b] Default TZ: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.832 ExpApp[24929:a0b] UTC: 2010-11-22 03:34:56 +0000
2010-11-24 22:06:14.832 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.832 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22t12:34:56+09:00
2010-11-24 22:06:14.833 ExpApp[24929:a0b] NSDate object: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.833 ExpApp[24929:a0b] Default TZ: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.834 ExpApp[24929:a0b] UTC: 2010-11-22 03:34:56 +0000
2010-11-24 22:06:14.834 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.834 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56.123+09:00
2010-11-24 22:06:14.835 ExpApp[24929:a0b] NSDate object: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.836 ExpApp[24929:a0b] Default TZ: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.836 ExpApp[24929:a0b] UTC: 2010-11-22 03:34:56 +0000
2010-11-24 22:06:14.836 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.837 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22t12.34.56.123+09:00
2010-11-24 22:06:14.837 ExpApp[24929:a0b] NSDate object: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.838 ExpApp[24929:a0b] Default TZ: 2010-11-22 12:34:56 +0900
2010-11-24 22:06:14.838 ExpApp[24929:a0b] UTC: 2010-11-22 03:34:56 +0000
2010-11-24 22:06:14.838 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.839 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56-05:00
2010-11-24 22:06:14.839 ExpApp[24929:a0b] NSDate object: 2010-11-23 02:34:56 +0900
2010-11-24 22:06:14.839 ExpApp[24929:a0b] Default TZ: 2010-11-23 02:34:56 +0900
2010-11-24 22:06:14.840 ExpApp[24929:a0b] UTC: 2010-11-22 17:34:56 +0000
2010-11-24 22:06:14.840 ExpApp[24929:a0b] ----------------------------------------
2010-11-24 22:06:14.840 ExpApp[24929:a0b] RFC3339 Format: 2010-11-22T12:34:56.123-05:00
2010-11-24 22:06:14.841 ExpApp[24929:a0b] NSDate object: 2010-11-23 02:34:56 +0900
2010-11-24 22:06:14.841 ExpApp[24929:a0b] Default TZ: 2010-11-23 02:34:56 +0900
2010-11-24 22:06:14.842 ExpApp[24929:a0b] UTC: 2010-11-22 17:34:56 +0000
&lt;/pre&gt;

&lt;h4&gt;参考文献&lt;/h4&gt;
&lt;div class="mm-middle" style="margin-bottom:0px;"&gt;&lt;div class="mm-image" style="float:left;"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41uQ1Wld8PL._SL160_.jpg" alt="詳解 Objective-C 2.0" title="詳解 Objective-C 2.0" width="127" height="160" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-content" style="float:left;margin-left:15px;line-height:120%"&gt;&lt;div class="mm-title" style="line-height:120%"&gt;&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797346809/mnbi-22/ref=nosim" target="_blank"&gt;詳解 Objective-C 2.0&lt;/a&gt;&lt;/div&gt;&lt;div class="mm-detail" style="margin-top:10px;"&gt;荻原 剛志&lt;br /&gt;ソフトバンククリエイティブ ( 2008-05-28 )&lt;br /&gt;ISBN: 9784797346800&lt;br /&gt;&lt;div style="margin:7px 0px"&gt;&lt;a href="http://mediamarker.net/u/mnbi/?asin=4797346809" target="_blank"&gt;物読みファイル (別館)で詳細を見る&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align:right;font-size:7pt;font-family:verdana"&gt;&lt;a href="http://mediamarker.net/" target="_blank"&gt;MediaMarker&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="clear:left"&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;関連リンク&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/gdata/docs/2.0/reference.html"&gt;Protocl Reference - Google Data Protocol&lt;/a&gt; (公式ドキュメントより)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.faqs.org/rfcs/rfc3339.html"&gt;RFC 3339 - Date and Time on the Internet: Timestamps&lt;/a&gt; (faqs.org)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.faqs.org/rfcs/rfc4287.html"&gt;RFC 4287 - The Atom Syndication Format&lt;/a&gt; (同上)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://netarrows.edoblog.net/Entry/29/"&gt;Atom の日付書式は RFC3339準拠。&lt;/a&gt; (寝太郎の江戸風呂。ね！)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDateComponents_Class/Reference/Reference.html"&gt;NSDateComponents Class Reference&lt;/a&gt; (iOS Reference Library)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;関連記事&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/macbloggerglass.html"&gt;記事一覧を取得する (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logrepo.blogspot.com/2010/11/gdata-objective-c-client-library.html"&gt;GData Objective-C Client Library を組み込む (MacBloggerGlass)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmingmac.blogspot.com/2008/12/20.html"&gt;荻原(2.0)本&lt;/a&gt; (Macをプログラムする; 目次を写してある)&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8445416630012754496-1824916590222789299?l=logrepo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://logrepo.blogspot.com/feeds/1824916590222789299/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://logrepo.blogspot.com/2010/11/rfc3339-nsdate.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1824916590222789299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8445416630012754496/posts/default/1824916590222789299'/><link rel='alternate' type='text/html' href='http://logrepo.blogspot.com/2010/11/rfc3339-nsdate.html' title='日付文字列の変換 - RFC3339 から NSDate へ'/><author><name>mnbi</name><uri>http://www.blogger.com/profile/15462639906558181840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_wobSevGXOh4/SxnQilFBULI/AAAAAAAAAyY/DHGHibuBfOY/S220/mnbi.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8445416630012754496.post-5324185731348756364</id><published>2010-11-24T07:33:00.003+09:00</published><updated>2010-11-24T16:24:54.798+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme
