Entries

スポンサーサイト

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

単純な間違いはなくなりません

Single misplaced '&' caused latest IE exploit - CNET News

なんと模範的な「C形式キャストによる失敗例」。
static_castを使っていれば……って、ポインタ型からvoid*へならそもそもキャストいらないか。
「すぐ近くにあるコードと書き方が揃っているから正しいと錯覚してしまう例」とも言える。間違い方のパターンとしては割とよくある。

それはともかく。

HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast(&pbArray));

CNETの記事のこの部分。reinterpret_castにテンプレート引数がない。
元記事の方にはあるのに、どこに消えた……? と思ったら

<p>The code lines he listed were:<br />
__int64 cbSize;<br />
hr = pStream->Read((void*) &cbSize, sizeof(cbSize), NULL);<br />
BYTE *pbArray;<br />
HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast<LPVOID *>(&pbArray));<br />
hr = pStream->Read((void*)&pbArray, (ULONG)cbSize, NULL);
</p>

実体参照していないからタグ扱いされているらしい。
( ´∀`) あるある。

そしてこの記事、日本語版では

<p>
__int64 cbSize;<br />
hr = pStream->Read((void*) &cbSize, sizeof(cbSize), NULL); <br />
BYTE *pbArray; <br />
HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast(&pbArray)); <br />
hr = pStream->Read((void*)&pbArray, (ULONG)cbSize, NULL);
</p>

テンプレート引数はもはやソースにも残っていなかった。

こうして人知れず情報は抜け落ちていくのであった。

cmake Boost

タグ: Xcode Boost iPhone

そういえばBoostは1.38.0以降、CMakeによるビルド環境構築に対応しているんだっけ。
ちょっと使ってみるか。

まずはBoostのルートディレクトリでcmake。

$ cmake -GXcode .

「これはCMakeベースのビルド環境で未だ開発途上にある試したければ云々」と但し書きが表示されるので、指示に従って(引数を追加して)再度cmake。

$ cmake -DCMAKE_IS_EXPERIMENTAL=YES_I_KNOW .

生成されたプロジェクトファイルを開く。

$ open Boost.xcodeproj
boost_cmake1.png

んー、このプロジェクトはiPhone向けのビルドもできるのかな?
構成を「Release」、ベースSDKを「iPhone Device 3.0」にして、

boost_cmake2.png

「libboost_serialization-mt.a」をビルドしてみよう。

boost_cmake3.png

デキタワァ(n'∀')η゚*。:*

うん、ちゃんとリンクできるし、問題なく動作する。
必要なライブラリをちまちまビルドする分にはこれで十分かな。

カテゴリのメソッドがリンクされない

タグ: Objective-C iPhone
// Foo.h
#import <Foundation/Foundation.h>

@interface Foo : NSObject
- (void)methodInClass;
@end
// Foo.m
#import "Foo.h"

@implementation Foo
- (void)methodInClass
{
    NSLog(@"methodInClass");
}
@end
// Foo+Foo.h
#import "Foo.h"

@interface Foo (Foo)
- (void)methodInCategoryFoo;
@end
// Foo+Foo.m
#import "Foo+Foo.h"

@implementation Foo (Foo)
- (void)methodInCategoryFoo;
{
    NSLog(@"methodInCategoryFoo");
}
@end

以上4ファイルからiPhone向けの静的ライブラリを作成。

作成したライブラリをアプリケーションに-ObjCフラグ付きでリンクし、main.mで

#import <Foo.h>
#import <Foo+Foo.h>

@interface Foo (Mine)
- (void)methodInMyCategory;
@end

@implementation Foo (Mine)
- (void)methodInMyCategory
{
    NSLog(@"methodInMyCategory");
}
@end

int main(int argc, char *argv[]) {
    
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    Foo * foo = [[[Foo alloc] init] autorelease];
    [foo methodInClass];
    [foo methodInMyCategory];
    [foo methodInCategoryFoo];

このようにして使ってみると、実機で実行した場合に限り、なぜかFooカテゴリのメソッドだけ呼び出すことができない(iPhone SDK 3.0)。

methodInClass
methodInMyCategory
*** -[Foo methodInCategoryFoo]: unrecognized selector sent to instance 0x113fe0

実機用の実行ファイルをnmしてみると、たしかにFooカテゴリのメソッドは見当たらない。

000021a4 t -[Foo methodInClass]
000020c4 t -[Foo(Mine) methodInMyCategory]

この現象は、Foo+Foo.mの中身(Fooカテゴリの実装)をFoo.mに移すことで解消される。

methodInClass
methodInMyCategory
methodInCategoryFoo
000021b8 t -[Foo methodInClass]
000021a4 t -[Foo(Foo) methodInCategoryFoo]
000020c4 t -[Foo(Mine) methodInMyCategory]

以上のことから考えるに、どうやらiPhone実機向けの静的ライブラリリンクでは、クラスの実装とは異なる中間ファイルで実装されたカテゴリのメソッドをリンクすることができないようだ。

この現象を回避するには、「クラスとカテゴリの実装を同じファイルにまとめる」ほかに、「他のリンカフラグ」に「-all_load」を追加するという方法もある。
これはこれで、逆に余計なものまでリンクするような気もするが……

NSCodingとBoost.Serialization

タグ: Objective-C C++ Boost
struct GPSPosition
{
    int degree;
    int minutes;
    float seconds;
};

こんな構造体があって

#import <Foundation/Foundation.h>

@interface Foo : NSObject <NSCoding>
{
@public // ←手抜き
    GPSPosition pos;
}
@end

こんなインタフェースがあったら

@implementation Foo
- (id)initWithCoder:(NSCoder*)decoder
{
    if (self = [super init])
    {
        pos.degree = [decoder decodeIntegerForKey:@"pos.degree"];
        pos.minutes = [decoder decodeIntegerForKey:@"pos.minutes"];
        pos.seconds = [decoder decodeFloatForKey:@"pos.seconds"];
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder*)encoder
{
    [encoder encodeInteger:pos.degree forKey:@"pos.degree"];
    [encoder encodeInteger:pos.minutes forKey:@"pos.minutes"];
    [encoder encodeFloat:pos.seconds forKey:@"pos.seconds"];
}
@end

こんな風に実装することで

Foo* foo1 = [[[Foo alloc] init] autorelease];
foo1->pos.degree = 34;
foo1->pos.minutes = 17;
foo1->pos.seconds = 8.5;
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:foo1];
Foo* foo2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%d,%d,%f", foo2->pos.degree, foo2->pos.minutes, foo2->pos.seconds);

オブジェクトをシリアライズできる。

34,17,8.500000

実に簡単ヽ( ´ー`)ノ

以上はObjective-CとFoundationフレームワークの話だが、C++の場合はBoost.Serializationを使うことでこれまた簡単に構造体やクラスのシリアライズができる。

namespace boost {
namespace serialization {

template <typename Archive>
inline void serialize(Archive& ar, GPSPosition& pos, const unsigned int version)
{
    ar & pos.degree & pos.minutes & pos.seconds;
}

} // namespace serialization
} // namespace boost

そしてObjective-CはObjective-C++によってC++も扱えるので、このようにすればBoost.Serialization用に定義したシリアライズ関数をNSCodingの実装でも流用できる。

#include <vector>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

template <typename T>
inline void encode(const T& target, NSCoder* encoder, NSString* key)
{
    typedef std::vector<char> bytes_type;
    typedef boost::iostreams::back_insert_device<bytes_type> device_type;
    
    bytes_type bytes;
    {
        boost::iostreams::stream_buffer<device_type> stream_buffer(bytes);
        boost::archive::binary_oarchive boa(stream_buffer);
        boa << target;
    }
    [encoder encodeBytes:reinterpret_cast<uint8_t*>(&bytes[0]) length:bytes.size() forKey:key];
}

template <typename T>
inline void decode(T& target, NSCoder* decoder, NSString* key)
{
    typedef boost::iostreams::array_source device_type;
    
    NSUInteger length;
    const uint8_t* bytes = [decoder decodeBytesForKey:key returnedLength:&length];
    
    boost::iostreams::stream_buffer<device_type> stream_buffer(reinterpret_cast<const char*>(bytes), length);
    boost::archive::binary_iarchive bia(stream_buffer);
    bia >> target;
}
@implementation Foo
- (id)initWithCoder:(NSCoder*)decoder
{
    if (self = [super init])
    {
        decode(pos, decoder, @"pos");
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder*)encoder
{
    encode(pos, encoder, @"pos");
}
@end

UIButtonの継承

タグ: iPhone Objective-C
@interface MyButton : UIButton
@end

@implementation MyButton
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        NSLog(@"(´ω`*)");
    }
    return self;
}
@end
NSLog(@"%@", [[[MyButton alloc] init] autorelease]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeCustom]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeRoundedRect]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeDetailDisclosure]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeInfoLight]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeInfoDark]);
NSLog(@"%@", [MyButton buttonWithType:UIButtonTypeContactAdd]);

iPhone SDK 3.0 + Simulator 3.0 での実行ログ。

(´ω`*)
<MyButton: 0xd1aea0; baseClass = UIButton; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0xd18f70>>
(´ω`*)
<MyButton: 0xd1d460; baseClass = UIButton; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0xd1dcf0>>
<UIRoundedRectButton: 0xd1d920; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0xd1d600>>
<UIButton: 0xd27c30; frame = (0 0; 29 31); opaque = NO; layer = <CALayer: 0xd19d60>>
<UIButton: 0xd28cd0; frame = (0 0; 18 19); opaque = NO; layer = <CALayer: 0xd28d50>>
<UIButton: 0xd29790; frame = (0 0; 18 19); opaque = NO; layer = <CALayer: 0xd295a0>>
<UIButton: 0xd29ca0; frame = (0 0; 29 29); opaque = NO; layer = <CALayer: 0xd29a40>>

buttonWithType: で MyButton のインスタンスが返されるのは引数が UIButtonTypeCustom の時だけか……

opaque な UIView はちゃんと塗りつぶさないと

タグ: iPhone Objective-C
@interface MyView : UIView
{
    int n;
}
@end

@implementation MyView
- (void)drawRect:(CGRect)rect {
    n = (n + 1) * 10;
    [[UIColor whiteColor] set];
    NSString *string = [NSString stringWithFormat:@"%d", n];
    [string drawInRect:rect withFont:[UIFont systemFontOfSize:20]];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self setNeedsDisplay];
}
@end
- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(10, 10, 400, 200)];
    assert(myView.clearsContextBeforeDrawing); // 初期値の確認
    assert(myView.opaque); // 初期値は「不透明」のはず
    [viewController.view addSubview:myView];
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

タップされる度に「1桁目が0でそれ以外が1」の数字を桁を増やしながら描くビューを追加するコード。
9回ほどタップするとオーバーフローするがまあそれはさておき。
これをアクティブSDK「Simulator 2.2.1」でビルドするとこうなるが、

clearsContextBeforeDrawing_221.png

アクティブSDK「Simulator 3.0」でビルドするとこうなる。

clearsContextBeforeDrawing_30.png

2.2.1では毎回黒塗りされるからてっきり backgroundColor が nil ならopaqueなビューの背景は「不透明な黒([UIColor blackColor])」になるかと思ったがそんなことはなかった。
opaque を NO にするか、 backgroundColor になんらかの色を設定すれば、3.0でも綺麗にクリアしてくれるようになる。

opaqueなビューは、 ビュー全体を不透明色で塗りつぶさなければどうなるか分からない ので、 drawRect: で完全に塗りつぶされないなら backgroundColor の設定は必須。

The results are unpredictable if opaque and the view doesn’t fill its bounds.

clearsContextBeforeDrawing を YES にしても、 drawRect: の前にグラフィックコンテキストが「透明な黒([UIColor clearColor])」でクリアされるようになる だけなので、ビューを不透明に塗りつぶす手助けにはならない。

If YES, the current graphics context buffer in the drawRect: method is automatically cleared to transparent black before drawRect: is invoked.

gcc 4.2の奇妙なコンパイルエラー

タグ: Objective-C
@interface Foo : NSObject
@end

@interface Bar : NSObject
{
    Foo *foo;
}
@property (nonatomic, readonly) NSObject *foo;
@end
Bar *bar = [[Bar alloc] init];
NSObject *foo1 = [bar foo]; // no error
NSObject *foo2 = bar.foo; // error: type of accessor does not match the type of property 'foo'

( ゚д゚) ……

@interface Baz : NSObject
{
    Foo *_foo;
}
@property (nonatomic, readonly) NSObject *foo;
@end
Baz *baz = [[Baz alloc] init];
NSObject *foo1 = [baz foo]; // no error
NSObject *foo2 = baz.foo; // no error

意味が分からない。
「プロパティと同名だが型の異なるインスタンス変数」が存在するだけでなぜ「アクセサの型とプロパティの型が一致しなく」なる?
少なくともインタフェース部だけではプロパティとインスタンス変数は関連付かない。

@synthesize foo;

がエラーになるというならまだ分かるが……

Appendix

タグ

Blog内検索

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