2011年10月24日

ハマスタを貶すなら日没を待て 〜日没サスペンデッドにつき再試合〜

続けてもう少し書く。

○京急+ミツウロコ等横浜系企業連合に勝ち目はあったのか?

多分、万に一つの勝ち目も無かったかと。先般より私は「NPB=秘密クラブ」説を唱えておりますが、球団売却およびその承認にはその性質が諸に出ます。まあ、どのような場であれ、既存プレイヤーに対する事前の根回しは大変に重要であり、その中から自らの後ろ盾になってくれそうな奴を見つけておくというのは非常に重要です。今回いろいろあったとはいえ、ある程度DeNAとTBSおよびオーナー会議も絡めて交渉の道筋が立った後での挙手というのは、どう見積もっても不利なんですよね。去年も結局話は流れたとはいえ、ノジマが参入しようとして素気無くされたのも記憶に新しいところ。

また、企業連合すなわちJVという形で参入する場合、球団会社をどのように構成するつもりなのかが一社のみによる単独保有の場合以上に問題になるんじゃないでしょうか。まあ、合弁会社として各企業からの出向社員・役員だらけで構成することもできますが、出資比率や株主構成の変更にもいちいちうるさく言ってくるNPBの監視の下、続けて行くのは容易ではないはずです。そこをもう球団企業単体でも足腰強くやって行ける経営を目指してみたいな絵図をハッタリ半分でもいいので描けていたならあるいはですが、まあ、無理筋だなと。

○無料招待席くらい、どこにでもある話じゃんね?

地域密着のためにと打った施策が時代背景の変遷によって実態にそぐわなくなる事例というのは良くある話で、これなんかもそうなんだよねと言ってしまえばそれまでなんですが。

http://blog.livedoor.jp/tbi38/archives/4436036.html
変革!! Baystars blog : 知ってほしい。ハマスタが抱える事実

横浜スタジアムは、横浜公園が国有地であること、且つ都市公園法によりプロ野球専用の球場を建設することが出来ないことから、第3セクターとして設立された㈱横浜スタジアムが球場建設後に横浜市に無料で寄付したという経緯があります。
この寄付に対する見返りとして横浜市は、『公園施設の寄付に関する契約』をスタジアム側と締結。株式会社は球場施設のプロ野球興行開催の優先的使用の許可にはじまり、売店経営・広告物掲出・放映権等あらゆる権利を『45年』間に亘って握り続けているのです。
他方、㈱横浜スタジアムは球場建設費充当のため1口250万円の株を、『45年』間プロ野球公式戦バックネット裏特別席の無料優先権を付与する形で販売。すると20億円の資金が集まったそうです。
単純計算すると無料席は800席存在、過去の記事では設立後の増資分を含めると1200席あるとも言われます。
文春が言うところの『永久シート』を含め、広告・物販収入が入らない本拠地使用球団が収入源の頼みの綱の入場料のうち、1000席以上をゼロで計算しなければならないというのは球団経営を逼迫させる要因にしかなりません。

たかだか1000席程度をタダ席扱いしなければいかんのがそんなに大事なら、収容人員4万人のビッグスワンの優に半分以上をタダ券で集めたとすらうたわれるアルビレックス新潟の立場は一体どうなるんでしょう。しかも当時は指定管理者指名もまだない「間借り」状態で条件は同じだったはずですし。

だいたい、今の横浜スタジアムの空席って1000席どころの話じゃないじゃん。

○というか

この横浜スタジアム建設時の施策をまるで悪代官のタクラミのような扱いで報じられるってつまり、地域のためによかれと思って行う取引がいつ爆弾として扱われるか分からない、って言ってるようなものなわけで。これじゃ地域密着なんておっそろしくて口にできないって話じゃないかなあ。この程度の融通の話、ホントに他球場にはないの?何がそんなに悪い話なの?これ。コレ読んで皆さん「けしからん!」ってお怒りなんですか?どこがよろしくないんでしょうか。

そしてこれは何度でも言いますが、そんっなに球場会社がおいしいところ握ってるなら尚の事ここの株買って支配権を握りましょうよって話にしかならないと思うんですけど。同業者さんなんだから話もしやすいでしょう、フジさんとテレ朝さんがそれぞれ持ってる5.74%を買い取るところから始めてみたっていいんじゃないでしょうか。ねえ?

○もう椅子取りゲームは終わった

横浜を出てだいたいどこへ行くのか、アテを考えて言ってらっしゃるのか不思議な方が大勢ですが、楽天参入時に「最後の椅子」として取り上げられたのが仙台なのであって、あとはもう、NPBの本拠地としてキャパが保てるかどうか疑わしい場所ばかりですよ。

NPB、そしてMLBでも同じような傾向なんですが、プロ野球球団本拠地として選ばれているのはどこもだいたい「100万人以上」の都市・都市圏ばかりなんです。そのくらいの規模が無いと、平日三連戦の興行も含めた観客動員で安定した数字を見込めない。そして球場への安定したアクセスを確保するための交通網を整備できる余力が保てない。1,2週に一度のホームゲーム開催で済むサッカーの試合とは要求される都市機能レベルが根本的に異なるんです。これを日本で供給できる都市圏って、札仙広福レベルがギリギリ。それ以下は形の上では100万人居るけれども、といったようなところばかり。

それでなくても新潟は独立リーグ球団であるアルビレックスBCとの兼ね合いもあるし、指定管理者はアルビレックス新潟・都市緑化センターグループが持ってるし(これぞ地元球団と財団法人とのJVですよ)、ここに食い込むのもこれはこれで大変ですよ。まあ、金に物言わせてぶんどるって方法もあるかもしれませんけど、横浜スタジアムとこっちと、どちらが安いかな、という話で。静岡だって、草薙球場にプロ野球を入れるにはさらに改修が必要でその費用負担は球団にお願いする必要がっていう話ですし。ま、どこも一筋縄じゃ行きませんよ。

○あたしとあのこの、どっちをとるの?(何

結論としては、「地元密着か球団存続か」を仮に二者択一で決めなければいけないときに、後者を選ぶ声が多くの場合圧倒的に多数であるということがNPBの限界であり、そこに正面から向かい合おうとせずにただのアリバイ作りとして前者を振りかざすその姿勢って、どういうことよと。

その地元からとってみれば、(曲がりなりにも)愛してきたクラブがそこから居なくなろうかというのに、外野はそれを黙って受け入れろと迫る。その図が如何に暴力的なものなのかは、実際にこれを受ける立場にならないと分からないものかもしれません。その他大勢にとってはNPBの試合は「TVを通して」観るものでしかなく、それが何処で行われているかは知ったことじゃないわけなのです。

2011年10月23日

ハマスタを貶すなら日没を待て

この問題って、結局TBSがどこまで腰据えてNPBにコミットしているのかを測るだけの意味しかなくて、ナベツネといっしょになってハマスタに向かって「きたないなさすが」とか言って拳振り上げて喜んでるような輩は一度我に帰ってそのおめでたさを心配した方がいいと思います。さもないとどこぞの球団社長みたいなことになりますよ。あ、中日ドラゴンズさんリーグ優勝おめでとうございます。

たとえば楽天が仙台入りするにあたっての宮城球場改修や広島の新球場建設に何十億が投下されたように、日常的な運営にかかるコストとは別にTBSがスタジアムおよびその周辺・地元に対して投資を行ったという話は寡聞にして全く聞いていませんし。もしそういう実績が本当はあるならそれを交渉材料にすればいいし、それがないなら尚の事、市やハマスタに対してお金も出さずに言うことだけ聞かせようというなら、どんだけ虫のいい話ですか?というだけのことでしょう。得るもの欲しけりゃコストも払えと。

http://blog.livedoor.jp/yuill/archives/51620215.html
バックスクリーンの下で ~For All of Baseball Supporters~ : さらば、横浜

http://blog.livedoor.jp/tbi38/archives/4436036.html
変革!! Baystars blog : 知ってほしい。ハマスタが抱える事実

この手の「ハマスタぼったくり」論に簡単に乗っかる人たちって、横浜スタジアム以外の球場がどのような運用になってるのか調べようともしないのかほんとに不思議なくらいですね。そんなにハマスタを好きなようにしたいなら、なぜ「株式会社横浜スタジアム」を買おうとしないんでしょう。

http://www.uforeader.com/v1/se/E04682_0070FR47_8_51.html
株式会社 横浜スタジアム - 大株主の状況 (娯楽業) - 有価証券報告書を閲覧・検索するなら有報リーダー

「ハマスタをボールパーク化する」ために球場会社(=株式会社横浜スタジアム)と球団会社(=株式会社横浜ベイスターズおよびTBS)が手を取り合いましょう、って話で、ディールとしては普通に事業提携か、それで足りないなら企業買収して同一グループ化しましょう、って話の進め方すればいいはずなのに、なぜそういう話にならないのかさっぱり理解できない。「(NPB球団は)球場施設の管理・運営権の受託を目指している。」と言うなら、既にその権利を持ってる会社を買えばいいじゃんと。現にオリックスは株式会社大阪シティドームに対してそれをやっている。(経営再建中という有利な状況があったからとはいえ)

http://ja.wikipedia.org/wiki/大阪シティドーム
大阪シティドーム - Wikipedia

http://ja.wikinews.org/wiki/大阪ドーム、オリックスが買収へ
大阪ドーム、オリックスが買収へ - ウィキニュース

もちろん一口に株式会社と言っても、それぞれの社の事情や設立経緯などいろいろあり、買収するにもすんなり行かないことだってあります。が、ダメならダメでそこを取り上げばいい話。そこでやっと「ハマスタがこっちの交渉に乗ってくれない」という話をすればいい訳です。やれ市とスタジアムの癒着だとか黒い噂だとかいう話にしたいなら、そういう交渉を吹っかけてきっちりと状況を整えてから思いきりやればよろしい。

いずれにしろ、どのような交渉が球場会社と球団会社の間にあったのか知りませんが、ここまでのTBSの球団経営へのコミットのしかたを振り返るに、球団存続のための最低限のことくらいしかしてないんじゃないかあんたたち?という疑念しか浮かばない訳でして。

http://www.uforeader.com/v1/se/E04682_S00087UM_14_24.html
株式会社 横浜スタジアム - 事業の種類別セグメント情報 (娯楽業) - 有価証券報告書を閲覧・検索するなら有報リーダー

ざっと眺められる限りにおいてのデータですが、どこも概ね年間10億前後の球場使用料を支払っているというデータが出てきます。中日や巨人、ソフトバンクなどにおいては数十億単位とも。一方で横浜スタジアムは7.8億(2010年度)。この額が妥当かどうか。さらに広告収入等球団分配金は約2.8億(同)。これらの収支が他の球団・球場のケースと比べてどうなのか。こういった一つ一つの数字を基準にしないと、できる話もできなくなってしまいます。それと、「間借り」が悪いならヤクルトだって同じ事例になるわけですが、こっちには問題がないのかどうなのか。そこを踏まえずに横浜だけ取り上げてどうこう言ってしまうあたりに、おめでたさばかりがつのります。

球団経営がそうであるように、球場経営だってタダじゃできない。人的にも金銭的にも必要なリソースがあり、それらを負担して初めてその取り分を主張できるわけです。現状球場会社がそれらを全て負担しているおかげで他より安めの球場使用料で済んでいるというだけのことだったりはしませんか?もし広告・物販収入の配分を変えて球団会社にもより多く取り分が行くようにする代わりに、人を出してくれとか、あれやこれややってくれ、みたいな話になったときに、球団およびTBSにそれに対応するだけの態勢やコスト余力あるんですか?大丈夫ですか?カネもヒトも出さない、けど利益は寄越せ、という交渉だとしたらあまりにあんまりではないですかね。

その上で、横浜に残って続ける場合の収支と、外に出て行って球場を自前でゼロから作る、あるいは既存のところに入るといった場合とそれぞれ比較し、最適なオプションを選択すればいいだけのことでしょう。それで儲かる、そういうアテがある、っていうなら幾らでもどうぞと思います。しかしどうでもいいところで「ハマスタはがめつい」と騒ぎに騒いで煽りに煽って、勢い良く飛び出そうたってどこに行こうというんでしょう。大方行った先で変な業者にうんこつかまされるか、もっとがめつい業者にシリの毛まで抜かされる事態になるかのどっちかでは。まあ、シリの毛抜かれるのが気持ちいいって人もいるかもしれないなあ。

ちなみに、TBSがベイスターズを横浜から出すなと主張する根拠が「TBSがハマスタの株主だから」と真剣に言ってる奴が居てコーヒー吹きました。おめでたさもここまで来るといよいよ盆と暮れの区別すら付かなそうな。ていうか、そんな関係性がTBSと球場会社の間にあるのなら、TBSからハマスタに「もっとベイスターズのために働けよゴラ」ってプレッシャーをかければそれだけで済む話になっちゃうと思います。大丈夫なんでしょうか。

http://blog.livedoor.jp/nanjhogei/archives/5148672.html
"当確"でございます!! : 横浜スタジアム社長が激怒!!!

2011年6月24日

サーバPC運用にちょっと長めのHDMIケーブルがあると生きていけそうだ。

つい先日まで自宅のサーバPC用にディスプレイを用意してたんだけど、あまりに使う局面がなさすぎて手放した。そしたら急にマシントラブルに見舞われるという何とかの法則並みの展開がついさっき起こった。

何でそんな馬鹿なことになってんの、とこれだけだと自分でも思うが、最近のPC(というかマザボ)はDVI+HDMI出力、でVGAなしという構成が一般的になってきているので、VGAしか受けられなかった以前のディスプレイだとそもそも繋がらなくなるので、代わりのディスプレイを探そうってことになってたわけで。

ただ何ぶん代わりのディスプレイを仕入れる前のトラブル発生だったのでだいぶテンパってしまったが、このHDMI端子を思い出して、テレビとHDDレコーダにつなげてある奴をひっぺがしてPCをテレビにつなげてみたら、何事も無かったように表示してくれて無事にトラブルシュートできた。

PLANEX ハイスピードHDMI Ver1.4ケーブル 2m (PS3/Xbox360対応) PL-HDMI02
PLANEX ハイスピードHDMI Ver1.4ケーブル 2m (PS3/Xbox360対応) PL-HDMI02
プラネックス 2007-07-26
売り上げランキング : 24


Amazonで詳しく見る
by G-Tools

今回使ったのはケーブル長1mくらいだったのでかなり取り回しで難儀したけど、3mなり5mくらいあれば、普段つないでおく用事はないけど何かあった時にビデオ出力がほしい、みたいなサーバPC用途には全く問題がなさそうだ。これからの時代、サーバPC向けにちょっと長めのHDMIケーブルはオススメですな。

2011年6月18日

ドコモ版iPhoneは出ない たぶん出ないと思う 出ないんじゃないかな

まちょっと覚悟は(ry

なんてことをドコモ社長が株主総会でコメントした件で、ニュースサイトがにぎやかしく「ドコモ、iPhoneを断念」との報が駆け巡った昨日17日ですが。

まず最初に、この株主総会中にドコモ首脳部である山田社長・辻村副社長両名がドコモ版iPhone発売の可能性ありやなしやを問われて曰く、

iPhoneの販売について、辻村清行代表取締役副社長が回答。「アップルが発売しているiPhoneやiPadは、世界的に売れており、ユーザーインターフェイスに優れた端末だと認識している」と前置きしながらも、「NTTドコモは、iPhoneを発売する予定はない」と断言した。

 その理由として辻村副社長は、「ドコモには、約5000万人のiモード利用者がいるが、おサイフケータイ、iチャネル、iコンシェルといった機能をスマートフォンでも利用したいというユーザーが多い。ドコモにとって、いかにこれを広げていくかが大事である。しかしアップルの場合は、この機能を加えることができない。一方で、Androidを搭載したスマートフォンは、既存のiモードサービスを乗せることができる。5000万人のiモードユーザーが、気持ちよくスマートフォンを使っていただくために、ドコモはAndroidをベースにやっていく」と語った。

http://k-tai.impress.co.jp/docs/news/20110617_453942.html
NTTドコモ、株主総会で「iPhoneの発売はない」と断言 - ケータイ Watch

「発売する予定はない」のはずっと前からだし、その意味では従来の見解を繰り返したと見てよいコメントだが、折に触れてドコモはiPhone/iPad発売に向けた意気込みを公言し隠そうとすらしなかったように、発売に向けた見込みは厳しいが意欲はあるという姿勢を崩してはいなかった。

http://k-tai.impress.co.jp/docs/news/20090730_306024.html
ドコモ、第1四半期減収減益も計画進捗は順調 - ケータイ Watch

すなわち、この従来の姿勢から一見踏み込んだように見えるこのコメントをどう解釈するかがポイントになるのだが、

http://www.nikkei.com/tech/personal/article/g=96958A88889DE3E2E4E4EBE7E2E2E2EAE2E5E0E2E3E2E2E2E2E2E2E2%3Bp=9694E3EAE3E0E0E2E2EBE0E4E2E6
アップル「iPad」にNTTドコモがラブコールを贈る理由  :日本経済新聞

この日経記事のライターである石川温氏(@iskw226)のツイートが今回の株主総会での社長発言を伝える一次ソース(の一つ)として広まっていったようである。

株主からの質問「ドコモからiPhoneを発売するのか?」。ドコモ辻村副社長「iモードユーザーがスマートフォンに移行している。5000万のiモードユーザーが気持ちよくスマートフォンを使ってもらうためにはAndroidになる。iPhoneを導入する予定はない」less than a minute ago via Echofon Favorite Retweet Reply

続けて石川氏は「アップルとの交渉は打ち切られたのでは」との見解を発している。これは推測だが、その場の語調としては確かにドコモが断念したかのような、そう思わせるような何かがあったものかもしれない。

ちなみにこれを受けてと思われる、同じく総会に臨席していたライターの石野純也(@june_ya)氏がこのようなコメントをのせている。

ドコモ辻村氏、現在のところiPhoneを販売する予定はない。ここまで言い切ったのは初めてかな。less than a minute ago via twicca Favorite Retweet Reply

「ここまで言い切ったのは初めて」と、彼もそのように述べている。やはりそこの「ニュアンス」はこれまでとやや違うことを感じ取ったようだ。

この後、gigazineを皮切りに、速報系ブログからwebニュース、個人まとめブログなど、サイト硬軟を問わず様々な場でこの一件がやり取りされることになる。

その後石川氏は追加取材を行った。「iPhoneを導入する予定はない」としたコメントのウラを取りに行ったものと思われる。「ニュアンス」だけで判断することの危険性を考えれば当然の行動だ。するとここではほぼ否定のコメント、すなわち見込みは厳しいが意欲はある姿勢は継続したままであることを引き出したのである。

ドコモ株主総会での「iPhoneの導入はない」発言について、先ほど、山田社長、辻村副社長に聞いてみました。すると「"現時点"という言葉が抜けていた」(辻村副社長)、「スタンスは従来と変わっていない」(山田社長)とのことでした。less than a minute ago via Echofon Favorite Retweet Reply

もともとドコモとしてもこの交渉がかなりの難事業であることは認めていて、勝ち目のうすいことはハナから覚悟していた節はある。というか、外部からの伝聞を元に意思決定しているようなことを口外してはばからず、それをもとに一喜一憂揺れ動く乙女心に様式美を漂わせつつあったところで、「負け戦」の覚悟が無ければただいいだけ踊らされた阿呆だったわけであります。そもそも「ノーコメント」で通して良かったところを無駄に口を開けてしまって墓穴を掘り続け、招かなくても良い展開を自ら呼び込んでしまったのであって、このドコモ首脳部の一貫した口の軽さと認識の甘さは、どうあがいても失敗の誹りを免れないだろう。

しかし今回の件より深刻なのは、この報道を扱った各種メディアの姿勢である。twitterの普及は、従来以上に情報のリアルタイム性を増すこととなった。今我々は、「これは」と思わせる事象は即座に取り出され、全世界に発信されてしまう社会を迎えている。しかし事象には必ず文脈があり、類似する並行事例がある。そこに基づいて正当な価値を付加し、正誤や軽重を判断し流通可能な「情報」として加工するためにどうしても必要な「時間」と「手間」がある。今回、この一件においてそれらが全く機能せず、最初の精査どころか「現時点では」すらそぎ落とされ、「ドコモが正式にiPhoneを断念した」とされてしまった。内容としては全く従来と変わらなかったのに、である。

こうしたコメントのニュアンスを十分にコントロールできなかったドコモ首脳部の失敗性向は終始一貫しており、その点で批判を免れ得ないことは何度でも重ねて指摘しておく。しかしその一方、事象を情報へと精錬する過程の間にかなりの予断と憶測と期待(おそらく、「ドコモがiPhoneを諦めてくれたほうがいい」と考える人は相当に多かったものと思われる)が入り込み、最終的に判断できる状態になる前に「本来の意図とは180度異なる」情報が出回ってしまったことへは、より深刻な認識が必要ではないだろうか。おそらく未決事項の秘匿性をことのほか重要視するAppleのこと、このような交渉下手を繰り返すドコモをこれ以上相手にすることはないだろうとする見立てには十分すぎるほどの合理性があろうし、これにより決定的かつ最終的に、iPhone導入の道筋は絶たれたとしても不思議はない。そしてその場合、ドコモに引導を渡したのはニュースメディアということになるわけだから。

もしこれでも「なぁに、ドコモからiPhoneなんて出っこないんだし、結果として同じことじゃないか」と言う向きがあるかもしれない。結果として同じなら報道内容が異なってもかまわない?そうか。ありもしない事実をでっち上げられ、結果としてその方向に向かわされることで、立ち会わなくても良かったはずの災厄を招いた事例がどれだけあったか、それらは全く大したことがないか。そうか。

2011年5月24日

Core Foundationを知る / CFHostでエラーハンドリング

さて、プログラミングは正常系ばかりではだめで、エラーもちゃんと扱えるようにしとかないといけない。とりわけネットワークプログラミングについては特にそれが言えるんじゃないでしょうか。並のPCでも最近は潤沢なメモリ事情を拝啓にすれば、malloc()がNULLを返すことはそうそう無いと看做すことはできても(でも危険だよ!)、それにくらべればネットワークは格段に不確実であり、名前が引けなかったり、TCPコネクションが確立できなかったりすることは普通にある。正常系以上にエラー系におけるAPIの挙動はぜひ把握しておきたい。

前のエントリまででCFHostを非同期による解決例を追ったので、それに続けて話を進めてみます。

○ネットワークが無効な場合
○DNSサーバが応答しない場合

上記2例については、いずれの場合もコールバック関数(CFHostClientCallBack)の呼び出しにエラー(CFStreamError)を渡される。具体的には、CFStreamErrorのdomainフィールドに12(kCFStreamErrorDomainNetDB)、errorフィールドに8(EAI_NONAME)が与えられた状態でのコールバックとなる。

domainがkCFStreamErrorDomainNetDBだった場合、errorコードは/usr/include/netdb.hに定義されている通りであるとはCFHost Referenceに書かれてある通りなのだが、当のnetdb.hを読んでみるとEAI_XXX、すなわちgetaddrinfo()のエラーコードが定義されているのみである。つまるところ、CFHostの下位レイヤーではgetaddrinfo()が使われ、そのエラーコードがCFStreamErrorに設定されてコールバックされるということなのだろう。

http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFHostRef/Reference/reference.html%23//apple_ref/doc/uid/TP40003333-CH6-DontLinkElementID_2
CFHost Reference

○存在しない名前を引いた場合

DNS的に言えばNXDOMAINだったときの話。この時も、コールバックで受け取るパラメータとしては上記2項と同じであり、domain = 12およびerror = 8が設定されたCFStreamError構造体が渡されることになる。EAI_NONAMEの意味からすればそのとおりではあるのだけれども、上記二つと区別がつかないというのは何となく納得が行かない気も、しないでもない。まあ、区別をつけたくば他の方法を使えということなのだろう。

○CFHostGetAddresses()の第二引数(Boolean *hasBeenResolved)について

上記"CFHost Reference"にあるように、CFHostGetAddresses()は名前解決の完了したCFHostオブジェクトを第一引数に持ち、名前解決を済ませたことによって得られたsockaddr構造体を生のバイト列としてCFDataオブジェクトにし、それをCFArray配列オブジェクトとして返すという仕様の関数である。

ただし、前述のようにいくつかの要因によって名前解決が出来なかった場合もあり、その際にはエラーコード指定の上でCFHostClientCallBack関数が呼び出される訳だが、そのエラー発生時のコールバック発生後にCFHostGetAddresses()関数を使うと、第二引数hasBeenResolvedは、名前解決ができたできないに関わらずTRUEが与えられるらしい点に注意せねばいけない模様。つまり、名前解決ができたかどうかはこの第二引数から得られる値で確かめることはできず、もっぱら返値が非NULL(何らかのCFArrayオブジェクト)かどうかでしか分からないらしい、ということである。

挙動から見る限りは、この第二引数は単にコールバック関数が呼ばれたかどうか、すなわち「名前解決の処理がおわったかどうか」だけを見ることができる、と看做したほうが自然なようである。

2011年5月17日

Core Foundation Hacks / iPhoneでも同じように動くのか?

さて、ここまでやってきた内容はMacOSX上の話にてございました(しかもSnow Leopard)。これがiOS上でも同じように挙動するかどうかも追っておきたい。

一応、MacOSX/iOS間で(ある程度)共通の環境基盤ということで用意されている以上、挙動がMacとiPhone/iPadとで食い違ったりしてたら嫌じゃんと。適宜こうした差異があるんだかないんだかというのは自分の目で確かめておきたいものでありまして。

下記の実装によって、MacOSXでの動作と同様にデフォルトではaccept()されたサーバ側ソケットがCFReadStreamClose()/CFWriteStreamClose()の呼び出しの際に同時にはcloseされず、kCFStreamPropertyShouldCloseNativeSocketプロパティをTRUE(kCFBooleanTrue)に設定することで同時closeとなるよう挙動を変更できることが確認できました。よろしければ皆様もご確認くださいませ。

#import <UIKit/UIKit.h>

@interface iPhoneSocketServerAppDelegate : NSObject <UIApplicationDelegate, NSStreamDelegate> {
    CFSocketRef _socket;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

@end

#import "iPhoneSocketServerAppDelegate.h"

#include <sys/socket.h>
#include <arpa/inet.h>


@implementation iPhoneSocketServerAppDelegate


- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"stream = %@, eventCode = %u", aStream, eventCode);
    
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            NSLog(@"open completed");
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [aStream release];
            break;
            
        case NSStreamEventHasBytesAvailable:
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [aStream release];
            break;
    }
}

- (void)setupInputStream:(NSInputStream*)instream
{
    [instream setDelegate:self];
    [instream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [instream open];
}

- (void)setupOutputStream:(NSOutputStream*)outstream
{
    [outstream setDelegate:self];
    [outstream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outstream open];
}


static void
_server_socket_callback_2(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
    iPhoneSocketServerAppDelegate* appdele = (iPhoneSocketServerAppDelegate*)info;
    
    NSLog(@"accepted. (s = %p)", s);
    NSLog(@"handle = %d", handle);
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
    
    CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    
    [appdele setupInputStream:(NSInputStream*)readStream];
    [appdele setupOutputStream:(NSOutputStream*)writeStream];
    
    NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
}

- (void)doServer
{
    CFSocketContext context = { 0, self, NULL, NULL, NULL };
    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, _server_socket_callback_2, &context);
    
    int yes = 1;
    setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(8888);
    sin.sin_len = sizeof(struct sockaddr_in);
    
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin));
    CFSocketSetAddress(socket, data);
    CFRelease(data);
    
    CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    CFRelease(source);
    
    NSLog(@"socket = %p", socket);
}

@synthesize window=_window;

@synthesize navigationController=_navigationController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    // Add the navigation controller's view to the window and display.
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];
    
    [self doServer];
    return YES;
}

- (void)dealloc
{
    [_window release];
    [_navigationController release];
    
    CFSocketInvalidate(_socket);
    CFRelease(_socket);
    _socket = nil;
    [super dealloc];
}

2011年5月16日

もっとCore Foundation / サーバソケットとCFStreamと私

で。

CFStream APIについて触れたかったのはこっち。CFStreamはネットワーク部分はいわゆるTCP/IPソケットと同じなので、connectとacceptによって通信路が確立される。

前のエントリで触れた内容ではクライアント側の実装、すなわちconnect側だったわけだが、もちろんサーバソケット(accept側)の実装もできる。ちょっと手間はかかるけれどもこんな感じ。

static void
_server_socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
    
    NSLog(@"accepted. (s = %p, handle = %d)", s, handle);
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
    
    if (_setup_read_stream(readStream) != 0) {
        CFRelease(readStream);
        readStream = NULL;
    }
    if (_setup_write_stream(writeStream) != 0) {
        CFRelease(writeStream);
        writeStream = NULL;
    }
    
    NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
}

static void
_do_server()
{
    CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, _server_socket_callback, &context);

    int yes = 1;
    setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(8888);
    sin.sin_len = sizeof(struct sockaddr_in);
    
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin));
    CFSocketSetAddress(socket, data);
    CFRelease(data);
    
    CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    CFRelease(source);
    
    NSLog(@"socket = %p", socket);
}
  1. CFSocketCreate()でCFSocketオブジェクトを生成し、
  2. CFSocketSetAddress()でバインディングアドレスを指定、
  3. CFSocketCreateRunLoopSource()およびCFRunLoopAddSource()でRun LoopにCFSocketオブジェクトを登録、
  4. 最初のCFSocketCreate()で登録したコールバック(_server_socket_callback)にacceptされたソケットディスクリプタ(=CFSocketNativeHandle)が渡り、
  5. 4.のソケットディスクリプタからCFStreamCreatePairWithSocket()より、CFReadStream/CFWriteStreamを生成。

これ以降は前エントリの扱いと同じ。

んで。

このパターンで生成されたCFReadStream/CFWriteStreamは、CFReadStreamClose()/CFWriteStreamClose()してもTCP接続は閉じないという現象を確認してしまいました。これそういうもん?と色々調べたところ、CFReadStream/CFWriteStreamオブジェクトのプロパティkCFStreamPropertyShouldCloseNativeSocketをTRUE(kCFBooleanTrue)にするとちゃんと閉じてくれるようになった。

static void
_server_socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    switch (type) {
        case kCFSocketAcceptCallBack: {
            CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
            
            NSLog(@"accepted. (s = %p, handle=%d)", s, handle);
            
            CFReadStreamRef readStream = NULL;
            CFWriteStreamRef writeStream = NULL;
            
            CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
            
            CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            
            if (_setup_read_stream(readStream) != 0) {
                CFRelease(readStream);
                readStream = NULL;
            }
            if (_setup_write_stream(writeStream) != 0) {
                CFRelease(writeStream);
                writeStream = NULL;
            }

            NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
        }
            break;
    }
}

もっとCore Foundation / CFStreamこそがtoll-freeだよ

さて前のエントリでCFHostをうっかりtoll-freeだと勘違いして書いてしまったせいで、引き続きのこのエントリが内容がらりと変えることになってしまいましてね、と前置きをしつつの第2回であります。

さて無事クエリを解決できたCFHostを用いていよいよ通信の確立ですが。CFStream APIを使えばそのまま非同期接続が可能なのでそれで充分です。さっそく行ってみる。ざっくり。

static int
_setup_read_stream(CFReadStreamRef readStream)
{
    CFStreamClientContext context = { 0, NULL, NULL, NULL, NULL };
    if (!CFReadStreamSetClient(readStream,
                               kCFStreamEventOpenCompleted |
                               kCFStreamEventHasBytesAvailable |
                               kCFStreamEventErrorOccurred |
                               kCFStreamEventEndEncountered,
                               _read_stream_callback, &context)) {
        CFStreamError err = CFReadStreamGetError(readStream);
        NSLog(@"err = %d/%ld", err.error, err.domain);
        return -1;
    }
    CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    
    if (!CFReadStreamOpen(readStream)) {
        // error.
        CFStreamError err = CFReadStreamGetError(readStream);
        NSLog(@"err = %d/%ld", err.error, err.domain);
        
        CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
        CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
        return -1;
    }
    
    return 0;
}

static int
_setup_write_stream(CFWriteStreamRef writeStream)
{
    CFStreamClientContext context = { 0, NULL, NULL, NULL, NULL };
    
    if (!CFWriteStreamSetClient(writeStream,
                                kCFStreamEventOpenCompleted |
                                kCFStreamEventCanAcceptBytes |
                                kCFStreamEventErrorOccurred |
                                kCFStreamEventEndEncountered,
                                _write_stream_callback, &context)) {
        CFStreamError err = CFWriteStreamGetError(writeStream);
        NSLog(@"err = %d/%ld", err.error, err.domain);
        return -1;
    }
    CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    
    if (!CFWriteStreamOpen(writeStream)) {
        // error.
        CFStreamError err = CFWriteStreamGetError(writeStream);
        NSLog(@"err = %d/%ld", err.error, err.domain);
        
        CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL);
        CFWriteStreamUnscheduleFromRunLoop(writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
        return -1;
    }
    
    return 0;
}

static void
_do_connect(CFHostRef host)
{
    NSLog(@"host = %@", host);
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host, 80, &readStream, &writeStream);
    
    NSLog(@"readStream = %@", readStream);
    NSLog(@"writeStream = %@", writeStream);
    
    if (_setup_read_stream(readStream) != 0) {
        CFRelease(readStream);
        readStream = NULL;
    }
    if (_setup_write_stream(writeStream) != 0) {
        CFRelease(writeStream);
        writeStream = NULL;
    }

    NSLog(@"connect start.");
}

_do_connect()で使うCFStreamCreatePairWithSocketToCFHost()からCFReadStream/CFWriteStreamオブジェクトを取り出してこれをCFRunLoopにバインドするまでで一連の処理になります。read/writeともに、使用するAPIの流れは以下の通り。

  1. CFXxxStreamSetClient()
  2. CFXxxStreamScheduleWithRunLoop()
  3. CFXxxStreamOpen()

最初のCFXxxStreamSetClient()でコールバック関数を指定した後、Run Loopに登録。その後にCFXxxStreamOpen()でCFStreamを開けばコールバック関数に処理が渡り始める。

static void
_read_stream_callback(CFReadStreamRef readStream, CFStreamEventType eventType, void* info)
{
    NSLog(@"readStream = %p, eventType = %ld", readStream, eventType);
    
    switch (eventType) {
        case kCFStreamEventOpenCompleted:
            ...            
        case kCFStreamEventHasBytesAvailable:
            ...
        case kCFStreamEventEndEncountered:
            ...
        case kCFStreamEventErrorOccurred:
            ...
    }
}

static void
_write_stream_callback(CFWriteStreamRef writeStream, CFStreamEventType eventType, void* info)
{
    NSLog(@"writeStream = %p, eventType = %ld", writeStream, eventType);
    
    switch (eventType) {
        case kCFStreamEventOpenCompleted:
            ...
        case kCFStreamEventCanAcceptBytes:
            ...
        case kCFStreamEventEndEncountered:
            ...
        case kCFStreamEventErrorOccurred:
            ...
    }
}

一方で、ここまでコールバックを設定するのにCFHostClientContextおよびCFStreamClientContextという構造体があったけれども、これがコールバック関数の引数に渡ってくることになる。

struct CFHostClientContext {
  CFIndex			 version;
  void *			  info;
  CFAllocatorRetainCallBack  retain;
  CFAllocatorReleaseCallBack  release;
  CFAllocatorCopyDescriptionCallBack  copyDescription;
};
typedef struct CFHostClientContext	  CFHostClientContext;

typedef CALLBACK_API_C( void , CFHostClientCallBack )(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info);

typedef struct {
    CFIndex version;
    void *info;
    void *(*retain)(void *info);
    void (*release)(void *info);
    CFStringRef (*copyDescription)(void *info);
} CFStreamClientContext;

typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
typedef void (*CFWriteStreamClientCallBack)(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);

CFHostClientContextおよびCFStreamClientContextのinfoフィールドで指定したアドレスが、CFHostClientCallBackの第4引数info、CFReadStreamClientCallBack/CFWriteStreamClientCallBackの第3引数clientCallBackInfoに渡ってくる。

このCFHostClientContext/CFStreamClientContextでは、infoフィールドのアドレスをCoreFoundationオブジェクトが標準でそうであるように、参照カウントの仕組みを前提としており、infoを引数にするretain/release関数を指定することができる。それぞれretain/releaseフィールドはこの用途で使われる。また、copyDescriptionフィールドは、デバッグ用途などでオブジェクトの内容を文字列化する際に使うことができる。CocoaでNSLogに書式化文字列を使って"%@"にオブジェクトを直接ぶち込み、オブジェクトの内容をコンソールに簡易に出力することができるアレのこと。

ただ、retain/release/copyDescriptionフィールドはあまり重要性が高くなく、Cocoaとの連携で考える限りにおいて参照カウントの管理についてもObjective-C側でのみ面倒を見ていれば大抵は用が足りると思われ、あんまりそこは気にしなくてよさそうな感触である。

2011年5月13日

Core Foundationをもっと使おうじゃないか / CFHostで非同期に名前解決

ちょっと自分で必要があって調べたので、ここにまとめておこうと思う。Core Foundationネタである。

大抵のことはCocoa(というかFoundationとAppKit)レイヤーで済んでしまうので、割とこう厄介になることは少ないもんなんだけど、いざ細かいところが気になり出して手を加えようとしはじめると粒度が荒くて...なんてことが割とある。以下はそのときに調べて使ったものを少しずつ小分けにまとめてみたもの。備忘録的に行ってみよう。

***

CFHostで非同期に名前解決を行えることを知ったのでそのやり方をまとめてみた。CFHostは普通の同期解決も非同期解決もできるんだが、メインループとの親和的な統合を歌っているだけあり、非同期解決の方でこそその旨味が発揮されるというものかもしれない。っていうか同期解決なら普通にNSHost(Cocoa)でもできるし。

非同期で名前解決することで得られるメリットは「アプリの挙動が安定すること」だ。MacOSX/iOSいずれであっても、ユーザの操作にはできるだけダイレクトに応答するようにした方が、アプリが安定して動いている感じがしてよろこばれるし、また何かしらのブロッキングな処理で一見アプリの動作が止まっているように見えても、「名前解決をしているのか」「データを読もうとしているのか」がわかる、あるいは本当に異常で「フリーズして」しまっているのかを見分けられるというのはアプリケーションに対する安心感として帰ってくる。また、もし誤操作でブロッキングさせた、もしくはブロックしている間に気が変わって他のことをしたくなったなら、簡単にキャンセルして元に戻せるようにもしたい。快適なインターネットライフ(何)のために、アプリケーションの挙動はできるだけユーザに安心と信頼を与えられるようでありたいものだ。

ネットワークアプリケーションを作るときに、ブロッキングが発生するタイミングは大きく三つある。

  • 名前解決
  • TCP接続の確立(connect(),accept())
  • データの送受信

これらの状況で何も考えずに処理を書くと簡単にブロックが発生してしまう。ブロックが発生するというのは即ちGUI操作が止まってウィンドウは動かせずボタンは押せず、レインボーカーソルのお出ましである。「サーバとの通信を行います」とモーダルダイアログで告知してたまに通信を行うだけの必要最低限のネットワーク機能しか無いようなやつならまだしも、メールソフトやブラウザが頻繁に固まるようなアプリケーションばかりでは、正直快適とは言いがたい。

Cocoaではこの「データの送受信」については既に非同期処理が導入されている(NSStreamsとか)。このため大抵のケースはこれで間に合うが、前の二つ「名前解決」「connect()/accept()」については同期のみ、というか手の入れようが無い。まあDNSはローカル上でもキャッシュ機構が働いているし、NSURLなどではそこも含めて隠蔽しているはずなので(未確認だけど)、こまけえこたぁ気に(ryってことなのかもしれない。

しかしNSURLにはそぐわないがヘビーにTCP/IPをつつきたい用途なんかもやっぱりあるわけで、そうするとあとはBSDソケットレイヤーでO_NONBLOCKを...とか言いたくなるところだが、Core FoundationにはCFHostというAPIがあって、こいつで非同期クエリが飛ばせるらしいと知った(しかもNSHostのtoll-free)←勘違いでした(_ _)。

http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFHostRef/Reference/reference.html
CFHost Reference

おおまかな流れとしては以下の通り。

  1. CFHostCreateWithName()でインスタンス生成
  2. CFHostSetClient()でコールバックを設定
  3. CFHostScheduleWithRunLoop()でメインループに接続
  4. CFHostStartInfoResolution()でクエリ送信
  5. 2.で設定したコールバックでクエリ応答を受信
static void
_host_client_callback(CFHostRef host, CFHostInfoType typeInfo, const CFStreamError *err, void* info)
{
    Boolean resolved = FALSE;
    
    CFArrayRef addr = CFHostGetAddressing(host, &resolved);
    
    NSLog(@"ASYNC: resolved = %d, err = %d/%ld", resolved, err->error, err->domain);
    NSLog(@"ASYNC: result = %@", addr);    
}

static void
_do_query(CFStringRef s)
{
    Boolean ret, resolved;
    CFStreamError err;
    
    CFHostRef host = CFHostCreateWithName(NULL, s);
    CFHostClientContext context = { 0, NULL, CFRetain, CFRelease, NULL };
    
    CFHostSetClient(host, _host_client_callback, &context);
    
    CFHostScheduleWithRunLoop(host, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    ret = CFHostStartInfoResolution(host, kCFHostAddresses, &err);
    {
        CFArrayRef addr;
        
        NSLog(@"SYNC: ret = %d", ret);
        
        addr = CFHostGetAddressing(host, &resolved);
        
        NSLog(@"SYNC: resolved = %d, err = %d/%ld", resolved, err.error, err.domain);
        NSLog(@"SYNC: result = %@", addr);    
    }
}

CFHostStartInfoResolution()に至る前にCFHostSetClient()を行ってコールバックを設定していなければ、非同期解決にはならずにそのままCFHostStartInfoResolution()でブロックがかかる。

そして_do_query()の引数にCFStringRefが与えられているがこれもNSStringのtoll-free bridgeがかかっているので、呼び出す時はNSStringオブジェクトにキャストして渡してやればよい。

    NSString* s = @"www.google.com";
    _do_query((CFStringRef)s);

そして実行結果は下記のようになる。

2011-05-13 12:17:54.757 CFHostTester[65231:903] SYNC: ret = 1
2011-05-13 12:17:54.760 CFHostTester[65231:903] SYNC: resolved = 0, err = 0/0
2011-05-13 12:17:54.761 CFHostTester[65231:903] SYNC: result = (null)
2011-05-13 12:17:54.860 CFHostTester[65231:903] ASYNC: resolved = 1, err = 0/0
2011-05-13 12:17:54.861 CFHostTester[65231:903] ASYNC: result = (
    <10020000 40e9b769 00000000 00000000>,
    <10020000 40e9b76a 00000000 00000000>,
    <10020000 40e9b793 00000000 00000000>,
    <10020000 40e9b763 00000000 00000000>,
    <10020000 40e9b767 00000000 00000000>,
    <10020000 40e9b768 00000000 00000000>
)

ログの出力に「SYNC:」と「ASYNC:」とそれぞれ付く箇所の出力内容に注意。SYNC:の付く箇所はCFHostStartInfoResolution()を呼んだ直後に取ったデータだが、ここには決して名前解決したデータが乗ってくることはない。何度も同じ名前解決を行って確実にDNSキャッシュに乗ったと思われる状況でも同じだったので、非同期設定を行ったCFHostではコールバックが行われるまではDNS情報はCFHostオブジェクトには渡って来ない、と見るべきなよう。もちろん、非同期設定を行わないつまりCFHostSetClient()およびCFHostScheduleWithRunLoop()を呼ばなければ、普通に「SYNC:」の行で結果は返る。

2011年4月 9日

「ずっと好きだった」が、好きだった。

...とタイトルをつけてみた割には、僕はどっちかというと「彼女」の方がより好きだな。

騒動そのものを追うのは他の人に任せて、僕は今、この件について感じたことだけを話そう。

斉藤和義自身の主張が反原発でも親原発でも、僕はどちらでもかまわないと思っている。そしてそのことを口舌で語るのではなく、音楽によって主張したという点において、そこにミュージシャンとしての気概を見いだして嬉しくもある。しかし一方で、それが「『ずっと好きだった」の替え歌」として行われたことには、すごく残念な思いがある。

ずっと好きだった 斉藤和義 歌詞情報 - goo 音楽

彼自身、どのような批判でも受けて立つ気持ちだったのだろうと思う。そこは最大限尊重したい。あと作風もわざわざ「青い光」を持ち出すまでもなく、割と社会的な話題をちりばめることは少なくないので、それ自体にとりたてて意外だとか驚いたとかってことも特には無い。そもそも清志郎チルドレンでもあるわけで(というのは笹平さん(@sshrtk)の指摘で気がついた、というか思い出した)、この局面で抱く思いもいろいろあるだろうってのは分かる。事務所やレコード会社など周囲との葛藤や軋轢も相当いろんなものがあろうし、その辺はここではおいておく。

RESPECT!
RESPECT!オムニバス 忌野清志郎 仲井戸麗市 三宅伸治 石田長生 泉谷しげる 及川光博 矢野顕子 坂崎幸之助

ポリドール 2000-05-05
売り上げランキング : 31802


Amazonで詳しく見る
by G-Tools

ただ、今回の「ずっとウソだった」の歌詞を聞いて、というかこの騒動の発端を耳にしてからずっと、どうにも強い違和感があった。

「ずっと好きだった」は形の上ではずっと好きだった女の子への未練をぶつける歌詞だが、「ずっと好きだった」のに、その思いに応えてくれなかった相手を責めるといったことではなく、未練を断ち切れずにいる自分の焼き焦がれるような内面をさらけ出す歌である。

「ずっと好きだった」だけだとはっきりしてこないかもしれないが、最初にも挙げた「彼女」やちょっと毛色は違うが「進め なまけもの」あたりを例に挙げたい。あと「ずっとウソだった」路線から攻めるなら、「ポストにマヨネーズ」の方なんかも近いかもしれない。

歌うたい15 SINGLES BEST 1993~2007
歌うたい15 SINGLES BEST 1993~2007斉藤和義 斉藤和義と玲葉奈

ビクターエンタテインメント 2008-08-06
売り上げランキング : 69


Amazonで詳しく見る
by G-Tools

好きだった、それでも別れた相手に「気づけなかった涙してたこと」をいつまでもひきずり(「彼女」)、うまくいかない毎日の、アパートに帰り着き眠ろうとするも賑やかそうな「隣が目障りだけれど寝ちゃえば平気」と敢えて外ではなく内へ意識を持って行こうとすることで軽く強がってみたり(「進め なまけもの」)、ストーカーだか変質者だかへどが出るような嫌がらせをしてくる相手に同じレベルになって相手してみせるなど(「ポストにマヨネーズ」)、これでもかと、脆く無様な自分の内面を引きずり出して描き出し、それを歌にする。「あんたの人生 楽しそうだな」って皮肉だろうか、それとも本心だろうか。何とも身震いさせる、ワクワクさせる歌詞を書いてくれるじゃないか。

こんな男が「ずっと好きだった」と歌う。他の歌から比べればかなり抑制の効いた内容だけに、むしろその内面はどれだけのカオスになっているものかと、想像かきたたせられるものがある。この歌で同じようなことドロドロと書いてたら野暮ってもんだよね、ってなくらいのもんだ。

斉藤和義というとそのくらいのものが言ってみれば「先入観」として浮かぶので、その上で「ずっとウソだった」を聞かされてん?何だ政府と東電disだとか?え?そんな歌だっけ?と、軽く面食らってしまった。「ずっと好きだった」と言う自分は、それを口にすればする程辛くなっていくことが分かっているはずなんだが、「ずっとウソだった」と嘆く自分からは、そんな様子は感じられなかった。いつもなら、相手に対して手に握り構えるナイフのあまりの冷たさに我が身をも凍てつかせ滅びさせようともする自分に向けた鋭さが、「ずっとウソだった」では全く立ちのぼって来なかった。

替え歌なんだから、元歌にどこまでも従属しなきゃいけないってもんでもなし、好きに組み変わって当然だと割り切る考え方もできる一方、作者本人の手によるものながら、しかもタイトルも歌詞構成もかなりそのままなのに詞世界を全然トレースしていないというのは、正直にぶっちゃけてしまえば、「ガッカリ」した。

プロテストソングをやりたいならやればいい(「ご勝手に」と突き放す意味ではなく)。けど、「ずっと好きだった」を好きだった立場からすれば、作者本人がこんなことをしてしまったら、まるで元歌が死んでしまったようである。元歌の自分と、替え歌の自分は、全く繋がっていないんだろうか。...いやむしろ実際は、同じように辛いし、同じように自分が責めを負うべきなんじゃないか。世間は政府や東電へのその批判精神と勇気に感動したなんつってるが、本当はそんな簡単な話じゃないんじゃないか。ねえ。どうなんだ。ああ、モヤモヤする。

教えてよ。やっぱいいや...。

最近のコメント

アイテム

  • old-magazine.png
  • msx-books.png
  • scansnap-ng.png
  • scansnap-scrapped.png
  • scansnap-book3.png
  • scansnap-book3.png
  • scansnap-book2.png
  • scansnap-book1.png
  • scansnap-desk.png
  • IMG_0045.png
OpenID対応しています OpenIDについて