void walkAndQuack(Duck* duck) {
[duck walk];
[duck quack];
}
int main() {
walkAndQuack([[Duck alloc] init]);
walkAndQuack([[Dog alloc] init]); // コンパイルエラー
}
void walkAndQuack(id duck) {
[duck walk];
[duck quack]; // Dogクラスのインスタンスを渡したときに実行時エラー
}
int main() {
walkAndQuack([[Duck alloc] init]);
walkAndQuack([[Dog alloc] init]);
}
int main() {
NSArray* animals = @[[[Duck alloc] init], [[Dog alloc] init]];
for (id animal in animals) {
[animal walk];
}
}
コンパイル前に行われる処理
モダンな言語にはない機能
int main() {
#if DEBUG
// デバッグビルド時のみコンパイルされる
#else
// リリースビルド時のみコンパイルされる
#endif
#if TARGET_OS_SIMULATOR
// シミュレーターでのビルド時のみコンパイルされる
#endif
}
条件に当てはまらないコードはコンパイルもされないためビルドエラーにもならない
リリースビルドに必要ないコードを含めないようにできるためバイナリを小さくできる
#define MAX_QUANTITY 99
#define MAX(A,B) (A > B ? A : B)
int main() {
int a = 10, b = 100;
if (MAX(a, b) > MAX_QUANTITY) {
puts("a or b is greater than the maximum.");
}
}
int main() {
int a = 10, b = 100;
if ((a > b ? a : b) > 99) {
puts("a or b is greater than the maximum.");
}
}
文字列の置き換えのため予期せぬ置き換えが発生することがある
そのためマクロ定数は基本的に大文字を使用する
#define max 3
int main() {
int max = 2; // `int 3 = 2;`と変換されてコンパイルエラー(`Expected identifier or '('`)
}
マクロ関数も文字列に置き換えで実施されるため計算順序が変わってしまう場合がある
そのため引数と返り値は全て括弧括る
#define DOUBLE(A) A * 2
#define PLUS_ONE(B) B + 1
int main () {
printf("%d", DOUBLE(1 + 1));
// `(1 + 1) * 2 = 4`が期待値だが、`printf("%d", 1 + 1 * 2);`と変換されて出力結果は3
printf("%d", PLUS_ONE(1) * 2);
// `(1 + 1) * 2 = 4`が期待値だが、`printf("%d", 1 + 1 * 2);`と変換されて出力結果は3
}
#define TWICE(A) ((A) + (A))
int main() {
int a = 1;
printf("%d", TWICE(++a));
// `2 + 2 = 4`が期待値だが、`printf("%d", ((++a) + (++a)));`変換されて出力結果は5
}
マクロ関数は関数呼び出しのオーバーヘッドを避けるために使用されてきた
今はインライン関数が使用できるのでそちらの方が良い
NS_INLINE int DOUBLE(int A) {
return A * 2;
}
プログラム自体を生成するプログラムのこと
マクロやリフレクションもメタプログラミングの一種
#import <objc/runtime.h>
@implementation Object
- (void) main {
__block BOOL called = NO;
SEL sel = NSSelectorFromString(@"aMethod");
IMP imp = imp_implementationWithBlock(^(Object* self) {
called = YES;
});
class_addMethod([Object class], sel, imp, "v@");
[self performSelector:sel];
NSAssert(called, @"Block is not called.");
}
@end
クラスを継承せずに動的にプロパティを追加できる
メタプログラミングの一種
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UILabel (Tooltip)
// @property (nonatomic, copy) NSString* tooltip;
- (NSString*) tooltip; // `label.tooltip`で値を取得できる
- (void) setTooltip:(NSString*)tooltip; // `label.tooltip = @"";`で値を設定できる
@end
static void * tooltipKey = "tooltipKey";
@implementation UILabel (Tooltip)
- (NSString*) tooltip {
return objc_getAssociatedObject(self, tooltipKey);
}
- (void) setTooltip:(NSString*)tooltip {
objc_setAssociatedObject(self, tooltipKey, tooltip, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
Swizzling (スウィズリング) = (カクテルなどの飲み物をマドラーで)かき混ぜる
フレームワークやライブラリの処理に割り込ませることができる
メタプログラミングの一種
#import <UIKit/UIKit.h>
@interface ViewController: UIViewController; @end
@implementation ViewController
- (void) updateView {
NSLog(@"updateView");
}
- (void) updateViewSwizzled {
[self updateViewSwizzled];
NSLog(@"updateViewSwizzled");
}
@end
#import <objc/runtime.h>
@implementation ViewController
- (void) viewDidLoad {
[self updateView]; // updateView
[super viewDidLoad];
Method from = class_getInstanceMethod([self class], @selector(updateView));
Method to = class_getInstanceMethod([self class], @selector(updateViewSwizzled));
method_exchangeImplementations(from, to);
[self updateView]; // updateView -> updateViewSwizzled
}
@end
@implementation ViewController
- (void) updateView {
NSLog(@"updateView");
}
- (void) updateViewSwizzled {
[self updateViewSwizzled];
NSLog(@"updateViewSwizzled");
}
@end
@implementation ViewController
- (void) updateView {
[self updateViewSwizzled];
NSLog(@"updateViewSwizzled");
}
- (void) updateViewSwizzled {
NSLog(@"updateView");
}
@end
Duck Typing
Preprocessor
ソースコードの部分的選択#if
, #else
, #endif
は使用可能
マクロの定義#define
は使用不可
Objective-Cで定義されたマクロは使用可能
Metaprogramming
#import <objc/runtime.h>
の代わりにimport ObjectiveC
で利用可能import ObjectiveC
private var tooltipKey = 0
extension UILabel {
var tooltip: String {
get {
return objc_getAssociatedObject(self, &tooltipKey) as String
}
set {
objc_setAssociatedObject(self, &tooltipKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_COPY_NONATOMIC))
}
}
}