• Like
Objective-C が好きになる Tips & Hack
Upcoming SlideShare
Loading in...5
×

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

  • 28,898 views
Uploaded on

ヤフー vs クラスメソッド「iOS 炎の7番勝負」にて発表 …

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
28,898
On Slideshare
0
From Embeds
0
Number of Embeds
27

Actions

Shares
Downloads
57
Comments
0
Likes
47

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. @taketo1024
  • 2. 話すこと 1. (中級) UIView を使いやすく 2. (上級) NSNull を黙らせる
  • 3. 1. UIView を使いやすく
  • 4. 初心者あるある // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }];
  • 5. 初心者あるある // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }]; え?
  • 6. 正しくは、 // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ CGRect frame = myView.frame; frame.origin.x += 10; myView.frame = frame; }];
  • 7. または、 // 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); }];
  • 8. あるいは、 // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame = CGRectOffset(myView.frame, 10, 0); }];
  • 9. うーん…
  • 10. そもそもなぜできない? // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }];
  • 11. <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
  • 12. <UIKit/UIView.h> @interface UIView(UIViewGeometry) @property(nonatomic) CGRect frame; @property(nonatomic) CGRect bounds; @property(nonatomic) CGPoint center; ... CGRect, CGPoint は構造体 → アクセスのたび値が生成されて返される @property(nonatomic,readonly) UIView *superview; @property(nonatomic,readonly,copy) NSArray *subviews; @property(nonatomic,readonly) UIWindow *window; @end
  • 13. <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 はオブジェクト → 特定のメモリ領域を指すポインタ
  • 14. とにかく、
  • 15. こういう風に書きたい // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }];
  • 16. できます!
  • 17. 予備知識 1) プロパティはアクセサメソッドの略記法 2) カテゴリで勝手にクラスを拡張できる
  • 18. 1) プロパティはアクセサメソッドの 略記法 // このコードは… CGRect frame = myView.frame; // こう実行される CGRect frame = [myView frame];
  • 19. 1) プロパティはアクセサメソッドの 略記法 // このコードは… myView.frame = CGRectMake(0, 0, 100, 200); // こう実行される [myView setFrame:CGRectMake(0, 0, 100, 200)];
  • 20. 2) カテゴリで勝手にクラスを拡張できる // UIView クラスを勝手に拡張 @interface UIView(TSExtension) - (CGFloat)x; - (void)setX:(CGFloat)x; @end UIView+TSExtension.h
  • 21. 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
  • 22. 2) カテゴリで勝手にクラスを拡張できる #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ [myView setX:([myView x] + 10)]; }]; ↑こう書ける
  • 23. 2つ合わせて、
  • 24. こんなカテゴリを作れば、 @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
  • 25. こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }];
  • 26. こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }]; // こう実行される [myView setX:([myView x] + 10)];
  • 27. こういう感じに作っとくと便利 @interface UIView(TSExtention) @property @property @property @property @property @property @property @property @property @property @end (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) (nonatomic) CGFloat x; CGFloat y; CGPoint origin; CGFloat left; CGFloat right; CGFloat top; CGFloat bottom; CGSize size; CGFloat width; CGFloat height;
  • 28. Oh, 直観的! #import "UIView+TSExtention.h" ... // myView1, myView2 が横に並んで 10pt 平行移動 [UIView animateWithDuration:0.25 animations:^{ myView1.left += 10; myView2.left = myView1.right + 5; }];
  • 29. どうぞご利用下さい https://github.com/taketo1024/ UIView-TSExtension
  • 30. 2. NSNull を黙らせる
  • 31. ありきたりなサーバ通信 [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"]; }];
  • 32. ありきたりなサーバ通信 [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 に表示
  • 33. oh...
  • 34. oh...
  • 35. 原因 •レスポンスのJSON: { } "text": null, ... •[NSJSONSerialization @{ }; JSONObject...] の結果: @"text": [NSNull null], ... こうなってた
  • 36. レスポンスを片っ端から NSNull チェックをする のが正しいんだけども…
  • 37. nil はメッセージ投げても ヌルポとか出さないのに、 NSNull は自己主張が強い。
  • 38. 黙らせる!
  • 39. 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)); } ...(続く)
  • 40. NSNull サイレンサーを実装 #import <objc/runtime.h> @implementation NSNull (TSSilencer) - (void *)silentGetter { NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); return nil; } nil を返すだけの getter - (void)silentSetter:(void *)value { NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); } ...(続く)
  • 41. 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 ...(続く)
  • 42. NSNull サイレンサーを実装 + (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;
  • 43. NSNull サイレンサーを実装 未定義のメッセージ受信時に必ず呼ばれる + (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;
  • 44. NSNull サイレンサーを実装 + (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 { set*** なら silentSetter: Method getter = class_getInstanceMethod(self, @selector(silentGetter)); class_addMethod(self, sel, method_getImplementation(getter), method_getTypeEncoding(getter)); } return YES; を呼ばせ、
  • 45. NSNull サイレンサーを実装 + (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 を呼ばせる。
  • 46. 試しにやってみる // 露骨にヤバい奴 _label.text = (id)[NSNull null];
  • 47. おぉ、クラッシュしない! 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 の中でなんかやってるのが分かる
  • 48. こんなのも行ける 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"]);
  • 49. 余裕! called: [NSNull stringByAppendingString:] string: (null) called: [NSNull objectAtIndexedSubscript:] array: (null) called: [NSNull objectForKeyedSubscript:] dic: (null)
  • 50. こんなことしていいんだろうか…
  • 51. いいんです!!!
  • 52. 大事なのは保守性と安全性。 どこまでやるかはあなた次第。
  • 53. ご利用は計画的に https://github.com/taketo1024/ NSNull-TSSilencer
  • 54. こちらもよろしく http://www.slideshare.net/taketo1024/ss-30036615
  • 55. Thank you! @taketo1024