SimpleBoxes

Safari で Reeder

[画像] Reeder on Safri

reederGoogle Reader を快適に利用できるアプリケーションです。

その reeder からインスパイアされて、似たルック & フィールを Chrome 上で実現する機能拡張が Reeder for Chrome (Unofficial) (以下、RFC) です。

残念ながら RFC の開発は継続されないことになりましたが、ソースは公開されています

RFC の最も肝となるルック & フィールは基本 css のみで実現されていて、Stylish を使えば、他のブラウザでも利用できそうです。

他に fork している人がいたので、その内容を確認しようと思っただけだったんですが、間違えて自ら fork してしまい、それをきっかけに中身を確認することに……。

  1. Stylish for Safari より Stylish の機能拡張をダウンロード・インストールします。
  2. ツールバー上にある「Stylish」ボタンから管理画面 (Manage) を開きます。
  3. 「Edit」を選ぶと新規スタイル編集画面が開きます。
    Title
    RFC
    CSS
    GitHub から CSS の内容をコピー
    Applies to
    "Regexp" を選択して、https?://www.google.*/reader/.*

その後、Google Reader を見ると Reeder のようなインタフェースに変わっていると思います。オススメは 3 カラム表示。

(Webkit に特化したスタイルを利用しているため) CSS の一部変更が必要になりますが、Firefox でもほぼ同様に Stylish から利用できます。

スポンサーリンク

WWDC 2012 雑感

Retina ディスプレイが搭載された新しい MacBook Pro が発表されました。

15 インチ、薄型で 13 インチ MacBook Pro より軽い、DVD ドライブはおろか HDD ドライブもなし。メモリもオンボード直付けで後からの換装は不可能。…… MacBook Air 15 インチのような存在というと語弊があるでしょうか。

Retina ディスプレイ MacBook Pro では、Tunderbolt を二口搭載しつつ、さらに HDMI を搭載しています。つまり外部ディスプレイ用ポートが標準で 3 つあることに。

とりあえず、現在の仕様上では、外部ディスプレイ二つまではサポートしているようです (つまり本体ディスプレイと合わせて 3 つのディスプレイ表示)。

折角、グラフィックチップも Intel HD Graphics 4000 と NVIDIA GeForce GT 650M の二つ搭載していますし、将来的にはソフトウェアアップデートで 3 つの外部ディスプレイサポートなんてこともあるんでしょうか……。

今回の WWDC の発表では、何といっても iOS6 が気になります。さっそくベータをダウンロードして使っていて、色んな変化があって嬉しいやら (今後の対応を考えると) 辛いやら……。

NDA があるので、どこまで触れていいのか分からないのが悩みどころ。Apple から公にされている部分は触れても構わないと思うので、そこを中心に……。

大幅に更新された Map アプリは、もうただ驚き。まだベータ版っぽい動作で粗はいっぱいありますが、使っていて楽しいアプリであることには間違いないです。……前職だったので、ちょっと指摘するのが辛いところもあるんですが、ナビ関係のアプリ開発はこれから大変そうです……。標準添付だもんなぁ、これ。

メールアプリ、Apple 公式の Pull-to-Refresh が来ましたね。この挙動、WWDC でデモしたのかな。それとメールや設定アプリのスクリーンショットで気付いた方もいるかもしれませんが、バッテリー状態などを表示するステータスバーのアピアランスが変わっています。グレーじゃなくなって、バッテリーアイコンが単色に……。結構雰囲気が変わって見えますね。

Facebook 対応は結構嬉しい。私が利用するかどうかというよりも、おそらく Twitter 同様 Apple からフレームワークとしてサポートされると思うので。そうなると、Facebook 対応アプリを作りたいという時に開発負担が激減すると思います。

初代 iPad が対応していないのはちょっと残念。それより前にリリースされている iPhone 3GS が対応しているので余計。メモリ的に厳しいのかなと予想。やはり Siri が……。

WWDC 基調講演で発表された以外にもいろいろアップデート (Mac Pro とか、AirPort Express とか) があったようで、盛りだくさんな感じですね。

スポンサーリンク

Objective-C / ARC で unsafe_unretained 利用時のインスタンス破棄タイミングについて

ここ最近のコーディングでは iOS5 SDK と同時に登場した ARC (Auto Reference Counting) を利用しています。

ARC の導入と注意点については、iOS 開発ブログ Natsu's note さんの一連の記事がとても参考になります。

C++ with boost に慣れた人なら、ARC はスマートポインタ (SmartPtr, AutoPtr) に近い動作をするという認識でよいと思います。

代入 (アサイン) 動作で参照カウンタが加算 (インクリメント) され、スコープを抜ける段階で release が呼ばるようなイメージで動作します。参照カウンタが 0 になった時点でオブジェクトが破棄されるのはこれまでの retain / release を使った従来の手法 MRC (Manual Retain-Release) と同様です。

明示的に retain / release を呼び出す必要がなくなり、巡回参照さえ気をつければ、メモリリークも軽減できる ARC はとても便利です。

そんな便利な ARC ですが、unsafe_unretained / weak 関連で不思議な動作をするのに気づいたので、忘れないようにメモしておきます。

Delegate 時の参照

[iOS5] ARC (Automatic Reference Counting) : Overview で触れられている通り、Cocoa プログラミングで多用される Delegate パターンでは「弱い参照 (weak reference)」を使って巡回参照によるメモリリークを防ぎます。

ただし、weak 修飾子は iOS5 以降でしか利用できません。iOS4 でも動作するアプリケーションにするためには、weak 修飾子を利用するところで unsafe_unretained 修飾子を使う必要があります。

unsafe_unretained 修飾子を使った場合、MRR で assign を利用したときと同様にアサインしたオブジェクトを適切なタイミングで nil 化してあげる必要があります。

しかしながら、unsafe_unretained 修飾子を利用する場合、ある条件下でオブジェクトが不用意に解放されてしまう場合があります。この場合、適切と思われる箇所で nil 化していても EXC_BAD_ACCESS の要因になりうるので注意が必要です。

多段 Delegate

さて、その「ある条件」について考察していきます。

以下は私が見つけたパターンですが、他にも同様なケースがあるかもしれません。

  1. ある Object A が Object B を作成し、その Delegate として動作します。

  2. Object B は内部で Object C を作成していて、その Delegate として動作していて、Object C からの通知を起点に Object B から Object A にメッセージが送信されます。

  3. Object A は Object B の Delegate メッセージを受け取り次第、Object B を利用しないので、Object B を破棄します。

文章にすると若干ややこしい感じがしますが、例えば、

ネットワーク越しのデータを処理するあるクラス (Object B) は内部で NSURLConnection (Object C) を利用していて、データ受信終了を起点に Object B でデータ処理を行い、Object A に通知する

というケースなどが該当します。

[図]多段 Delegate の基本パターンダイアグラム

コードで表現すると、

@class SampleClassA;
@class SampleClassB;
@class SampleClassC;

@protocol SampleDelegateB <NSObject>

- (void)callFromClassB:(SampleClassB *)object;

@end

@protocol SampleDelegateC <NSObject>

- (void)callFromClassC:(SampleClassC *)object;

@end

#pragma mark -

@interface SampleClassA : NSObject <SampleDelegateB>

@end

@interface SampleClassB : NSObject <SampleDelegateC>

@property (nonatomic, unsafe_unretained) id<SampleDelegateB> delegate;

@end

@interface SampleClassC : NSObject

@property (nonatomic, unsafe_unretained) id<SampleDelegateC> delegate;

@end

というような形で、SampleClassB では SampleDelegateC のデリゲートメソッド callFromClassC: 内で SampleDelegateB デリゲートメッセージを通知します。

- (void)callFromClassC:(SampleClassC *)object
{
  NSLog(@"  -> started  [%p] with '%@'",self,self.message);
  [self.delegate callFromClassB:self];
  NSLog(@"  -> endded   [%p] with '%@'",self,self.message);
}

つまるところ、オブジェクト C からのメッセージをフォワードするような感じのイメージ。

多段 Delegate のワナ

この形だけでは特になんてことはないのですが、

Object A は Object B の Delegate メッセージを受け取り次第、Object B を利用しないので、Object B を破棄します。

という条件が加わると、オブジェクトが破棄されるタイミングが実装によって変わってきます。

  • weak 参照を利用している

  • unsafe_unretained 参照を使っていて、プロパティ (メソッド) を通してデリゲートにアクセスしている

  • unsafe_unretained 参照を使っていて、メンバ変数でデリゲートにアクセスしている

オブジェクトが破棄されるタイミングが weak 参照をつかっているか、unsafe_unretained 参照を使っているか……さらにはプロパティを使っているかなどの条件で変わってきます。

サンプルコードを github に上げたので、それを元に挙動を確認することができます。

以下の実行結果は、Xcode 4.3 / iOS5 SDK を利用して行なったものです。

weak 参照を利用

SBSampleDelegate.h において

  • kUsingWeak 1
  • kAsseccingViaProperty 0
  • kUsingRetainAutoreleaseHack 0

のように設定した場合。

[A]===> initialized [0x6a67830]
[C]  -> initialized [0xXXXXXXX]
[B]  -> initialized [0xXXXXXXX] with 'msg0'
[C]  -> started     [0xXXXXXXX]
[B]  -> started     [0xXXXXXXX] with 'msg0'
[A]===> called      [0xXXXXXXX]
[A]===> removed     [0xXXXXXXX]
[B]  -> endded      [0xXXXXXXX] with 'msg0'
[C]  -> ended       [0xXXXXXXX]
[B]  -> destroyed   [0xXXXXXXX] with 'msg0'
[C]  -> destroyed   [0xXXXXXXX]

C → B と作成され、逆に B → C と破棄されています。期待通りの動作と言えそうです。

unsafe_unretained 参照を使っていて、プロパティ (メソッド) を通してデリゲートにアクセス

SBSampleDelegate.h において

  • kUsingWeak 0
  • kAsseccingViaProperty 1
  • kUsingRetainAutoreleaseHack 0

のように設定した場合。

[A]===> initialized [0xXXXXXXX]
[C]  -> initialized [0xXXXXXXX]
[B]  -> initialized [0xXXXXXXX] with 'msg0'
[C]  -> started     [0xXXXXXXX]
[B]  -> started     [0xXXXXXXX] with 'msg0'
[A]===> called      [0xXXXXXXX]
[A]===> removed     [0xXXXXXXX]
[B]  -> endded      [0xXXXXXXX] with 'msg0'
[B]  -> destroyed   [0xXXXXXXX] with 'msg0'
[C]  -> ended       [0xXXXXXXX]
[C]  -> destroyed   [0xXXXXXXX]

C → B と作成され、逆に B → C と破棄されていますが、分かるでしょうか、[B] -> destroyed[C] -> ended の呼び出される順番が逆になっています。

SampleClassC からデリゲート B を呼び出しているメソッド callDelegate のスコープにまだいる最中に SampleClassC のデリゲートである SampleClassB のインスタンスが破棄されています。

これですぐに問題になるケースはぱっと思いつきませんが、ちょっとだけ不安な感じ。

unsafe_unretained 参照を使っていて、メンバ変数でデリゲートにアクセス

SBSampleDelegate.h において

  • kUsingWeak 0
  • kAsseccingViaProperty 0
  • kUsingRetainAutoreleaseHack 0

のように設定した場合。

[A]===> initialized [0xXXXXXXX]
[C]  -> initialized [0xXXXXXXX]
[B]  -> initialized [0xXXXXXXX] with 'msg0'
[C]  -> started     [0xXXXXXXX]
[B]  -> started     [0xXXXXXXX] with 'msg0'
[A]===> called      [0xXXXXXXX]
[A]===> removed     [0xXXXXXXX]
[B]  -> destroyed   [0xXXXXXXX] with 'msg0'
[B]  -> endded      [0xXXXXXXX] with '(null)'
[C]  -> ended       [0xXXXXXXX]
[C]  -> destroyed   [0xXXXXXXX]

明らかにおかしい挙動になります。

まだ C からのデリゲート callFromClassC: のスコープにいるのにも関わらず、dealloc が呼び出されています。

SampleClassB のインスタンスが破棄された後、callFromClassC: のメンバである mMessage にアクセスしているので、(null) と表示されています。場合によっては EXC_BAD_ACCESS の要因になります。

retain-autorelease ハック

という訳で unsafe_unretained 参照を利用すると、weak 参照とは異なった挙動になってしまいます。

これは MRR で assign を利用した場合でも起こりうる現象です。MRR ではこれに対して、

[[obj retain] autorelease]

retain と autorelease を併用することで、当該オブジェクトを autorelease プールに登録して、少なくともスコープ内はオブジェクトが破棄されないようにすることができました。

ご承知の通り、ARC では retain / autorelease メソッドの呼び出し自体が禁止されているので、同じハックは利用できません。

ただ、ローカルな変数に対しては、__autorelesing 修飾子を利用することによって autorelease プールを利用することを明示できます。

__autorelasing id temp = obj;

これでスコープ内では temp を利用すると、インスタンス obj に対して retain-autorelease で呼び出したのと同じような効果が得られます。

SBSampleDelegate.h において

  • kUsingWeak 0
  • kAsseccingViaProperty 0
  • kUsingRetainAutoreleaseHack 1

のように設定した場合、SampleClassA において retain-autorelease ハックを利用します。

[A]===> initialized [0xXXXXXXX]
[C]  -> initialized [0xXXXXXXX]
[B]  -> initialized [0xXXXXXXX] with 'msg0'
[C]  -> started     [0xXXXXXXX]
[B]  -> started     [0xXXXXXXX] with 'msg0'
[A]===> called      [0xXXXXXXX]
[A]===> removed     [0xXXXXXXX]
[B]  -> endded      [0xXXXXXXX] with 'msg0'
[C]  -> ended       [0xXXXXXXX]
[B]  -> destroyed   [0xXXXXXXX] with 'msg0'
[C]  -> destroyed   [0xXXXXXXX]

weak 参照と同様に C → B と作成され、逆に B → C と破棄されています。

ログでは同じように見えていますが、こちらの実装では、B は autorelease プールのタイミングで破棄されているので、内部的には異なる挙動になっています。ただ、不用意な破棄がなくなり、アクセスバイオレーションはなくなります。

スポンサーリンク

SBPullToRefreshHeaderView - 「引っ張って更新」を再実装してみる

HMDT さんから開発者向け電子書籍がリリースされています ([本] HMDT JOURNAL 創刊 | Cocoaの日々情報局 経由)。

早速、Vol.001 と Vol.002 を購入してみました。木下さんらしい読みやすい文体で、かなり丁寧に書かれています。

……特定の連載記事だけをバラで購入できたら嬉しいなぁ……なんて。ともあれ、今後の展開が楽しみです。

EGOTableViewPullRefresh

さて、この HMDT JOURNAL Vol.001 で紹介されている EGOTableViewPullRefresh は、いわゆる「引っ張って更新」を実現するクラスで、Facebook 謹製のライブラリでも採用されていたりする有名なライブラリです。

組み込みも簡単で、コードも分かりやすく記述されているライブラリなのですが、導入にあたってコントローラ側で UIScrollViewDelegate のメッセージをフォワードする必要がある点がちょっと不満。

例えば、UIButton ではボタンのハイライトのためにタッチイベントをコントローラ側からフォワードしなくても、ボタン側で勝手に処理してくれるように、EGOTableViewPullRefresh もスクロールイベントを勝手に処理してくれたらなぁと思うわけです。

SBPullToRefreshHeaderView - yet another "pull-to-refresh"

……そこで自分で「引っ張って更新」するクラス SBPullToRefreshHeaderView を自作してみました。

[図] SBPullToRefreshHeaderView の実装例

ARC 対応で __weak 修飾子を使っているので、iOS5 以降でお使いいただけます。

他に EGOTableViewPullRefresh から微妙に変わっている部分に「離して更新」の時にリロード用画像を使っています。OS X 向け Twitter アプリケーションのような感じ。

サンプルデモのコードにある通り、コントローラ側では SBPullToRefreshHeaderView の初期化メソッドにターゲットとなるスクロールビューを渡すと、あとは SBPullToRefreshHeaderView 側でよしなにしてくれます……はずです。

mRefreshHeaderView = [[SBPullToRefreshHeaderView alloc] initOnScrollView:self.tableView
                                                            withDelegate:self];

デリゲート SBPullToRefreshHeaderViewDelegate のメソッドはふたつ。

@protocol SBPullToRefreshHeaderViewDelegate <NSObject>

/// 「引っ張り更新」の発動 (ユーザが更新するためにスクロールビューを離した) を通知します。
/// @param headerView - 関連する SBPullToRefreshHeaderView のインスタンス
- (void)didTriggerRefresh:(SBPullToRefreshHeaderView *)headerView;

/// ターゲットが「ローディング」状態がどうかを確認します。
/// @param headerView - 関連する SBPullToRefreshHeaderView のインスタンス
/// @return ローディング状態にある場合、YES をそうでない場合 NO を返します
- (BOOL)isRefreshStillProcessing:(SBPullToRefreshHeaderView *)headerView;

@end

コントローラ側では基本的にはこのふたつのデリゲートメソッドを実装して、ローディングが終了した後に「resetView:」メソッドを送信します。

SBPullToRefreshHeaderView の実装

SBPullToRefreshHeaderView ではスクロールイベントを処理するために KVO (Key-Value Observing) を使っています。

スクロールが発生すると、UIScrollViewcontentOffset が変化することに着目して、そこでスクロール時に処理する内容を記述しています。

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
  if (object == mScrollView &&
      [keyPath isEqual:@"contentOffset"])
  {
    [self scrollViewDidScroll:mScrollView];
    if (mIsDragging != mScrollView.isDragging)
    {
      if (!mScrollView.isDragging)
      {
        [self stopDragging];
      }
      mIsDragging = mScrollView.isDragging;
    }
  }
}

ユーザーがターゲットのスクロールビューから指を離したかどうかの判定もここで行っています。スクロールの変化で判定しているので、実際のタッチリリースとは異なるタイミングになりますが、動作させてみた感じ特に違和はない感じ。

オブザーブを開始・終了するタイミングが少しハック的な実装になっています。

……というのもオブザーブを開始した時点で参照カウンタがインクリメントされてしまうため、どこか適当なタイミングでオブザーブを終了してあげないといつまで経ってもオブジェクトが解放されなくなってしまいます。

SBPullToRefreshHeaderView の実装では、ターゲットとなっているスクロールビューの子ビューとして追加されたときにオブザーブを開始、親ビューがターゲットから外れた時点でオブザーブを終了するようにします。

- (void)didMoveToSuperview
{
  if (self.superview == mScrollView)
  { // The view has been added into the target scroll view so it starts 
    // observing contentOffset changes.
    [mScrollView addObserver:self 
                  forKeyPath:@"contentOffset" 
                     options:NSKeyValueObservingOptionNew 
                     context:nil];
  }
  else
  { // The view has been removed from the target scroll view so it stops 
    // observing contentOffset changes.
    [mScrollView removeObserver:self
                     forKeyPath:@"contentOffset"];
  }
}

UIView の 「didMoveToSuperview」メソッドで親ビューの変化をキャッチできるので、そこでオブザーブの開始・終了を制御しています。

GitHub にサンプルデモと一緒に置いてみましたので、もしよろしければお使いください (MIT-License)。

スポンサーリンク

Xcode 4.3 リリース

二月になってしまいましたが、2012 年もよろしくお願いします。

Xcode 4.3 がリリースされました。

Mac App Store との親和性を高めるため、Xcode 4.3 からはインストール先が変更されています。

  • Xcode 4.2 まで : /Developer
  • Xcode 4.3 から : /Applications

インストール方法も変わりました。Xcode 4.2 までは何らかの方法で Xcode のインストーラを起動する必要がありました。

Xcode 4.2 では Mac App Store からダウンロードされるものは「Xcode Install」で Xcode そのものではありませんでした。

Xcode 4.3 からはインストーラは目に見えては存在しません。App Store からダウンロードして、そのまま他のアプリケーションと同じように起動するだけです。

Xcode 4.3 はインストールされる場所が最大の違いで、機能的な違いはほとんどない印象です。

Xcode に付録されるユーティリティ

Xcode 4.2 まで /Developer/Applications の中にあった Instruments や Icon Composer, FileMerge といったユーティリティは Xcode 4.3 から Xcode アプリケーション内に内包されています。

Dashcode, Quatz Compozer などこれまで Xcode に付録 (?) として付いてきたいくつかのアプリケーションは Xcode 4.3 には含まれていません。

developer.apple.comダウンロードセンターから別途ダウンロードする必要があります。

  • Dashcode → Dashcode for Xcode - February 2012
  • Quartz Composer → Graphics Tools for Xcode - February 2012

Instruments など Xcode 4.3 に内包されているアプリケーションを起動するには Xcode を立ち上げて、Xcode メニューより「Open Developer Tool」を利用します。

[図] デベロッパツールメニュー。

Xcode に内包されているアプリケーションでも起動させた状態で Dock に保持するようにすれば、Dock から (Xcode を通さずに) 立ち上げることができます。Xcode に内包されているアプリケーションは Spotlight では拾ってくれないようです。

コマンドラインツール

Xcode 4.3 にはもちろんコンパイラは付いてきますが、Xcode 4.2 まで /Developer/usr/bin に含まれていたいくつかのツールは含まれていません。前述のダウンロードセンターからダウンロードするか、設定画面の「Downloads」より「Command Line Tools」をダウンロードします。

[図] Xcode 4.3 設定内のダウンロード。

Xcode 4.3 からの仕様変更により OS X のパッケージマネージャ Homebrew は Developer Preview の時には動作しなくなってしまいましたが、Command Line Tools をちゃんと入れれば、大丈夫な様子。

以前の Xcode との共存

Xcode 4.3 はこれまでの Xcode と同様、以前のバージョンの Xcode と共存が可能です。一番最初に Xcode 4.3 を立ち上げたときにインストールされている Xcode をゴミ箱に移動するかどうか聞かれます。

[図] Xcode 4.3 最初の起動時に表示されるダイアログ。

該当するディレクトリを単にゴミ箱に移すだけのようで、/Developer/Library/uninstall-developer-folder は実行されない様子。

ヘルプでは「(古い Xcode は) 後からでもいつでもゴミ箱に入れられます」とあって、uninstall-developer-folder の実行には触れられていません。ゴミ箱に移して削除すればアンインストールされるんでしょうか。

スポンサーリンク

Re: Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)

JunichiIto さんによる「Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)」という記事を見かけて自分でもやってみる。

正確に時間を測ったわけではありませんが、問題 1 と問題 2 あわせて一応 90 分で解けたと思います。

問題1: Excel列名変換問題
  • 仕様
    • 入力されたアルファベットを数字に変換する。
    • 変換ルールはExcelの列名と同等。
    • 例) A=1、B=2、Z=26、AA=27、XFD=16384
  • 起動時引数
    • [0] アルファベット (A〜ZZZZ...[上限なし])
  • 実行例
    • ExcelColConv.pl A → 1
    • ExcelColConv.pl AA → 27

[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]

問題 1 に対する私の回答

問題2: Excel列名変換問題(逆変換)
  • 仕様
    • 入力された数字をアルファベットに変換する。
    • ただし、問題1で作ったプログラムを拡張すること。
  • 起動時引数
    • [0] 0=数字へ変換、1=アルファベットへ変換
    • [1] 変換する数字またはアルファベット(どちらも上限なし)

[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]

問題 2 に対する私の回答

問題 1 回答

#!/bin/perl

if ($ARGV[0] =~ /[A-Za-z]+/)
{
  print &convert(uc($ARGV[0])) . "\n";
}
else
{
  &usage();
}
exit 0;

sub usage
{
  print 'Usage: ',$0,' input',"\n";
}

sub convert
{
  my $input  = shift;
  my $output = 0;

  my $rank = 0;
  foreach my $char ( reverse(split(//,$input)) )
  {
    my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1;
    $output += $num * (26 ** $rank++);
  }

  return $output;
}

アプローチとしては、3 位の方と同等ですね。一応、入力パラメータチェックしてます。

問題 2 回答

#!/bin/perl

if ($ARGV[0] eq '0' and $ARGV[1] =~ /[A-Za-z]+/)
{
  print &convert(uc($ARGV[1])) . "\n";
}
elsif ($ARGV[0] eq '1' and $ARGV[1] =~ /[0-9]+/ and $ARGV[1] > 0)
{
  print &rev_convert($ARGV[1]) . "\n";
}
else
{
  &usage();
}
exit 0;

sub usage
{
  print 'Usage: ',$0,' dir input',"\n";
  print '       dir 0 : alpha to num [input must be alphabet]'."\n";
  print '       dir 1 : num to alpha [input must be number greater than 0]'."\n";
}

sub convert
{
  my $input  = shift;
  my $output = 0;

  my $rank = 0;
  foreach my $char ( reverse(split(//,$input)) )
  {
    my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1;
    $output += $num * (26 ** $rank++);
  }

  return $output;
}

sub rev_convert
{
  my $input  = shift;
  my $output = '';
  my @chars = split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
  my $base = @chars;

  my @stack = ();
  while ($input > $base)
  {
    my $num = $input % $base;
    push(@stack, $chars[$num - 1]);
    $input = int($input / $base);
    # Needs to decrement $input if $input value is in multiple of $base
    if ($num == 0)
    {
      $input--;
    }
  }
  # Now handles within $base case
  push(@stack, $chars[$input % $base - 1]);

  return join('',reverse(@stack));
}

入力数値が基数にあたる 26 の倍数の時、桁を正しく処理する必要があるのがトリッキーな部分になるでしょうか。

一応、ざっくり確認したつもりですが、間違っていたらごめんなさい。

[2010.11.04 03:30 追記] perl の場合、配列に対して負のインデックスを指定すると、配列の後ろからアクセスするという仕様があります。なので、$input % $base の結果が 0 になる場合、@stack には "Z" が格納されます。

スポンサーリンク

FloatyMemo / FloatyMemo+ ver 1.09 リリース

FloatyMemo ならびに FloatyMemo+ ver 1.09 がリリースされました。

今回は FloatyMemo+ で「メモ一覧」をサポートしたのがもっとも大きな変更になります。

iTunes Store で頂いた takuxya さんの要望

できれば、
■□□□□□□□・・・
↑の現状の仕様通り、右にページが増えるモードと別に、

□□□
□■□
□□□
↑のようにタイル状にページが増えるモードが欲しいです。
一番右のページまでスクロールさせるのが結構時間かかるので。

[→FloatyMemo+ カスタマレビュー]

をアレンジして実装した形になります。アイデアをご提供いただき、ありがとうございました。

新たに追加された「メモ一覧」ボタンをタップするか、ページ自体をピンチすることで「メモ一覧」モードに移行します。

FloatyMemo+ 自体は横一列にページが配置されていますが、ページ一覧ではスペースをより効果的に利用するため、各ページがタイル状に配置されます。

[イメージ]メモの一覧表示

以下、技術的なお話。

「メモ一覧」では、各ページのスナップショットをイメージとして取得して、それをタイル状に並べることで実装しています。

この「スナップショット」は、UIKit の UIGraphicsGetImageFromCurrentImageContext() 関数を利用することによって実現しています。

ただ、そのままの実装だと、最大までページを追加した状態 (16 ページ) だと、簡単にメモリ不足に陥ってしまい、OS から強制終了されてしまいます。

とくに初代 iPad だとあっさり落ちることが多く、メモリ不足で強制終了された場合、デバグもできない状態になります。

最初はメモリの警告を受け取ったら、それ以降はサムネイルを作成しないようにしたんですが、一部しかページサムネイルが表示されないのはあまりにあんまりです。

UIKit のドキュメントをつらつら見ていたら、UIGraphicsBeginImageContextWithOptions 関数に気づきました。

イメージコンテキストを初期化する関数 UIGraphicsBeginImageContext に追加のオプションが指定できる関数で、iOS4 から利用できます。

この関数の三番目の引数 CGFloat scale で iPhone4 などの Retina ディスプレイでは高解像度のイメージが取得できます。

UIGraphicsBeginImageContextWithOptions(view.bounds.size,
                                       view.opaque,
                                       [[UIScreen mainScreen] scale]);

のように [[UIScreen mainScreen] scale] を渡すとデバイスの解像度に応じたイメージが取得できます。

この scale に実際の解像度より小さい値を渡すと、実際よりも低い解像度のイメージを取得することもできます。そうすることで使用メモリも大幅に減らすことができます。

今回のようにサムネイルとして利用する場合は、実際の解像度は必要ありません。むしろ利用するメモリを制限するために、あえて低解像度のイメージを取得するようにします。

UIGraphicsBeginImageContextWithOptions(view.bounds.size,
                                       view.opaque,
                                       [[UIScreen mainScreen] scale] * 0.5);

これで初代 iPad で最大ページを利用していてもかなり安定して動作するようになりました。

iOS4 より前の OS でも動作するようにするためには UIGraphicsBeginImageContextWithOptions が利用できるかチェックする必要があります。

if (UIGraphicsBeginImageContextWithOptions != NULL)
{
  UIGraphicsBeginImageContextWithOptions(view.bounds.size,
                                         view.opaque,
                                         [[UIScreen mainScreen] scale] * 0.5); 
}
else
{
  UIGraphicsBeginImageContext(view.bounds.size);
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

iOS4 より前では、解像度の指定はできなくなってしまいますが、マルチタスクでもないため、とりあえず、これでよいかなと思っています。

スポンサーリンク

OS X Lion で FastCGI を利用する

Mac OS X 10.6 Snow Leopard のときには FastCGI が標準でインストールされた状態でした。

ですので、FCGI モジュールを CPAN 経由でインストールして、conf ファイルで FastCGI モジュールを読み込むように設定するだけで FastCGI に関する基本的な設定 (perl CGI で FastCGI を利用するための下準備) は完了できました。

しかしながら、Mac OS X 10.7 Lion では標準で FastCGI がインストールされていませんので、自前でインストールする必要があります。

FastCGI 最新版と FastCGI Apache2 モジュール最新版を fastcgi.com からそれぞれ取得します。

まずはいつものように FastCGI をインストール。

$ tar zxf fcgi-current.tar.gz
$ cd fcgi-(VERSION)
$ ./configure
$ make
$ sudo make install

続いて Apache のモジュールをインストール。

$ tar zxf mod_fastcgi-current.tar.gz
$ cd mod_fastcgi-(VERSION)
$ apxs -o mod_fastcgi.so -c *.c
$ sudo apxs -i -a -n fastcgi mod_fastcgi.la

参考 : Running Movable Type on OS X with FastCGI & apxsでapacheにモジュールを追加する

perl の FCGI モジュールは cpan コマンドでインストールしておきます。

httpd.conf で インストールした FastCGI をロードするように追加します。

LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so

最後に Apache2 を再起動して設定終了です。

$ sudo apachectl restart

スポンサーリンク

FloatyMemo / FloatyMemo+ ver 1.08 リリース

FloatyMemo ならびに FloatyMemo+ ver 1.08 がリリースされました。

今回はシゴタノさんのところ「シンプルに使えるiPad付箋アプリ3選」で要望 (?) として挙がっていた

付箋新規作成のアクション(画面ダブルタップが望ましい)

[→シンプルに使えるiPad付箋アプリ3選]

を追加機能として盛りこんでみました。

倉下忠憲さん、ご紹介いただきありがとうございます。

また、同時にご利用いただいているユーザーから頂いたメモ位置の保存に関する不具合を修正しています。

FloatyMemo / FloatyMemo+ ではメモ位置をその時の座標位置をそのまま保存しています。

縦画面・横画面も一応考慮しているのですが、iOS の仕様上、アプリケーション開始時に横画面になっていても、内部では常に縦画面で開始される動作になっていました。

この動作に基づいて座標の変換処理を入れるようにしましたが、誤差が結構ある様子なので、FloatyMemo / FloatyMemo+ の再起動を何度も繰り返すと徐々にずれていってしまいます。なので完璧ではありませんが、とりあえず以前の動作よりもずいぶん素直な動作になったと思います。

スポンサーリンク

Safari 5.1 の機能を強化する - SIMBL プラグイン・機能拡張

MacBook Pro セットアップ - デフォルトアプリケーション編」でも触れたとおり、Mac OS X Lion 導入を機に Safari をデフォルトウェブブラウザとして利用するようになっています。

Safari 用の SIMBL プラグイン

Safari の機能を強化する定番 SIMBL プラグインに SafariStand があります。

SafariStand には色々な機能がありますが、その中でも個人的に重宝していた機能は

  • セッション復元機能
  • アドレスバーのキーワードサポート

の二つです。前者は Safari 5.1 で正式にサポートされるようになりましたが、後者は Safari 単体ではまだできないようです。

最近、このキーワードをサポートする Safari Omnibar という SIMBL プラグインを見つけました。

Safari Omnibar のもっとも特徴的な機能はアドレスバーと検索バーの統合です。Safari Omnibar をインストールすると、検索バーがなくなります。アドレスバーが検索も兼ねる Google Chrome のような動作になります。

キーワードは SafariStand 同様カスタマイズすることができます。SafariStand とも併用できますが、キーワードが重複していると、Safari Omnibar の方が優先されます。

Safari Omnibar でキーワードをカスタマイズするには、アドレスバーでコンテキストメニュー (右クリックあるいは Ctrl + 左クリック) を表示して「Edit Omnibar Search Providers ...」を選択します。

[イメージ] Safari Omnibar のメニュー

{searchTemrs}が入力された検索ワードで置き換わります。「Set as Default」ボタンで選択されたプロバイダをデフォルトプロバイダとして設定します。

[イメージ] Safari Omnibar 検索プロバイダの設定

デフォルトになったプロバイダは「編集」メニュー「検索」でも利用される他、「?」キーワードでも利用できるようになります。

Safari の機能拡張

Safari では ver 5 より機能拡張に対応しています。2011.08.14 現在、インストールした機能拡張は以下の通り。

Hatena Bookmark Safari Extension

見ているページをはてなブックマークでブックマークしたり、ブックマークコメントを見たいときに利用します。

Firefox 版 / Chrome 版もあります。

AutoPagerize

複数のページに別れたウェブページをページ遷移することなく、その場で継ぎ足しロードしてくれる定番の機能拡張です。

Firefox 版 / Chrome 版もあります。

User CSS

特定のウェブページに独自のスタイルシートを適用することができる機能拡張です。

Firefox / Chrome では Stylish という機能拡張を使って、特定ページのスタイルをカスタマイズしていましたが、User CSS でもほぼ同様なことができます。Stylish のように作成したスタイルシートを共有する仕組みがありませんが、私の利用範囲では十分。

AdBlock

広告イメージやフラッシュを非表示にしてくれる定番機能拡張です。

Firefox 版 / Chrome 版もあります。

Ultimate Status Bar

Firefox 5 や Google Chrome のような可変長ステータスバーを表示してくれる機能拡張です。

気がつくとこちらのスタイルにすっかり慣れてしまっていたので、導入してみました。

時々うまく表示されないことがあったりしますが、それ以外は快適に利用できています。

スポンサーリンク

2/13