Your SlideShare is downloading. ×
0
@taketo1024
話すこと
1. (中級) UIView を使いやすく
2. (上級) NSNull を黙らせる
1. UIView を使いやすく
初心者あるある

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

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

え?
正しくは、
// myView を右に 10pt 移動
[UIView animateWithDuration:0.25 animations:^{
CGRect frame = myView.frame;
frame.origin.x += ...
または、
// myView を右に 10pt 移動
[UIView animateWithDuration:0.25 animations:^{
myView.frame =
CGRectMake(myView.frame.origin.x ...
あるいは、
// myView を右に 10pt 移動
[UIView animateWithDuration:0.25 animations:^{
myView.frame =
CGRectOffset(myView.frame, 10, 0...
うーん…
そもそもなぜできない?

// myView を右に 10pt 移動
[UIView animateWithDuration:0.25 animations:^{
myView.frame.origin.x += 10;
}];
<UIKit/UIView.h>
@interface UIView(UIViewGeometry)
@property(nonatomic) CGRect frame;
@property(nonatomic) CGRect bounds;
...
<UIKit/UIView.h>
@interface UIView(UIViewGeometry)
@property(nonatomic) CGRect frame;
@property(nonatomic) CGRect bounds;
...
<UIKit/UIView.h>
@interface UIView(UIViewGeometry)
@property(nonatomic) CGRect frame;
@property(nonatomic) CGRect bounds;
...
とにかく、
こういう風に書きたい

// myView を右に 10pt 移動
[UIView animateWithDuration:0.25 animations:^{
myView.x += 10;
}];
できます!
予備知識
1) プロパティはアクセサメソッドの略記法
2) カテゴリで勝手にクラスを拡張できる
1) プロパティはアクセサメソッドの
略記法

// このコードは…
CGRect frame = myView.frame;
// こう実行される
CGRect frame = [myView frame];
1) プロパティはアクセサメソッドの
略記法

// このコードは…
myView.frame = CGRectMake(0, 0, 100, 200);
// こう実行される
[myView setFrame:CGRectMake(0, 0,...
2) カテゴリで勝手にクラスを拡張できる

// UIView クラスを勝手に拡張
@interface UIView(TSExtension)
- (CGFloat)x;
- (void)setX:(CGFloat)x;
@end

UIVi...
2) カテゴリで勝手にクラスを拡張できる
// frame を使って getter / setter を定義
@implementation UIView(TSExtension)
- (CGFloat)x
{
return self.fram...
2) カテゴリで勝手にクラスを拡張できる

#import "UIView+TSExtention.h"
...
[UIView animateWithDuration:0.25 animations:^{
[myView setX:([myV...
2つ合わせて、
こんなカテゴリを作れば、
@interface UIView(TSExtention)
@property (nonatomic) CGFloat x;
@end

@implementation UIView (TSExtention)
- ...
こう書ける!

#import "UIView+TSExtention.h"
...
[UIView animateWithDuration:0.25 animations:^{
myView.x += 10;
}];
こう書ける!

#import "UIView+TSExtention.h"
...
[UIView animateWithDuration:0.25 animations:^{
myView.x += 10;
}];
// こう実行される
[...
こういう感じに作っとくと便利
@interface UIView(TSExtention)
@property
@property
@property
@property
@property
@property
@property
@prope...
Oh, 直観的!
#import "UIView+TSExtention.h"
...
// myView1, myView2 が横に並んで 10pt 平行移動
[UIView animateWithDuration:0.25 animatio...
どうぞご利用下さい

https://github.com/taketo1024/
UIView-TSExtension
2. NSNull を黙らせる
ありきたりなサーバ通信
[NSURLConnection
sendAsynchronousRequest:request
queue:queue
completionHandler:^(NSURLResponse *response,
NSDa...
ありきたりなサーバ通信
[NSURLConnection
sendAsynchronousRequest:request
queue:queue
completionHandler:^(NSURLResponse *response,
NSDa...
oh...
oh...
原因
•レスポンスのJSON:
{
}

"text": null,
...

•[NSJSONSerialization
@{
};

JSONObject...] の結果:

@"text": [NSNull null],
...

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

はメッセージ投げても

ヌルポとか出さないのに、
NSNull は自己主張が強い。
黙らせる!
NSNull サイレンサーを実装
#import <objc/runtime.h>
@implementation NSNull (TSSilencer)
- (void *)silentGetter
{
NSLog(@"called: [NS...
NSNull サイレンサーを実装
#import <objc/runtime.h>
@implementation NSNull (TSSilencer)
- (void *)silentGetter
{
NSLog(@"called: [NS...
NSNull サイレンサーを実装
#import <objc/runtime.h>
@implementation NSNull (TSSilencer)
- (void *)silentGetter
{
NSLog(@"called: [NS...
NSNull サイレンサーを実装
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *selName = NSStringFromSelector(sel);
if([selName hasPr...
NSNull サイレンサーを実装
未定義のメッセージ受信時に必ず呼ばれる
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *selName = NSStringFromSelector(sel...
NSNull サイレンサーを実装
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *selName = NSStringFromSelector(sel);
if([selName hasPr...
NSNull サイレンサーを実装
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *selName = NSStringFromSelector(sel);
if([selName hasPr...
試しにやってみる

// 露骨にヤバい奴
_label.text = (id)[NSNull null];
おぉ、クラッシュしない!

2014-02-25 13:39:02.348 called: [NSNull length]
2014-02-25 13:39:02.349 called: [NSNull length]
2014-02-25 1...
こんなのも行ける
NSString *str = (id)[NSNull null];
NSLog(@"string: %@",
[str stringByAppendingString:@"hoge"]);
NSArray *arr = (i...
余裕!
called: [NSNull stringByAppendingString:]
string: (null)
called: [NSNull objectAtIndexedSubscript:]
array: (null)
call...
こんなことしていいんだろうか…
いいんです!!!
大事なのは保守性と安全性。
どこまでやるかはあなた次第。
ご利用は計画的に

https://github.com/taketo1024/
NSNull-TSSilencer
こちらもよろしく

http://www.slideshare.net/taketo1024/ss-30036615
Thank you!
@taketo1024
Upcoming SlideShare
Loading in...5
×

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

30,551

Published on

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

Published in: Technology
0 Comments
53 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
30,551
On Slideshare
0
From Embeds
0
Number of Embeds
29
Actions
Shares
0
Downloads
62
Comments
0
Likes
53
Embeds 0
No embeds

No notes for slide

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

  1. 1. @taketo1024
  2. 2. 話すこと 1. (中級) UIView を使いやすく 2. (上級) NSNull を黙らせる
  3. 3. 1. UIView を使いやすく
  4. 4. 初心者あるある // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }];
  5. 5. 初心者あるある // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }]; え?
  6. 6. 正しくは、 // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ CGRect frame = myView.frame; frame.origin.x += 10; myView.frame = frame; }];
  7. 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. 8. あるいは、 // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame = CGRectOffset(myView.frame, 10, 0); }];
  9. 9. うーん…
  10. 10. そもそもなぜできない? // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.frame.origin.x += 10; }];
  11. 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. 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. 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. 14. とにかく、
  15. 15. こういう風に書きたい // myView を右に 10pt 移動 [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }];
  16. 16. できます!
  17. 17. 予備知識 1) プロパティはアクセサメソッドの略記法 2) カテゴリで勝手にクラスを拡張できる
  18. 18. 1) プロパティはアクセサメソッドの 略記法 // このコードは… CGRect frame = myView.frame; // こう実行される CGRect frame = [myView frame];
  19. 19. 1) プロパティはアクセサメソッドの 略記法 // このコードは… myView.frame = CGRectMake(0, 0, 100, 200); // こう実行される [myView setFrame:CGRectMake(0, 0, 100, 200)];
  20. 20. 2) カテゴリで勝手にクラスを拡張できる // UIView クラスを勝手に拡張 @interface UIView(TSExtension) - (CGFloat)x; - (void)setX:(CGFloat)x; @end UIView+TSExtension.h
  21. 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. 22. 2) カテゴリで勝手にクラスを拡張できる #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ [myView setX:([myView x] + 10)]; }]; ↑こう書ける
  23. 23. 2つ合わせて、
  24. 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. 25. こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }];
  26. 26. こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }]; // こう実行される [myView setX:([myView x] + 10)];
  27. 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. 28. Oh, 直観的! #import "UIView+TSExtention.h" ... // myView1, myView2 が横に並んで 10pt 平行移動 [UIView animateWithDuration:0.25 animations:^{ myView1.left += 10; myView2.left = myView1.right + 5; }];
  29. 29. どうぞご利用下さい https://github.com/taketo1024/ UIView-TSExtension
  30. 30. 2. NSNull を黙らせる
  31. 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. 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. 33. oh...
  34. 34. oh...
  35. 35. 原因 •レスポンスのJSON: { } "text": null, ... •[NSJSONSerialization @{ }; JSONObject...] の結果: @"text": [NSNull null], ... こうなってた
  36. 36. レスポンスを片っ端から NSNull チェックをする のが正しいんだけども…
  37. 37. nil はメッセージ投げても ヌルポとか出さないのに、 NSNull は自己主張が強い。
  38. 38. 黙らせる!
  39. 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. 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. 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. 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. 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. 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. 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. 46. 試しにやってみる // 露骨にヤバい奴 _label.text = (id)[NSNull null];
  47. 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. 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. 49. 余裕! called: [NSNull stringByAppendingString:] string: (null) called: [NSNull objectAtIndexedSubscript:] array: (null) called: [NSNull objectForKeyedSubscript:] dic: (null)
  50. 50. こんなことしていいんだろうか…
  51. 51. いいんです!!!
  52. 52. 大事なのは保守性と安全性。 どこまでやるかはあなた次第。
  53. 53. ご利用は計画的に https://github.com/taketo1024/ NSNull-TSSilencer
  54. 54. こちらもよろしく http://www.slideshare.net/taketo1024/ss-30036615
  55. 55. Thank you! @taketo1024
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×