統計情報(30日間)


最新情報をツイート


人気の投稿

Objective-C に Modules がやってきた!

このエントリーをはてなブックマークに追加

Modulesの他、iOSの新しいトピックについても多数解説(NSProgressなど)。



注目は Modules。
Modulesは今までのinclude(Objcだとimport)を置き換える新しい仕組み。メリットとしては
・#includeの順序依存による問題を解消(定義順序によって挙動が変わる・名前衝突でエラーなど)
・ビルドシステム向けのメタ情報の埋め込み(公開APIの定義や、リンクリンクライブラリの指定など)

Xcode5ではデフォルトでモジュールが使えるようになっている。従来のプロジェクトでは明示的に有効にする必要あり。
設定:Apple LLVM 5.0 - Language - Modules


モジュールを使うには #import の代わりに @import を使う。
こんな感じ。
@import UIKit;
@import MapKit;
@import iAd;
iOS/OSXの場合、フレームワーク名を指定する感じになる。

モジュールにはサブモジュールを定義することができて、特定のサブモジュールだけ利用することもできる。
@import UIKit.UIView;

モジュールの特徴は本記事の最後の方にまとめてある。メリットとしてはコンパイル速度の向上などがあるが、元々Xcodeはプリコンパイルヘッダの仕組みがあるのでそこまで変わらないかもしれない。

最もメリットがありそうなのはビルド時にフレームワークの選択が不要になること。

モジュールはリンクすべきライブラリ情報を持っているので、Xcodeがそれを参考にして自動的にリンクしてくれる(Automatic Link)とのこと。

実際そうなのか試してみた。

Xcode5で新規にプロジェクトを立ち上げて MKMapViewをビューに配置してみた。
#import 
@import MapKit;

@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet MKMapView *mapView;

@end
コンパイルは通った。おお。

しかし実行するとエラーで落ちた。

XibにあるMKMapViewをインスタンス化(アンアーカイブ)できないとのこと。
MapKit.framworkを追加すると当然だが問題なく実行できる。

むむ。もしかしてXibにある(アーカイブされている)から問題なのか?
そこでXibにMKMapViewを置くのはやめ、コードでインスタンス生成してビューに貼り付けるようにしてみた。
- (void)viewDidLoad
{
    [super viewDidLoad];
    MKMapView* mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
    [self.view addSubview:mapView];
    self.mapView = mapView;
    
}
すると

できた。MapKit.frameworkは指定していない。

現時点ではXib上に配置したオブジェクトにはモジュールのAutomatic link は適用されないようだ。
この辺り今後Xcodeのバージョンアップで改善されるかもしれない。

なお紹介記事によれば内部的には#importはモジュールに変換されているようだ(なのでわざわざ#importを@importに書き換える必要は無い)。


------
以下は Modulesのとりとめないまとめ。

Modulesについては InfoQの日本語記事が詳しい(2012年提案時のニュース)。


上記の元ネタのスライド(AppleのDong Gregor氏)



スライドの中で現在のインクルードヘッダの問題点が2つ取り上げられている。
・Fragility ... 定義の順番で結果が変わる
・Performance ... 巨大で大量のヘッダとそのプリプロセッシングは遅い

これを解決する為に import キーワードを導入する(#のつかない import)。

Before
#include <stdio.h>

int main() {
 printf("Hello, world!\n");
}

After
import std;

int main() {
 printf("Hello, world!\n");
}
書き方が変わっただけでなくビルド時の解釈の仕方が従来のインクルードと大きく異り、ビルドシステムがimport情報を積極的に利用することで様々なメリットが生まれる。

Import Resilience
例えば従来のincludeだと名前空間がファイル毎に分離されていないのでシンボルが重複するとビルドが失敗する。
#define FILE "myfile.txt"
#include <stdio.h>

int main() {
 printf("Hello, world!\n");
}

一方、importの場合、他のモジュールとは独立しているのでこの問題は起きない。

#define FILE "myfile.txt"
import std;

int main() {
 printf("Hello, world!\n");
}

Selective Import
サブモジュールを定義しておけば、一部のモジュールだけ取り込むことができる。
import std.stdio;

int main() {
 printf("Hello, world!\n");
}

モジュールの書き方
こんな感じ。
// stdio.c
export std.stdio:

public:
typdef struct {
 ...
} FILE;
int printf(const char*, ...) {
 ...
}
:
・ソースファイル中にモジュール名を定義する(export ...)
・public:を使うことで外部への公開範囲を制御することができる(いままでのincludeはもちろんできない)。
・ヘッダファイル不要(!)

互換性
従来のヘッダをモジュールへ対応させる方法も提供されている。
module std {
    module stdlib { header "stdlib.h" }
    module math { header "math.h" }


Umbrella Headers
同じディレクトリ内のヘッダ全体をモジュールとして扱う方法。
module ClangAST {
    umbrella header "AST/AST.h"
    module * {}
}
ネーミングがCocoaのUmbrella Frameworksそれと同じ(Appleっぽい)。

パフォーマンス
・モジュールヘッダのパースは1回だけ(キャッシュされる) ※Objcのプリコンパイルヘッダ相当か
M x N が M + N のオーダーに。

Automatic Linking
モジュール内にリンクすべきライブラリ情報を記述できる。
module ClangAST {
    umbrella header "AST/AST.h"
    module * {}
    link "-lclangAST"
}
この情報をビルドシステムが使えば、開発者がいちいちリンクライブラリを明示的に指定する必要がなくなる。

instancetype で generics の夢をみる?

このエントリーをはてなブックマークに追加

instancetype は clang 3.1 から導入されたキーワード。



こう書いていたのを
@interface Person
+ (id)personWithName:(NSString *)name;
@end
こう書ける
@interface Person
+ (instancetype)personWithName:(NSString *)name;
@end

instancetypeを使うことでコンパイラに戻り値の型を推論させて型チェックが行えるようになる。

例えば
@interface Sample : NSObject
+ (id)factory1;
+ (instancetype)factory2;
@end
これを使ってこう書く
NSArray* s1 = [Sample factory1];
NSArray* s2 = [Sample factory2];
するとコンパイラは s2の方でワーニング出す。s1は戻り値がidなので何にでも代入できるが、s2はinstancetypeにより型が特定できるのでNSArrayとの型の違いがコンパイラによって指摘されている。

Cocoaでも、例えば UICollectionViewLayoutAttributes などで使われ始めている。

UICollectionViewLayoutAttributes Class Reference
+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath

* * * *

この記事ではinstancetype使ったgenericsっぽい実装を紹介している。例えばその実装を使うと NSURLのコレクションをこんな感じで扱える。
NSURL <MapCollection> *sites = (id)[NSURL mapCollection];
[sites put:[NSURL URLWithString:@"http://www.jonmsterling.com/"]
        at:@"jon"];
[sites put:[NSURL URLWithString:@"http://www.nshipster.com/"]
        at:@"nshipster"];

NSURL *jonsSite = [sites at:@"jon"]; // => http://www.jonmsterling.com/
sitesはNSURLのインスタンスではあるがNSURL型を保存するマップ型(NSDictionary)のコレクションとして使える。

この実装コードが下記ブログで解説されている。



実装はinstancetypeの利用の他、非形式プロトコル(NSObjectのカテゴリ)やメソッドフォワーディングを使い、型付けのされたコレクションを実現している。

先ほどのコードに続けてこんなコードも書ける。
NSString <MapCollection> *schemes = (id)sites.scheme.uppercaseString;
/* => { jon: "HTTP://",
        reddit: "HTTP://",
        foam_repo: "GIT://" }
 */

??..正直最初見たときには目が点になった...。実装を見れば実現方法がわかるのだが非常に面白い。sitesはNSURLのコレクションで、その要素1つ1つに対して schemeとuppercaseStringのメソッドを投げて、その結果をNSStringのコレクションとして受け取っている。

instancetypeはコレクションからオブジェクトを取り出す箇所で使われている。
@protocol OrderedCollection <nsfastenumeration>
- (instancetype)at:(NSUInteger)index;
- (void)put:(id)object;
@end

@protocol MapCollection <nsfastenumeration>
- (instancetype)at:(id)key;
- (void)put:(id)object at:(id)key;
@end

良い刺激になった。

(関連情報)instancetype


(関連情報)メソッドのフォワーディングは木下さんの記事がわかりやすい。実装を読み解くのに参考になる。


Objective-C の動的性質を生かすにはこれ。


__atribute__

このエントリーをはてなブックマークに追加

__attribute__はGCCから導入されたコンパイラ向けのディレクティブで Clangでも使われている。iOS/OS X関連のキーワードなどが紹介されている。先日このブログで紹介した overloadableについての記述もあり。



ライブラリを作る場合は availability が参考になる。
void f(void) __attribute__((availability(
  macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));

C でオーバーローディングが可能に (Clangフィーチャー)

このエントリーをはてなブックマークに追加

これは..





__attribute__((overloadable)) を使うと型の異なる同名の関数を複数定義できる。
例えば NSDictionaryとNSArrayにそれぞれ対応した同名のmap関数を定義した例。
__attribute__((overloadable)) NSArray *map(NSArray *array, id(^)(id obj));
__attribute__((overloadable)) NSDictionary *map(NSDictionary *dictionary, id(^)(id key, id obj));

今後 Objective-C にどんな影響があるのか楽しみ。


(関連)


instancetype

このエントリーをはてなブックマークに追加

instancetype なんてキーワードが使えるらしい。



こんな感じで使う(上記サイトからコピー)。
@implementation TextViewController 

- (instancetype)init
{
    self = [super initWithNibName:@"TextViewController" bundle:nil];
    if (self) {
//...
    }
    return self;
}

以下、引用
と書いてあり、@interface の中で使うと、指定の型チェックを行うことが出来る機能のようです。つまり、サブクラスの中で戻り値の型を適当に定義したときにエラーが表示されるようになるようです。idの場合は、基本的にどんな型を返してもエラーになりませんが、instancetypeを使うことによって、型チェックの機能をコンパイラで使用することが出来ます。

なるほど。なお
この instancetype は、Clang 3.0, xcode 4.3 から使用できる機能のようです。ただ、条件として、メソッドの戻り値にしか使用できないという条件があるようで、
だそうです。

最新 clang で @YESや dict[@"key"] が可能に

このエントリーをはてなブックマークに追加

Objective-Cのリテラル強化きた。

NSArray *array =
[NSArray arrayWithObjects: @0, @23, @3.1415, @YES, nil];
int値、float値や BOOL値 に @ を付けると NSNumberに変更される。




以下、上記サイトからの紹介。
※今回の新しいリテラル表記は clang trunk(clangの最新コード)の話で現在の Xcodeではまだ使えない。


NSArrayの初期化で @[]表記が使える。
NSArray *gronk = @[ @"hi", @"bork", @23, @YES ];


NSArray配列に []でアクセスが。
NSArray *gronk = @[ @"hi", @"bork", @23, @YES ];

NSLog (@"gronk 0: %@", gronk[0]);
NSLog (@"gronk 2: %@", gronk[2]);

入れ子
NSArray *nesting = @[ @"hi", @[ @"ohai" ] ];
NSLog (@"nesting: %@", nesting[1][0]);

-> nesting: ohai

NSMutableArrayなら代入も可能
mutt[0] = @"aaa";
mutt[3] = @"d";
mutt[4] = @"e";
さらにインデックスが範囲外の場合、自動的にインサート(insertObject:index:)される。


次に NSDictionary の初期化。"キー:値" 表記が可能に。
NSDictionary *splunge = @{ @"hi" : @"bork", @"greeble" : @"bork" };
NSLog (@"splunge %@", splunge);

->splunge {
greeble = bork;
hi = bork;
}

今までのを組み合わせるとこれが
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSFont userFontOfSize: 150.0],  NSFontAttributeName,
        [NSNumber numberWithFloat: 3.0], NSStrokeWidthAttributeName,
        [NSColor blueColor],             NSStrokeColorAttributeName,
        [NSNumber numberWithInt: 10],    NSKernAttributeName,
        [NSNumber numberWithFloat: 0.5], NSObliquenessAttributeName,
        nil];
こうなる
NSDictionary *attributes = @{
        NSFontAttributeName        : [NSFont userFontOfSize: 150.0],
        NSStrokeWidthAttributeName : @3.0,
        NSStrokeColorAttributeName : [NSColor blueColor],
        NSKernAttributeName        : @10,
        NSObliquenessAttributeName : @0.5
    };
すっきりしてわかりやすい。


NSDictionaryへのアクセスは [@キー]が使える。
NSDictionary *splunge = @{ @"hi" : @"bork", @"greeble" : @"bork" };

NSLog (@"%@", splunge[@"greeble"]);
NSLog (@"%@", splunge[@"ACK"]);

入れ子
NSDictionary *nest = @{ @"hi" : @"ohai",
@"ichc" : @{ @"oop" : @"ack"} };
NSLog (@"nest: %@", nest);
NSLog (@"noost: %@", nest[@"ichc"][@"oop"]);
NSLog (@"nist: %@", nest[@"ichc.oop"]); // try a key path


なお staticな宣言では利用できない。例えば下記はNG
#import

NSString *thing = @"thing 1";
NSArray *array = @[ @"thing 2" ];

@implementation BNRGroovyObject
...

- - - -
大幅なリテラル表記の新規追加はいまさら感もあるがコーディングが楽になるし可読性が増すので歓迎したい。しかし ARC導入といい、数年前の Objective-C コードとの断絶が出てきた。いっそのこと ARCと新リテラル表記で Objective-C 3.0 にした方が良かったと思うくらい。


clang 3.1 から導入された instancetype とは?

このエントリーをはてなブックマークに追加



以下の様に書くことができるとのこと。
@protocol ConvenienceConstructor

+ (instancetype)defaultObject;

@end

@interface Clazz : NSObject <ConvenienceConstructor>

@end
この場合、+[Clazz defaultObject] を介して取得したインスタンスのクラスは Clazz だとclangが推論する(他のクラス型で受け取ろうとすると警告が出る)。


Clang Language Extensions に解説が出ててた。

A method with a related result type can be declared by using the type instancetype as its result type. instancetype is a contextual keyword that is only permitted in the result type of an Objective-C method, e.g.

@interface A
+ (instancetype)constructAnA;
@end

Clang の便利なアノテーション

このエントリーをはてなブックマークに追加

CF_RETURNS_RETAINED の紹介ほか。ちょっとした場面で便利。


以下、記事から抜粋。

次のようなコードを Clang Static Analyzer にかけると cgColor が開放されていないと指摘される。
-(CGColorRef)cw_cgColor { 
 NSColor *nscolor = [self colorUsingColorSpaceName:
    NSCalibratedRGBColorSpace]; 
 CGFloat components[4]; 
 [nscolor getRed:&components[0] green:&components[1]
   blue:&components[2] alpha:&components[3]]; 
 CGColorSpaceRef space = CGColorSpaceCreateWithName(
   kCGColorSpaceGenericRGB); 
 CGColorRef cgColor = CGColorCreate(space, components); 
 CGColorSpaceRelease(space); 
 return cgColor;
}

メソッドを呼び出す側での開放を想定している場合はこの指摘を無視したい。そんな時に CF_RETURNS_RETAINED が使える。
@interface NSColor (CWNSColorAdditions)
-(CGColorRef)cw_cgColor CF_RETURNS_RETAINED;
@end


Clang のアノテーションは他にもあって、まとめページが記事で紹介されていた。



引数の NULLチェックを静的に行う為の nonnull なんてものもある。

その他、最初に紹介したような戻り値の開放に関するもの(returns)、引数として渡す時の開放に関するもの(consumed)などがある。
Mac OS X API Annotations
  Cocoa & Core Foundation Memory Management Annotations
    Attribute 'ns_returns_retained'
    Attribute 'ns_returns_not_retained'
    Attribute 'cf_returns_retained'
    Attribute 'cf_returns_not_retained'
    Attribute 'ns_consumed'
    Attribute 'cf_consumed'
    Attribute 'ns_consumes_self'
Custom Assertion Handlers
  Attribute 'noreturn'
  Attribute 'analyzer_noreturn'