iPhoneでNSThreadのスレッドを使う際にうっかりミスった点をメモしておきます。
割と常識のようですが、特にUIImageViewのほうはエラーも出ないので初めてだと焦りますね;;
autoreleaseに注意
何も考えずにスレッド用の関数内でautoreleaseが呼ばれると、
*** NSAutoReleaseNoPool(): Object 0x********* of class NSConreteData autoreleased with no pool in place ….
といった感じのエラーが出てしまいます。
お決まりパターンとして、スレッドの最初で
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
しておいて、スレッド終了時に [pool drain] すればとりあえず問題なさそうです。
別スレッドからUIImageViewのimageを更新しても、表示されない
別スレッドで
imageView.image = [UIImage imageNamed:@"Hoge.png"];
のような処理を実行しても、表示されません。
UIImageViewのimageを設定して表示する処理は、メインスレッドでないとダメみたいです。
メインスレッドで実行するには、実行する処理を関数にまとめた上、
[delegate performSelectorOnMainThread:@selector(theProcess:) withObject:nil waitUntilDone:YES];
のようにperformSelectorOnMainThreadを使えばOKです。
先日のiPhone性能検証で作ったソースの一部として、超手抜きUDPサーバのコードを載せておきます。
CFSocketが思ったよりも使いにくいので、BSDソケットほとんど生です・・・
/**
* 手抜きUDPサーバ
*/
#import <netinet/in.h>
#import <Foundation/Foundation.h>
@interface MyUdpConnection : NSObject {
id delegate;
}
- (id)initWithDelegate:(id)_delegate; //receiveData:(NSData*)data を実装すること
- (void)bind;
- (void)bindThread;
@end
/**
* 手抜きUDPサーバ実装部
*/
@implementation MyUdpConnection
// 初期化
- (id)initWithDelegate:(id)_delegate {
delegate = _delegate;
return self;
}
// 受信開始
- (void)bind {
NSThread *th = [[NSThread alloc]initWithTarget:self selector:@selector(bindThread) object:nil];
[th start];
}
// 受信用スレッド
- (void)bindThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(5555); //適当なポートで待機
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
char buf[100000]; //100KBまで対応
while (1) {
//ここでデータを受信するまでブロックされる
int size = recv(sock, buf, sizeof(buf), 0);
//NSDataに変換し、delegateに通知
NSData *data = [NSData dataWithBytes:buf length:size];
[delegate performSelectorOnMainThread:@selector(receiveData:) withObject:data waitUntilDone:YES];
}
[pool drain];
}
@end
これを使うと、たとえばViewControllerなどで
- (void)receiveData:(NSData *)data;
を実装しておいて、
MyUdpConnection *udp = [[MyUdpConnection alloc]initWithDelegate:self];
[udp bind];
のようにすれば、データを受信したときにreceiveData:が呼ばれます。
エラー処理や再送処理はおろか、ポート番号すらべた書きのものですが、重要でないデータを受け取る分には、ちょっと改良すれば使い物になりそうです。
iPhoneやiPod Touchは、ネットワークの性能がそれほど高くないため、NSURLConnectionでたくさんコネクションを張ったりすると目に見えて遅延します。
以下のような実験をやってみました。
iPhone SDKで、以下の2種類のテストアプリを作りました。
なお、機材の都合上iPhoneではなくiPod Touchを使っています。
【A】
タイマーで0.05秒ごとにHTTPリクエストを送信し、レスポンスとして受け取った画像を表示する。
サーバでは、リクエストを受け取るたびに0~9の数字画像を順番に返す。
【B】
UDPで接続を待ち、画像データを受け取ったら表示する。
サーバでは、iPhoneに向かって0.05秒ごとに0~9の数字画像を順番にUDPで送信する。
サーバは、十分に高性能なマシンを、有線LANで接続しました。
MacBook ProやiPod Touch実機は、同じ無線LAN (IEEE802.11g) を使っています。
【結果】
Aのアプリ(MacBook Pro上のシミュレータ)
Aのアプリ(iPod Touch 実機)
Bのアプリ(iPod Touch実機)
このように、HTTPを使う場合、シミュレータではスムーズに動くものの、実機ではかなりカクカクです。
実機でも、HTTPを使わずにUDPにしてしまえば、十分スムーズになることが分かります。
今回使った画像は各2KB程度のPNG画像ですが、50KB程度のJPG画像を使っても結果はほとんど変わらなかったため、帯域よりもコネクションの数が大きなウェイトを占めているようです。
どの程度性能が出るか大まかに分かれば良かったので、原因などの細かい検証は今回は行いません。
使ったソースコードなどは後日このブログに載せておこうかと思います。
CakePHPのpaginatorは、ページングの他にソートもやってくれて大変便利です。
しかし、たまに「ソートはするけどページングしたくない」ということもありますよね?
そんなとき、$this->paginate['limit'] = null のようにやると、Zero Divide でエラーになります。paginatorの中ではそんなことは想定されていません。
ゼロからソート部分を作るのも面倒なので、paginatorのソート部分だけを無理矢理使わせてもらいましょう。
コントローラで以下のように処理します。
//手動でsort
$order = null;
if (isset($this->passedArgs['sort'])) {
$direction = null;
if (isset($this->passedArgs['direction'])) {
$direction = strtolower($this->passedArgs['direction']);
}
if ($direction != 'asc' && $direction != 'desc') {
$direction = 'asc';
}
$order = array($this->passedArgs['sort'] => $direction);
}
//paginate処理はするが、そのデータは使わない
$this->paginate();
//全件を表示
$result = $this->Model->find('all', array('order' => $order)));
この方法なら、ビューでは、
$paginator->sort(’なまえ’, ‘User.name’);
のように、普通にpaginatorを使うときの同じようにかけます。
強引であまり褒められた方法ではありませんが、これで目的は達成できました。
Windows 7 アップグレード版をインストールする際、すでにHDDにWindowsが入っている場合、アップグレードインストールでも新規インストールでも問題なく行えます。
しかし、HDDを交換した場合など、Windowsが入ったHDDが接続されていない場合、プロダクトキー入力のところで弾かれます。

「プロダクトキーが有効ではありません」と表示される
Windows 7では、通常版とアップグレード版でプロダクトキーが分かれており、アップグレード版の場合は、インストール時に既に何らかのWindowsがHDD内に存在することが条件になっています。
ライセンス上、古いWindowsの正規ライセンスを保持していれば問題ないはずなので、若干不親切な仕様ですね。
逆に、入っているWindowsがライセンスを持っているかどうかは判定されないので、いったんプロダクトキーを空で入力して、その後アップグレードインストールし直せば、プロダクトキーは受理されます。もちろん、正規ライセンスを持っている状態でやってください。
・・・とここまで書いて気がついたんですが、Vistaの時も全く同じことやっていましたね。
せめて、エラーメッセージを「このプロダクトキーでは新規インストールできません」としてくれると、混乱を減らせると思うんですが。
順番が逆になってしまいましたが、Windows 7をインストールするためにCPUクーラーを取り替えました。
気分の問題は大切です。
今までは見た目で選んだSpin-Qを使っていました。
こいつは強そうで格好いいのですが、値段が高かったのに、うるさい割に冷却性能はリテールと大差ない気がして、正直見た目以外はいまいちでした。ネットでも地雷評価されていますね。

今までの状態 ケースはお気に入りのカタツムリです
ということで、今度は無難に「安い・静か・冷える」と評判のAndyの後継KABUTOにしておきました。
マザーを外すのは面倒なので、いつもバックプレート不要のものばかり選んでしまいます。

銅プレートは使われていない

グリスは保存できるチューブ型が良かったな・・・

取り付けてみた
ケースに組み込んだままでも、多少狭いものの無事に取り付けできました。
マザーはRampage II Extremeなのですが、これでQ-Fanを有効にしたところ、CPU FAN ERROR が発生して焦りました。
低回転のFANなので、電圧が低めだと回転数が下がりすぎてしまうのが原因みたいです。
Q-Fan無効でもそれほどうるさくないのですが、温度が上がれば勝手に回転数あげてくれるはずなので、CPU FAN Speed MonitoringをIgnoreにして無視することにしました。
今のところ、CPUは40度程度で安定して冷えているようです。
まあ、元々オーバークロックも高負荷処理もあまりやらないので、リテールでも全然問題ないのですが・・・
無償のVMWare Playerでも仮想マシンの作成ができるようになったので、インストールしてみました。
しかし、ゲストにWindows 7をインストールしたところ、ハードディスクのアクセスが激しすぎて、全く使い物になりませんでした。スタートメニューを開くのに10秒以上かかり、ホストまでまともに動かないほどです。
どうやらVMWareの仮想ハードディスク周りの機能が悪さをしているらしいので、それらを無効にすると早くなるとのことです。
このページがよくまとまっていました。
http://revilog.com/2008/05/011299.html
しかし、VMWare PlayerではAdvanced設定ができないのですね。
一応vmxファイルを直接編集すれば可能ですが、ほかにもNICの詳細設定ができないなど不便が多いので、結局アンインストールしてVMWare Workstationに乗り換えました。
Workstationで上記サイトの設定を施したら、異常なディスクアクセスは収まりました。
機能制限版でもVMの直接起動ができない以外はほとんど変わらないので、しばらくこれを使ってみようと思います。
自宅マシンもようやくWindows 7にしました。
インストールの際にはHDDとCPUクーラーも交換する!と決めていたので、なかなか億劫になって遅くなってしまいました。
ちなみに、当然ながら64bit版です。
Windows 7になって、ようやく64bitが普及してきましたね。うれしい限りです。64bit大好きです。
いろいろと必要ソフトをインストールしているのですが、Creative Suite CS3のインストールに失敗して時間がかかりました。
インストール完了時に「共通コンポーネント」で失敗したとのエラーが出て、Photoshopなどを起動すると「このコンピュータのライセンシングサービスが起動していません」と出て終了してしまいます。
Adobeサポートでも紹介されている有名な問題らしいのですが、これらを実行しても解決しませんでした。
結局原因はKasperskyで、これを無効にしてアンインストール→インストールをやり直したら無事起動できました。
Kasperskyは今までも使っていて問題なかったのですが、Windows 7対応版は初めて使いました。
2010になって保護が強力になりすぎたのでしょうか?ブロックするなら通知くらい出してほしかったです。
何はともあれ無事インストールが終わったので、次はVisual Studio 2010 betaなどを入れていこうと思います。
iPhoneの画面をInterface Builderで作るとき、UIButtonなどではInspector部分に「Font」の項目が出るのに、TextViewだと出ません。
フォントサイズを変更するにはどうするのかと思ったら、Commandと+/- を同時に押したら上手く行きました。
また、フォントスタイルを変更するには、画面上部にあるメニューから「Font」→「Show Fonts」を選べば、フォント編集ダイアログが出てきました。
インスペクタとメニューの役割分担が曖昧ですね。
Interface BuilderのようなUIなら、フォント編集はメニューじゃなくてインスペクタに表示すべきで、わかりにくいです。
iPhoneで画面を横にしたいときは、ViewControllerで
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
のようにオーバーライドします。
(この関数はViewControllerを作る際にXCodeが生成するので、コメントアウトして少し書き換えるだけ)
しかし、Tab Bar Applicationを使っている場合、この方式だと、全部のViewControllerのうち、最も厳しい条件のものに合わせられます。
「タブ1:オーバーライドしない、タブ2:常にYESを返す」だと、画面は回転しません。
「タブ1:常にYES、タブ2:LandscapeRightの時だけYES」だと、画面は常に右向きになります。
このように、Tab Bar Applicationだと、画面ごとに向きを変えることが出来ないようです。
そのような場合、仕方ないので手動で回転させましょう。
- (void)viewDidLoad {
[super viewDidLoad];
self.view.transform = CGAffineTransformMakeRotation(M_PI * 0.5f); //90度回転
}
これで一応回転できます。
なお、InterfaceBuilderを横にしたい場合は、右上の矢印をクリックすればOKです。
-206x299.png)
見つけるのに30分かかりました。わかりにくい。