Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

本日見かけた酷いコード13

タグ: C++ 酷いコード
// クラス定義
class Foo
{
public:
    static Foo* GetInstance();
 
    static void Init();
    static void Function();
    // 以下、静的メンバ関数が続く
 
private:
    Foo() {}
    ~Foo() {}

    Foo(const Foo&);
    Foo& operator=(const Foo&);

    static Member1 member1;
    static Member2 member2;
    // 以下、静的データメンバが続く
};
// 呼び出し側はこんなコード
Foo::GetInstance()->Function();

コンストラクタとデストラクタが非公開で、インスタンスを取得できるのは唯一静的メンバ関数からのみ。「シングルトンパターンを知ったので使ってみました」的なコードだが、そんなことよりまずはクラスとインスタンスの違いを認識しろと。
クラスFooには静的メンバ関数(クラスメソッド)しか存在せず、静的ではないメンバ関数(インスタンスメソッド)が1つも存在しない。だからそもそもシングルトンにする(インスタンスの数を1つに制限する)とか以前に、インスタンスを作る意味がない。

// わざわざ Foo::GetInstance() しなくとも……
Foo::Function();

「メンバをことごとくstatic」にした上さらに「シングルトンにする」など、そうそうやることではない。
やるとしたら……「クラスとインスタンスの違い」をさほど重要視しておらず、「コード変更量を最小に抑える」ことをポリシーにしている人だろうか。
そういう人なら、

  1. 静的メンバだけのクラスFooを作る。
  2. 最初に1度だけ Init() が呼ばれるよう、シングルトンぽくする。
  3. 静的メンバのstaticを外さなくても、ちゃんと GetInstance() さえ呼んでくれれば動作に支障は生じないので、無駄にコード変更量を増やさないためにstaticは外さない。

なんてことをやってもおかしくはない。勘弁してほしいが。

iPhone SDK 3.0で変わったこと

タグ: iPhone Objective-C

iPhone SDK 3.0がリリースされたので、2系向けに書いた記事から変更された部分のうち気付いたものをざっとまとめておく。

まず、2系のSDKには存在しなかった「iPhone向けSenTestingKitフレームワーク」が追加された。
これによって、Google Toolbox for Macを使わなくても、Simulator向けならこれまで通り「ビルド時」に、Device向けなら「アプリケーションの実行時」にテストを走らせることが可能となった。
詳しいことは『iPhone Developement Guide: Unit Testing Applications』を参照。

UIScrollViewには、2系ではなぜか用意されていなかった「拡大率を直接変更するためのメソッド/プロパティ」が追加された。
また、「スクロールの減速度」を調整するプロパティが追加された。
実は2系でも

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    [scrollView setValue:[NSValue valueWithCGSize:CGSizeMake(0.99, 0.99)]
                  forKey:@"decelerationFactor"];
    [scrollView setValue:[NSNumber numberWithFloat:-0.01]
                  forKey:@"decelerationLnFactorH"];
    [scrollView setValue:[NSNumber numberWithFloat:-0.01]
                  forKey:@"decelerationLnFactorV"];
}

とか書くと減速率を調整できたりするのだが、だいぶ無理矢理な方法なので、正式な調整手順が追加されたのは喜ばしい。
厳しいのは、UIScrollViewのジェスチャー回りの流れが変わったために「touchesMoved:等をオーバーライドして操作を拡張する」のが難しくなっていること。
「UIScrollViewにあって然るべきものは揃えたから小細工せずに素直に使え」ということだろうか。

@synthesize ivar declared in super class

タグ: Objective-C Mac iPhone
// .h
@interface Foo : NSObject
{
    int n;
}
@end

@interface Bar : Foo
@property int n;
@end
// .m
@implementation Foo
@end

@implementation Bar
@synthesize n;
@end

上記のコードは「Mac OS X(32-bit Universal)」や「iPhone Simulator」といったレガシーランタイム向けのコンパイルではエラーにならないが、iPhone SDK 3.0で「Mac OS X(32/64-bit Universal)」や「iPhone Device」といったモダンランタイム向けにコンパイルするとエラーになる。

error: property 'n' attempting to use ivar 'n' declared in super class of 'Bar'

これはモダンランタイムで 親クラスのインスタンス変数をsynthesizeすると親クラスのサイズが変わった時にクラッシュやら未定義動作を引き起こす要因になる(iPhone SDK Release Notes for iPhone OS 3.0 ためで、SDK 3.0ではこのようなコードがちゃんとコンパイルエラーになるように修正された。
モダンランタイムには 親クラスのインスタンス変数を並び替えてもそれを継承する子クラスを再ビルドする必要はない(Objective-C 2.0 Runtime Programming Guide: Runtime Versions and Platforms という利点があるが、おそらくこれはその辺に関係した問題なのだろう。

今回のようなコードなら、以下の様に書けば少なくともヘッダには影響を与えることなく対応できる。

// .m
@interface Foo ()
@property int n;
@end

@implementation Foo
@synthesize n;
@end

@implementation Bar
@dynamic n;
@end

もちろん、普通にsetterとgetterを書いてもいい。

Objective-Cのランタイムとモダンプロパティ

タグ: Objective-C Mac iPhone

「同名のインスタンス変数が存在しないプロパティ」を「代替となるインスタンス変数の指定なし」で @synthesize すると、レガシーランタイム(Mac OS Xの32bitプログラム)向けのコンパイルでは「エラーになる」が、モダンランタイム(Mac OS X 10.5?の64bitプログラム及びiPhoneアプリケーション)向けのコンパイルでは「代替のインスタンス変数が勝手に用意される」。
(『The Objective-C 2.0 Programming Language: Declared Properties』より)

@interface MyClass : NSObject
@property (retain) id property;
@end
 
@implementation MyClass
@synthesize property; // iPhone Device向けではエラーにならない
@end

いちいち同名のインスタンス変数を書くのも面倒だし、これはこれで良いのだが、

If you are using the modern runtime and synthesizing the instance variable, however, you cannot access the instance variable directly, so you must invoke the accessor method:

- (void)dealloc {
    [self setProperty:nil];
    [super dealloc];
}

そこまでやってなおdeallocでrelease相当のものを要求するのはなぜだ……
「@synthesizeはretain/releaseを考慮したsetter/getterを自動生成してくれる」のだから、「@synthesizeが自動生成するインスタンス変数はdeallocで必要に応じてreleaseされる」くらいやってくれてもいいと思うのだが。

でもまあ、この「モダン」な書き方、今のところあまり使いどころがない。
iPhoneではたしかに使えるが、「Mac OS Xの32bitプログラム」であるiPhone Simulatorでは使えないわけで……

プロトコルのメソッドへのリンク(その2)

タグ: Doxygen Objective-C

以前、Doxygenでプロトコルのメソッドへのリンクを発生させる方法を書いたが、実はこの構文は引数が2つあると成立しない
なので他に方法はないものかと色々試してみたところ、

  • 「@link」なら引数が2つでもリンクを生成できる
  • 同じプロトコル内なら「#」で問題なくメンバへのリンクを自動生成できる

ということが分かった。

/** 
 * @brief プロトコル Foo
 * 
 * - @@ref
 *   - @ref Foo-p::bar:
 *   - @ref Foo-p::baz:qux:
 * - #
 *   - #bar:
 *   - #baz:qux:
 * - @@link
 *   - @link Foo-p::bar: @endlink
 *   - @link Foo-p::baz:qux: @endlink
 */  
@protocol Foo  
/** 
 * @brief 引数1つ。
 */  
- (void)bar:(int)bar;
/** 
 * @brief 引数2つ。
 */  
- (void)baz:(int)baz qux:(int)qux;  
@end
doxygen159_protocol_self.png
/**
 * @defgroup group
 *
 * - @@ref
 *   - @ref Foo-p::bar:
 *   - @ref Foo-p::baz:qux:
 * - #
 *   - #bar:
 *   - #baz:qux:
 * - @@link
 *   - @link Foo-p::bar: @endlink
 *   - @link Foo-p::baz:qux: @endlink
 */
doxygen159_protocol_link.png

だいぶ無理矢理だが、今(Doxygen 1.5.9)のところはまあこれでいいか。

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。