• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Objective-C が好きになる Tips & Hack
 

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

on

  • 25,697 views

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

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

Statistics

Views

Total Views
25,697
Views on SlideShare
15,611
Embed Views
10,086

Actions

Likes
45
Downloads
54
Comments
0

22 Embeds 10,086

http://techblog.yahoo.co.jp 3758
http://yashigani.hatenablog.com 2910
http://keeetaka.hateblo.jp 2196
http://gihyo.jp 539
http://acaric-valuator.blog.jp 207
https://twitter.com 207
http://cptl.corp.yahoo.co.jp 93
http://pgmy.github.io 38
http://s.deeeki.com 30
http://mt01-cms.corp.yahoo.co.jp 30
http://wpdustbox.wordpress.com 18
http://crenieq.blogspot.jp 15
http://feedly.com 13
http://tweetedtimes.com 12
http://mym.corp.yahoo.co.jp 11
https://www.chatwork.com 3
http://49.212.34.189 1
http://snp12.smartnews.be 1
http://snp26.smartnews.be 1
http://getpocket.com 1
http://localhost 1
http://cache.yahoofs.jp 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • @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 += 10; myView.frame = frame; }];
    • または、 // 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); }];
    • あるいは、 // 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; @property(nonatomic) CGPoint center; ... @property(nonatomic,readonly) UIView *superview; @property(nonatomic,readonly,copy) NSArray *subviews; @property(nonatomic,readonly) UIWindow *window; @end
    • <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
    • <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 はオブジェクト → 特定のメモリ領域を指すポインタ
    • とにかく、
    • こういう風に書きたい // 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, 100, 200)];
    • 2) カテゴリで勝手にクラスを拡張できる // UIView クラスを勝手に拡張 @interface UIView(TSExtension) - (CGFloat)x; - (void)setX:(CGFloat)x; @end UIView+TSExtension.h
    • 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
    • 2) カテゴリで勝手にクラスを拡張できる #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ [myView setX:([myView x] + 10)]; }]; ↑こう書ける
    • 2つ合わせて、
    • こんなカテゴリを作れば、 @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
    • こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }];
    • こう書ける! #import "UIView+TSExtention.h" ... [UIView animateWithDuration:0.25 animations:^{ myView.x += 10; }]; // こう実行される [myView setX:([myView x] + 10)];
    • こういう感じに作っとくと便利 @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;
    • Oh, 直観的! #import "UIView+TSExtention.h" ... // myView1, myView2 が横に並んで 10pt 平行移動 [UIView animateWithDuration:0.25 animations:^{ myView1.left += 10; myView2.left = myView1.right + 5; }];
    • どうぞご利用下さい https://github.com/taketo1024/ UIView-TSExtension
    • 2. NSNull を黙らせる
    • ありきたりなサーバ通信 [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"]; }];
    • ありきたりなサーバ通信 [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 に表示
    • 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: [NSNull %@]", NSStringFromSelector(_cmd)); return nil; } - (void)silentSetter:(void *)value { NSLog(@"called: [NSNull %@]", NSStringFromSelector(_cmd)); } ...(続く)
    • 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)); } ...(続く)
    • 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 ...(続く)
    • 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;
    • 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;
    • 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; を呼ばせ、
    • 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 を呼ばせる。
    • 試しにやってみる // 露骨にヤバい奴 _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 13:39:02.349 called: [NSNull _fastCharacterContents] ↑ UILabel の中でなんかやってるのが分かる
    • こんなのも行ける 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"]);
    • 余裕! called: [NSNull stringByAppendingString:] string: (null) called: [NSNull objectAtIndexedSubscript:] array: (null) called: [NSNull objectForKeyedSubscript:] dic: (null)
    • こんなことしていいんだろうか…
    • いいんです!!!
    • 大事なのは保守性と安全性。 どこまでやるかはあなた次第。
    • ご利用は計画的に https://github.com/taketo1024/ NSNull-TSSilencer
    • こちらもよろしく http://www.slideshare.net/taketo1024/ss-30036615
    • Thank you! @taketo1024