2011年6月4日土曜日

NSURLConnectionによる非同期通信での注意

2時間くらいハマったのでメモ。
NSURLConnectionで非同期通信をする際、

[NSURLConnection connectionWithRequest:delegate:]

などを呼ぶスレッドがメインスレッドはない場合、
デリゲートへの一切のメッセージ送信が行われないようです。

別スレッドからロードを開始する場合は、

[NSURLConnection sendSynchronousRequest:returningResponse:error:]

あたりを使って意図的に同期通信にしないとダメですね。

2011年6月1日水曜日

getterのブラケット記法をドットに置き換える方法

注意:
ドット記法が使えないgetterの多くは、何らかの意図があってドット記法を許さないようにしていると推測できます。
乱用は控えましょう。


Objective-Cでは、ヘッダで @property を用いてアクセサを定義した場合のみ
メソッドをドット記法で呼び出すことができます。
(該当メソッドが存在すれば呼ぶことはできますが、警告が出るはずです。)

また、Objective-Cでは、カテゴリを用いて既存のオブジェクトを拡張することができます。

この二つの機能を用いて、
ドット記法で呼び出せないはずのインスタンスメソッド
をドット記法で呼び出せるようにできます。

やり方:

//
//  NSString+dotOperator.h
//  M2
//

@interface NSString (dotOperator)

@property (readonly) NSUInteger length;

@end


//
//  NSString+dotOperator.m
//  M2
//

#import "NSString+dotOperator.h"

@implementation NSString (dotOperator)

@dynamic length;

@end



上記の例では、

- [NSString length]

をドット記法で呼び出せるようにしました。
この拡張を使いたいファイル(もしくは***.pch)で

#import "NSString+dotOperator.h"

とすることで、下記のような書き方が可能になります。

NSLog(@"%d", @"abc".length);


括弧の入れ子が少なくなって便利ですが、使いどころを考えないとハマる可能性はありますよね!

NSStringをLL風に置換・加工できるようにするカテゴリ拡張

参考: getterのブラケット記法をドットに置き換える方法

@"abc".replace(@"b", @"d"); // adc
@"abc".append(@"def");      // abcdef
@"abc".length;              // 3

のような形で、文字列を扱えるようになるカテゴリです。
ブロックを得るgetterを設定していますが、関数の中身まではXcodeで補完できないので、
引数の数などにご注意下さい。
(引数チェックは省いています。)

//
//  NSString+LLLikeOperation.h
//  M2
//

typedef NSString *(^NSStringReplacer)(NSString *existingString, NSString *newString);
typedef NSString *(^NSStringAppender)(NSString *aString);

@interface NSString (LLLikeOperation)

@property (readonly) NSUInteger length;
@property (readonly) NSStringReplacer replace;
@property (readonly) NSStringAppender append;

@end



//
//  NSString+LLLikeOperation.m
//  M2
//

#import "NSString+LLLikeOperation.h"

@implementation NSString (LLLikeOperation)

@dynamic length;

- (NSStringReplacer)replace
{
  NSStringReplacer replacer = ^NSString*(NSString *existingString, NSString *newString) {
    return [self stringByReplacingOccurrencesOfString:existingString withString:newString];
  };
  
  return replacer;
}

- (NSStringAppender)append
{
  NSStringAppender appender = ^NSString*(NSString *aString) {
    return [self stringByAppendingString:aString];
  };
  
  return appender;
}

@end

2011年5月31日火曜日

UIScrollViewの加速度スクロールを任意のタイミングで止める方法

力技。
他に良い方法があれば教えて下さい……!

// スーパービューを取得
UIView* scrollSuperView = scrollView.superview;

// 外したときに解放されないように、参照カウントを上げてからautoreleaseをかけておく
[[scrollView retain] autorelease];

// 親ビューから外す
[scrollView removeFromSuperview];

// 親ビューに追加しなおす
[scrollSuperView addSubview:scrollView];

2011年5月28日土曜日

Objective-Cでクラス定数っぽいもの

// 定義

@property (nonatomic, assign, readonly) CGRect defaultFrame;


// 実装

@dynamic defaultFrame;

- (CGRect) defaultFrame
{
  return CGRectMake(0, 0, 320, 480); 
}

プロパティで宣言するのは、ピリオド演算子を使ってインスタンスから直接アクセスするためです。

例)  self.frame = self.defaultFrame;


参考: getterのブラケット記法をドットに置き換える方法

2011年5月23日月曜日

リファクタリングしても怖くないNibNameの指定方法

RootViewController* viewController;
NSString* nibName;

// 怖い書き方
nibName = @"RootViewController";
viewController = [[RootViewController alloc] initWithNibName:nibName bundle:nil];

// 怖くない書き方
nibName = [[RootViewController class] description];
/* または
  nibName = NSStringFromClass([
RootViewController class]);

 */
viewController = [[RootViewController alloc] initWithNibName:nibName bundle:nil];

NSString* nibName1 = @"RootViewController";
NSString* nibName2 = [[RootViewController class] description];

if ([nibName1 isEqualToString:nibName2]) {
  NSLog(@"いっしょです");
}

2011年5月20日金曜日

Objective-Cで"Sleep sort"

常識を覆すソートアルゴリズム!その名も"sleep sort"!
http://d.hatena.ne.jp/gfx/20110519/1305810786

Genius sorting algorithm: Sleep sort
http://dis.4chan.org/read/prog/1295544154

Objective-Cで。

- (void)somewhere {  
  NSArray* numbers = [NSArray arrayWithObjects:
                      [NSNumber numberWithFloat:1.1],
                      [NSNumber numberWithFloat:4.7],
                      [NSNumber numberWithFloat:0.9],
                      [NSNumber numberWithFloat:1.2],
                      [NSNumber numberWithFloat:7.7],
                      [NSNumber numberWithFloat:3.5],
                      [NSNumber numberWithFloat:6.9],
                      [NSNumber numberWithFloat:6.6],
                      [NSNumber numberWithFloat:9.5],
                      [NSNumber numberWithFloat:8.9],
                      [NSNumber numberWithFloat:2.5],
                      [NSNumber numberWithFloat:6.0],
                      [NSNumber numberWithFloat:9.4],
                      [NSNumber numberWithFloat:9.5],
                      [NSNumber numberWithFloat:3.5],
                      [NSNumber numberWithFloat:4.1],
                      [NSNumber numberWithFloat:1.3],
                      [NSNumber numberWithFloat:0.7],
                      [NSNumber numberWithFloat:1.4],
                      [NSNumber numberWithFloat:3.3], nil];
  
  NSLog(@"---- ↓ソート前 ----");
  
  for (NSNumber* number in numbers) {
    NSLog(@"%.1f", [number floatValue]);
    [self performSelector:@selector(sort:)
               withObject:number
               afterDelay:[number floatValue]];
  }
  
  NSLog(@"---- ↓ソート後 ----");
}

- (void)sort:(NSNumber *)number
{
  NSLog(@"%.1f", [number floatValue]);
}

ちゃんとソートされる……