objective-c が好きになる tips & hack

55
@taketo1024

Upload: taketo-sano

Post on 10-May-2015

30.048 views

Category:

Technology


2 download

DESCRIPTION

ヤフー vs クラスメソッド「iOS 炎の7番勝負」にて発表 http://dev.classmethod.jp/news/yxcm/

TRANSCRIPT

Page 1: Objective-C が好きになる Tips & Hack

@taketo1024

Page 2: Objective-C が好きになる Tips & Hack

話すこと

1. (中級) UIView を使いやすく

2. (上級) NSNull を黙らせる

Page 3: Objective-C が好きになる Tips & Hack

1. UIView を使いやすく

Page 4: Objective-C が好きになる Tips & Hack

初心者あるある

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10;}];

Page 5: Objective-C が好きになる Tips & Hack

初心者あるある

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10;}];

え?

Page 6: Objective-C が好きになる Tips & Hack

正しくは、

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ CGRect frame = myView.frame; frame.origin.x += 10; myView.frame = frame;}];

Page 7: Objective-C が好きになる Tips & Hack

または、

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.frame = CGRectMake(myView.frame.origin.x + 10, myView.frame.origin.y, myView.frame.size.width, myView.frame.size.height);}];

Page 8: Objective-C が好きになる Tips & Hack

あるいは、

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.frame = CGRectOffset(myView.frame, 10, 0);}];

Page 9: Objective-C が好きになる Tips & Hack

うーん…

Page 10: Objective-C が好きになる Tips & Hack

そもそもなぜできない?

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10;}];

Page 11: Objective-C が好きになる Tips & Hack

<UIKit/UIView.h>@interface UIView(UIViewGeometry)

@property(nonatomic) CGRect frame;@property(nonatomic) CGRect bounds;@property(nonatomic) CGPoint center;

...

@property(nonatomic,readonly) UIView *superview;@property(nonatomic,readonly,copy) NSArray *subviews;@property(nonatomic,readonly) UIWindow *window;

@end

Page 12: Objective-C が好きになる Tips & Hack

<UIKit/UIView.h>@interface UIView(UIViewGeometry)

@property(nonatomic) CGRect frame;@property(nonatomic) CGRect bounds;@property(nonatomic) CGPoint center;

...

@property(nonatomic,readonly) UIView *superview;@property(nonatomic,readonly,copy) NSArray *subviews;@property(nonatomic,readonly) UIWindow *window;

@end

CGRect, CGPoint は構造体→ アクセスのたび値が生成されて返される

Page 13: Objective-C が好きになる Tips & Hack

<UIKit/UIView.h>@interface UIView(UIViewGeometry)

@property(nonatomic) CGRect frame;@property(nonatomic) CGRect bounds;@property(nonatomic) CGPoint center;

...

@property(nonatomic,readonly) UIView *superview;@property(nonatomic,readonly,copy) NSArray *subviews;@property(nonatomic,readonly) UIWindow *window;

@end UIView, NSArray はオブジェクト→ 特定のメモリ領域を指すポインタ

Page 14: Objective-C が好きになる Tips & Hack

とにかく、

Page 15: Objective-C が好きになる Tips & Hack

こういう風に書きたい

// myView を右に 10pt 移動[UIView animateWithDuration:0.25 animations:^{ myView.x += 10;}];

Page 16: Objective-C が好きになる Tips & Hack

できます!

Page 17: Objective-C が好きになる Tips & Hack

予備知識

1) プロパティはアクセサメソッドの略記法

2) カテゴリで勝手にクラスを拡張できる

Page 18: Objective-C が好きになる Tips & Hack

1) プロパティはアクセサメソッドの略記法

// このコードは… CGRect frame = myView.frame; // こう実行される CGRect frame = [myView frame];

Page 19: Objective-C が好きになる Tips & Hack

1) プロパティはアクセサメソッドの略記法

// このコードは… myView.frame = CGRectMake(0, 0, 100, 200); // こう実行される [myView setFrame:CGRectMake(0, 0, 100, 200)];

Page 20: Objective-C が好きになる Tips & Hack

2) カテゴリで勝手にクラスを拡張できる

// UIView クラスを勝手に拡張 @interface UIView(TSExtension)

- (CGFloat)x;- (void)setX:(CGFloat)x;

@end

UIView+TSExtension.h

Page 21: Objective-C が好きになる Tips & Hack

2) カテゴリで勝手にクラスを拡張できる

// frame を使って getter / setter を定義@implementation UIView(TSExtension)

- (CGFloat)x{ return self.frame.origin.x;}

- (void)setX:(CGFloat)x { CGRect frame = self.frame; frame.origin.x = x; self.frame = frame;}

@end

UIView+TSExtension.m

Page 22: Objective-C が好きになる Tips & Hack

2) カテゴリで勝手にクラスを拡張できる

#import "UIView+TSExtention.h"

...

[UIView animateWithDuration:0.25 animations:^{ [myView setX:([myView x] + 10)];}];

↑こう書ける

Page 23: Objective-C が好きになる Tips & Hack

2つ合わせて、

Page 24: Objective-C が好きになる Tips & Hack

こんなカテゴリを作れば、@interface UIView(TSExtention)

@property (nonatomic) CGFloat x;

@end

@implementation UIView (TSExtention)

- (CGFloat)x{ return self.frame.origin.x;}

- (void)setX:(CGFloat)x { CGRect frame = self.frame; frame.origin.x = x; self.frame = frame;}

@end

Page 25: Objective-C が好きになる Tips & Hack

こう書ける!

#import "UIView+TSExtention.h"

...

[UIView animateWithDuration:0.25 animations:^{ myView.x += 10;}];

Page 26: Objective-C が好きになる Tips & Hack

こう書ける!

#import "UIView+TSExtention.h"

...

[UIView animateWithDuration:0.25 animations:^{ myView.x += 10;}];

// こう実行される[myView setX:([myView x] + 10)];

Page 27: Objective-C が好きになる Tips & Hack

こういう感じに作っとくと便利

@interface UIView(TSExtention)

@property (nonatomic) CGFloat x;@property (nonatomic) CGFloat y;@property (nonatomic) CGPoint origin;@property (nonatomic) CGFloat left;@property (nonatomic) CGFloat right;@property (nonatomic) CGFloat top;@property (nonatomic) CGFloat bottom;@property (nonatomic) CGSize size;@property (nonatomic) CGFloat width;@property (nonatomic) CGFloat height;

@end

Page 28: Objective-C が好きになる Tips & Hack

Oh, 直観的!

#import "UIView+TSExtention.h"

...

// myView1, myView2 が横に並んで 10pt 平行移動[UIView animateWithDuration:0.25 animations:^{ myView1.left += 10; myView2.left = myView1.right + 5;}];

Page 29: Objective-C が好きになる Tips & Hack

https://github.com/taketo1024/UIView-TSExtension

どうぞご利用下さい

Page 30: Objective-C が好きになる Tips & Hack

2. NSNull を黙らせる

Page 31: Objective-C が好きになる Tips & Hack

ありきたりなサーバ通信[NSURLConnection

sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

_label.text = result[@"text"];

}];

Page 32: Objective-C が好きになる Tips & Hack

ありきたりなサーバ通信[NSURLConnection

sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

_label.text = result[@"text"];

}]; レスポンスの “text” の値を _label に表示

Page 33: Objective-C が好きになる Tips & Hack

oh...

Page 34: Objective-C が好きになる Tips & Hack

oh...

Page 35: Objective-C が好きになる Tips & Hack

原因

{ "text": null, ...}

@{ @"text": [NSNull null], ...};

•[NSJSONSerialization JSONObject...] の結果:

•レスポンスのJSON:

こうなってた

Page 36: Objective-C が好きになる Tips & Hack

レスポンスを片っ端からNSNull チェックをするのが正しいんだけども…

Page 37: Objective-C が好きになる Tips & Hack

nil はメッセージ投げてもヌルポとか出さないのに、NSNull は自己主張が強い。

Page 38: Objective-C が好きになる Tips & Hack

黙らせる!

Page 39: Objective-C が好きになる Tips & Hack

NSNull サイレンサーを実装

#import <objc/runtime.h>

@implementation NSNull (TSSilencer)

- (void *)silentGetter{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); return nil;}

- (void)silentSetter:(void *)value{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd));}

...(続く)

Page 40: Objective-C が好きになる Tips & Hack

NSNull サイレンサーを実装

#import <objc/runtime.h>

@implementation NSNull (TSSilencer)

- (void *)silentGetter{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); return nil;}

- (void)silentSetter:(void *)value{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd));}

...(続く)

nil を返すだけの getter

Page 41: Objective-C が好きになる Tips & Hack

NSNull サイレンサーを実装

#import <objc/runtime.h>

@implementation NSNull (TSSilencer)

- (void *)silentGetter{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); return nil;}

- (void)silentSetter:(void *)value{ NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd));}

...(続く)何もしない setter

Page 42: Objective-C が好きになる Tips & Hack

+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selName = NSStringFromSelector(sel);

if([selName hasPrefix:@"set"]) { Method setter = class_getInstanceMethod(self, @selector(silentSetter:));

class_addMethod(self, sel, method_getImplementation(setter), method_getTypeEncoding(setter));

} else { Method getter = class_getInstanceMethod(self, @selector(silentGetter)); class_addMethod(self, sel, method_getImplementation(getter), method_getTypeEncoding(getter)); } return YES;}

NSNull サイレンサーを実装

Page 43: Objective-C が好きになる Tips & Hack

+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selName = NSStringFromSelector(sel);

if([selName hasPrefix:@"set"]) { Method setter = class_getInstanceMethod(self, @selector(silentSetter:));

class_addMethod(self, sel, method_getImplementation(setter), method_getTypeEncoding(setter));

} else { Method getter = class_getInstanceMethod(self, @selector(silentGetter)); class_addMethod(self, sel, method_getImplementation(getter), method_getTypeEncoding(getter)); } return YES;}

未定義のメッセージ受信時に必ず呼ばれる

NSNull サイレンサーを実装

Page 44: Objective-C が好きになる Tips & Hack

+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selName = NSStringFromSelector(sel);

if([selName hasPrefix:@"set"]) { Method setter = class_getInstanceMethod(self, @selector(silentSetter:));

class_addMethod(self, sel, method_getImplementation(setter), method_getTypeEncoding(setter));

} else { Method getter = class_getInstanceMethod(self, @selector(silentGetter)); class_addMethod(self, sel, method_getImplementation(getter), method_getTypeEncoding(getter)); } return YES;}

set*** なら silentSetter: を呼ばせ、

NSNull サイレンサーを実装

Page 45: Objective-C が好きになる Tips & Hack

+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selName = NSStringFromSelector(sel);

if([selName hasPrefix:@"set"]) { Method setter = class_getInstanceMethod(self, @selector(silentSetter:));

class_addMethod(self, sel, method_getImplementation(setter), method_getTypeEncoding(setter));

} else { Method getter = class_getInstanceMethod(self, @selector(silentGetter)); class_addMethod(self, sel, method_getImplementation(getter), method_getTypeEncoding(getter)); } return YES;} それ以外は silentGetter を呼ばせる。

NSNull サイレンサーを実装

Page 46: Objective-C が好きになる Tips & Hack

試しにやってみる

// 露骨にヤバい奴_label.text = (id)[NSNull null];

Page 47: Objective-C が好きになる Tips & Hack

おぉ、クラッシュしない!

2014-02-25 13:39:02.348 called: [NSNull length]

2014-02-25 13:39:02.349 called: [NSNull length]

2014-02-25 13:39:02.349 called: [NSNull _fastCharacterContents]

↑ UILabel の中でなんかやってるのが分かる

Page 48: Objective-C が好きになる Tips & Hack

こんなのも行ける

NSString *str = (id)[NSNull null]; NSLog(@"string: %@", [str stringByAppendingString:@"hoge"]);

NSArray *arr = (id)[NSNull null]; NSLog(@"array: %@", arr[1]);

NSDictionary *dic = (id)[NSNull null]; NSLog(@"dic: %@", dic[@"key"]);

Page 49: Objective-C が好きになる Tips & Hack

余裕!

called: [NSNull stringByAppendingString:]string: (null)

called: [NSNull objectAtIndexedSubscript:]array: (null)

called: [NSNull objectForKeyedSubscript:]dic: (null)

Page 50: Objective-C が好きになる Tips & Hack

こんなことしていいんだろうか…

Page 51: Objective-C が好きになる Tips & Hack

いいんです!!!

Page 52: Objective-C が好きになる Tips & Hack

大事なのは保守性と安全性。どこまでやるかはあなた次第。

Page 53: Objective-C が好きになる Tips & Hack

ご利用は計画的に

https://github.com/taketo1024/NSNull-TSSilencer

Page 55: Objective-C が好きになる Tips & Hack

Thank you!@taketo1024