Archive for the ‘Cocoa’ Category

XCodeで「一時的にデータフォーマッタが使用できません」というエラーが起きてしまい、
実行できなくなってしまっていた。

原因は、フレームワークも一緒に直していて、
フレームワークの改修をしてビルドしている途中に、
フレームワークを読み込んでいるアプリを実行しようとすると発生すると思われる。

一度、陥ったら、再起動してもうまく行かない。
とりあえず、フレームワークのリンクを削除して、
再度フレームワークを追加することで、直った。

あせっちゃいけませんってことですかね。。。

XCodeでSQLiteを使用する

Cocoaアプリケーションに久々に触ったので、
忘れないようにメモ。

SQLiteを使用したい場合は、既存のフレームワーク「libsqlite3.0.dylib」を使用することで、
簡単にアクセスすることができる。

書いたコードは以下

  1.  
  2. #import <sqlite3.h>
  3.  
  4. /*間省略*/
  5.  
  6. - (NSArray *)getSQLiteTable {
  7.         sqlite3* db; //DBハンドラー
  8.         sqlite3_stmt *sth; //ステートメントハンドラー
  9.         NSString * databaseFilePath = @"/Users/admin/SQLite/test.db"; //SQLiteのファイルパス
  10.         NSMutableArray * resultArray = [ NSMutableArray array ]; //結果を格納する配列
  11.         NSString * sql = [ NSString stringWithString:@"SELECT * FROM users" ]; //SQL
  12.        
  13.         //SQLファイルをオープン
  14.         //ファイルが存在しない場合は、作成してしまうので、
  15.         //エラーにしたい場合は、ファイルの有無をNSFileManager等で判断する
  16.         if( sqlite3_open([databaseFilePath UTF8String ], &db) == SQLITE_OK )
  17.         {
  18.                 //prepare
  19.                 int result = sqlite3_prepare_v2(
  20.                                                 db,
  21.                                                 [sql UTF8String ],
  22.                                                 -1,
  23.                                                 &sth,
  24.                                                 NULL );
  25.                 //SQLiteでコンパイルできたか
  26.                 if( result != SQLITE_OK){
  27.                         NSLog(@"Faild to Sqlite");
  28.                         NSLog(@"%s",sqlite3_errmsg(db) ); //エラーになった際の詳細のエラー文
  29.                         return;
  30.                 }
  31.                 int count = sqlite3_column_count(sth); //カラムの数を取得
  32.                 int i;
  33.                 NSMutableArray * keyArray = [ NSMutableArray array ]; //カラム名の配列
  34.                 for( i=0; i<count; i++){
  35.                         //カラム名を取得し、配列に格納
  36.                         [ keyArray addObject:[ NSString stringWithUTF8String:(char*)sqlite3_column_name(sth, i)] ];
  37.                 }
  38.                
  39.                 while( sqlite3_step(sth) == SQLITE_ROW ){
  40.                         NSMutableDictionary * dict = [ NSMutableDictionary dictionary ];
  41.                         //各値を取得
  42.                         for( i=0; i<count; i++){
  43.                                 NSString * value = [ NSString stringWithUTF8String:(char*)sqlite3_column_text(sth, i)];
  44.                                 [ dict setValue:value forKey:[ keyArray objectAtIndex:i ] ];
  45.                
  46.                         }
  47.                         //結果配列に格納
  48.                         [ resultArray addObject:dict ];
  49.                 }
  50.                 //ステートメントハンドラを破棄(これしないとメモリが廃棄されない)
  51.                 sqlite3_finalize(sth);
  52.         }
  53.         sqlite3_close(db); //DBを閉じる
  54.  

ポイント
・16行目 ・・・ SQLiteのファイルを開く。MySQLとかの場合はconnectと同じことかと。
         ただし、ファイルが存在しなくても作成されてしまうので、ファイルが存在しない場合にエラーにする場合は、
         NSFileManagerで存在チェックするべき。

久々にMacを触ったら「objc_class_nameクラス名」というエラーに引っかかった。
OS 10.6 XCode 3.2のお話です。

開発環境のマシンが変更になっていたので、
昔のソースを引っ張ってビルドしてみたら、こんなエラーで引っかかった。

とりあえず分からなかったので、調べてみたところ
こちらのサイトを見てみた。
iPhoneメモ “.objc_class_name_クラス名”, referenced from:

なるほど、いらなくなったソースを消すと出やすいのか。
まずは、クリーニングということで、やってみたが、
事態は一向に変わらず。。。

まずはリンクが怪しいということで、
自作のFrameworkをクリーニング、再ビルド。

Frameworkを一度削除し、再度新規に登録。
クリーニングを行い、再度ビルド。

まだダメ。

これは、もうクリーニングではないなということで、
設定を再度見直し。
#そもそもソース削除してないし。。。

ターゲットのLink Binary With Librariesの中を見ると、
なんとCocoaとCarbonがないではないか。
そりゃ動かんよ。

そこにCocoaとCarbonを移し、ビルド。
うまく行ったと思いきやまた同じエラー(クラス名が違う)

再度、設定を見直すと、
ターゲットの「ソースをコンパイル」の部分に
エラーになったクラスのソースが含まれて無いじゃん!

ということで、そこに目的のファイルを入れたら、
無事動きました。

久々だったので、いろいろ対処を忘れちゃいました。

ファイルがエイリアスかどうかを判断する必要がある場合に、
NSFileManager経由で、attributesを取ったところで、
取得できない。
シンボリックリンクかどうかはattributeのNSFileTypeがNSFileTypeSymbolicLinkかどうかで
取得することができる。
#Finderで見ると、同じアイコンなので紛らわしい。。。

そこで、CoreServicesのCarbonCoreにあるAlias.hの
FSIsAliasFileを使って判別することにする。
NSFileManagerのカテゴリとして実装

  1.  
  2. -(BOOL)isAlias:(NSString*)path{
  3.         BOOL returnValue = NO; //返戻値
  4.         FSRef fsref;
  5.         Boolean isAlias, isFolder;
  6.        
  7.         if( [ self stringPathToFSRef:path fsRef:&fsRef ] &&
  8.                 noErr == FSIsAliasFile(&fsref, &isAlias, &isFoder ) ){
  9.                 returnValue = isAlias;
  10.         }
  11.        
  12.         return returnValue;
  13. }
  14.  
  15. -(BOOL)stringPathToFSRef:(NSString*)path fsRef:(FSRef*)outRef{
  16.         BOOL returnValue = NO;
  17.        
  18.         OSStatus err = FSPathMakeRef((const UInt8*)[ path fileSystemRepresentation ] , outRef, NULL );
  19.        
  20.         if( err == noErr ){
  21.                 returnValue = YES;
  22.         }
  23.        
  24.         return returnValue;
  25. }
  26.  

これで、パスのファイルがエイリアスかどうかわかった。

CocoaでSleepのような動作を実装する場面があったので、
メモメモ。

例えば、ファイル関連で、ファイルのIOより、メソッドの実行速度が早い場合に
よく行う手ですが、実行を少し遅らせたい場合にとっても有効な手段。

  1.  
  2. //次のメソッドの実行を0.5秒遅らせる(AAAがログに表示されるのが0.5秒遅らせる)
  3. [NSThread sleepUntilDate:[[NSDate date] addTimeInterval:0.5]];
  4. NSLog(@"AAA");
  5.  

覚えておきたい書き方です。

追記–2010.08.18
上記のサンプルは、10.4までの書き方。

10.5以上なら以下の書き方

  1.  
  2. //次のメソッドの実行を0.5秒遅らせる(AAAがログに表示されるのが0.5秒遅らせる)
  3. [NSThread sleepForTimeInterval:0.5];
  4. NSLog(@"AAA");
  5.  

シンプルでいいなぁ~

SM様よりコメントいただきました。ありがとうございます。

グラフを書きたくなって、いろいろ調べてみた。

なんか、難しい話がいっぱい出てきてしまったので、
簡単にできる方法はないかなぁと考えました。

できたテストアプリはこんなの

で、解決のヒントは、HTMLでグラフを書いたときに使った方法。
短冊上の小さい画像を用意して、それを連結させる方法。
#う〜ん、他にもいい方法があるはず、時間があるときに探そう

でも、なんかそれっぽく見えたのでOK?かなぁ

Macのペーストボードを監視する方法を模索

例えば、ペーストボードを監視して、その変更を受け取りたいときとかある。
NSPasteboardのリファレンスとか読んでみたけど、
タイミング的なものを取るものがない。。。ように見える

ってことで、
作ってみた。

構造は単純。
NSTimerを使って、ひたすらgeneralPasteboardのchangeCountを監視。
値が変わっていたら、Notificationを投げるというもの。

以下ソース

  1.  
  2. //インスタンス変数で、以下の変数を宣言。initで初期値を設定
  3. // BOOL isRunning = NO;
  4. // int nowChangeIntValue = -1;
  5. -(void)awakeFromNib{
  6.         //スケジュールを決めて実行
  7.         [ NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(checkPasteBoard) userInfo:nil repeats:YES ];
  8. }
  9.  
  10.  
  11. //———————————————-//
  12. // 時間でペーストボードを監視                   //
  13. //———————————————-//
  14. -(void)checkPasteBoard{
  15.         if( isRunning == YES ){
  16.                 return;
  17.         }
  18.        
  19.         isRunning = YES;
  20.        
  21.         NSPasteboard * pb = [ NSPasteboard generalPasteboard ];
  22.        
  23.         if( [ pb changeCount ] == nowChangeIntValue ){
  24.                 isRunning = NO;
  25.                 return;
  26.         }
  27.        
  28.         nowChangeIntValue = [ pb changeCount ];
  29.        
  30.         NSNotification * notification = [ NSNotification notificationWithName:@"SDZChangedPasteBoardNotification" object:pb ];
  31.         [ [ NSNotificationCenter defaultCenter ] postNotification:notification ];
  32.        
  33.        
  34.         isRunning = NO;
  35.        
  36. }
  37.  
  38.  
  39.  
  40. //———————————————–//
  41. // ペーストボードの値が変更になったときのセレクタ//
  42. //———————————————-//
  43. -(void)changedPasteBoard:(NSNotification *)aNotification{
  44.         NSLog(@"Changed PasteBoard");
  45. }
  46.  

Notificationを受け取った先で、typeを見て適宜処理すればOK
ただ、重くなるかもしれません。。。

画面を立ち上げた段階で、NSOutlineViewのすべての項目を開きたい!!
と思って、実装してみた。

やり方があってるかどうかは微妙だけど、
なんかそれっぽく動いたのでよしとしましょ。

  1. //前提として、
  2. //この処理の前に_treeArrayの値をセットして、NSTreeControllerを介して
  3. //OutlineViewに表示している。(IBOutletでOutlineViewは接続済み)
  4. //awakeFromNibの部分で以下のコードを実行。
  5.  
  6. //itemの総数を取得(階層レベルが一番上の個数)
  7. int count = [ _treeArray count ];
  8.  
  9. //ループ
  10. //ただし、スタートを総数-1からにしている。
  11. //これは、0からスタートしてしまうと、ROWの総数と階層レベルが一番上の個数とがずれてしまうから。
  12. //NSOutlineViewは子を開くとそれだけROWが増えてしまうので、
  13. //一番上のレベルのアイテムを選択できない。
  14. int i;
  15. for ( i = count - 1; i>=0; i ){
  16. [ OutlineView expandItem:[ OutlineView itemAtRow:i ] expandChildren:YES];
  17. }

注意が必要なのはループの回し方だけですね。

Viewに背景色をつけたい場合ってたまーにあります。
また、色を途中で変更したい場合とかもありますよね。

これらを実現するために、NSViewのサブクラスを作って実装してみました。

追加したインスタンス変数
NSColor * _color;
追加したメソッド
setBackgroundColor:(NSColor *)color

.mファイル

  1. //オーバーライド
  2. - (id)initWithFrame:(NSRect)frame {
  3. self = [super initWithFrame:frame];
  4. if (self) {
  5. // Initialization code here.
  6. _color = [ NSColor windowBackgroundColor ]; //初期の色は、Windowの背景色
  7. }
  8. return self;
  9. }
  10.  
  11. //オーバーライド
  12. //背景を設定
  13. - (void)drawRect:(NSRect)rect {
  14. // Drawing code here.
  15. [ _color set ];
  16. NSRectFill(rect);
  17. }
  18.  
  19.  
  20. -(void)setColor:(NSColor *)color{
  21. _color = color;
  22.  
  23. //描画し直し
  24. [ self display ];
  25. }

InterfaceBuilderはこんな感じで配置

で、実行結果はこのようになった

私が、普段から参考にさせていただいてるページを紹介。

Appleのリファレンスライブラリ。とりあえず、英語だが困ったときはここにアクセス
http://developer.apple.com/referencelibrary/

日本語
-Happy Machintosh Developing Timeの著者の木下さんのページ
Cocoa Programing Tips 1001は触りはじめのころ、本当にお世話になりました。
http://hmdt.jp/

-OOMORI.com
APIを和訳してくれています。よく使うメソッドには例文までついていて、
メソッドを調べたりするときなど、よく利用します。
http://www.oomori.com/

英語
-CocoaBuilder.com
メーリングリストで流れたアーカイブを検索、参照することができます。
基本、つまづくところはみんな一緒。かなりの情報量です。
日本語で探してもわからない場合に、利用します。
http://www.cocoabuilder.com/