Hatena::Groupiphone-dev

iPhoneアプリ開発まっしぐら★ このページをアンテナに追加 RSSフィード

tokoromのその他の日記

2011-12-25

uncaught exceptionが発生した場所を確認する

| 02:42 | はてなブックマーク -  uncaught exceptionが発生した場所を確認する - iPhoneアプリ開発まっしぐら★

uncaught exception でアプリリセット

uncaught exceptionでmain.mUIApplicationMainのところでリセットが発生しているときに、以下のようなログが出て、

2011-12-26 02:14:41.837 test[64894:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x12e2052 0x1896d0a 0x12ce674 0x4a7f7 0x2ab7 0x22d9d6 0x22e8a6 0x23d743 0x23e1f8 0x231aa9 0x21cffa9 0x12b61c5 0x121b022 0x121990a 0x1218db4 0x1218ccb 0x22e2a7 0x22fa9b 0x27d9 0x1fc5)

「で、けっきょくこのExceptionどこで発生してんねん」と思ってしまうことがあります*1

ふつーにぱっと見で認識できるスタックトレースとか出してくれればいいのにねって。

exception をキャッチしてデバッグ表示

それなら、uncaughtなexceptionをキャッチしてあげようということで、UIApplicationMainのところをtry/catchで囲ってデバッグ表示を追加してみました。

int main(int argc, char *argv[])
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  int retVal;
  @try {
    retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  }
  @catch (NSException *exception) {
    NSLog(@"%@", [exception callStackSymbols]); //< ★1
    @throw exception; //< ★2
  }
  @finally {
    [pool release];
  }
  return retVal;
}
★1

どうもiOS4.0から、NSExceptionにcallStackSymbolsという便利メソッドが追加されたようなのでそれを利用。

そうすると、uncaughtなexceptionをキャッチして、以下ようにそのexceptionが発生するまでの経緯がログ出力されます。

2011-12-26 02:27:18.933 test[65600:f803] (
	0   CoreFoundation                      0x012e206e __exceptionPreprocess + 206
	1   libobjc.A.dylib                     0x01896d0a objc_exception_throw + 44
	2   CoreFoundation                      0x012ce674 -[__NSArrayI objectAtIndex:] + 196
	3   test                                0x0004a6e7 -[RootViewController init] + 215
	4   test                                0x000029a7 -[AppDelegate application:didFinishLaunchingWithOptions:] + 567
	5   UIKit                               0x0022d9d6 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1292
	6   UIKit                               0x0022e8a6 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 508
	7   UIKit                               0x0023d743 -[UIApplication handleEvent:withNewEvent:] + 1027
	8   UIKit                               0x0023e1f8 -[UIApplication sendEvent:] + 68
	9   UIKit                               0x00231aa9 _UIApplicationHandleEvent + 8196
	10  GraphicsServices                    0x021cffa9 PurpleEventCallback + 1274
	11  CoreFoundation                      0x012b61c5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
	12  CoreFoundation                      0x0121b022 __CFRunLoopDoSource1 + 146
	13  CoreFoundation                      0x0121990a __CFRunLoopRun + 2218
	14  CoreFoundation                      0x01218db4 CFRunLoopRunSpecific + 212
	15  CoreFoundation                      0x01218ccb CFRunLoopRunInMode + 123
	16  UIKit                               0x0022e2a7 -[UIApplication _run] + 576
	17  UIKit                               0x0022fa9b UIApplicationMain + 1175
	18  test                                0x0000258b main + 187
	19  test                                0x00001d65 start + 53
)

これを見れば「RootViewControllerのinitメソッド内にダメなコードがあるんだね」ということがすぐに分かるようになります。

★2

ちなみにexceptionをキャッチしっぱなしだとアプリ的には異常がなかったという扱いになってしまうので、同じexceptionをそのままthrowしておきます。

DEBUGビルド時にだけこれを有効に

DEBUGビルド時にだけこれを有効にしたい場合には、以下のように「#ifdef DEBUG」で囲ってやるのが良いかもしれません。

int main(int argc, char *argv[])
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  int retVal;
#ifdef DEBUG
  @try {
#endif
    retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
#ifdef DEBUG
  }
  @catch (NSException *exception) {
    NSLog(@"%@", [exception callStackSymbols]);
    @throw exception;
  }
  @finally {
#endif
    [pool release];
#ifdef DEBUG
  }
#endif
  return retVal;
}

ARCを利用する場合

ARCを利用する場合はもうちょっとシンプルにできる。

int main(int argc, char *argv[])
{
  int retVal;
  @autoreleasepool {
#ifdef DEBUG
    @try {
#endif
      retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
#ifdef DEBUG
    }
    @catch (NSException *exception) {
      NSLog( @"%@", [exception callStackSymbols] );
      @throw exception;
    }
#endif
  }
  return retVal;
}

でも、こんなごちゃごちゃ書かなくてもよいかもしれない

ちなみに、uncaught exceptionを制御するための「NSSetUncaughtExceptionHandler」といった関数もあるようで、こちらを使えばもっとスマートな方法があるかもしれません*2

もう眠くなったのと、↑でも特に困らなかったので今日はここまで。

*1:そもそもこのExceptionの発生場所をすぐに特定できる方法を既知のかたは是非フォローお願いしますm(_ _)m

*2:こちらも既知のかた是非フォローをお願いします

2011-11-27

MacアプリでもUIKitを使ってみる with Chameleon

| 10:42 | はてなブックマーク -  MacアプリでもUIKitを使ってみる with Chameleon - iPhoneアプリ開発まっしぐら★

Chameleon

Chameleon - UIKit for Mac development

このChameleonを使うとMacアプリの開発でもUIKitを使えるようになるとのこと。

前々から気になっていたので少し試してみた。

Chameleon自体はBigZaphod/Chameleon - GitHubからダウンロード可能。

また、Chameleonを試用した本記事のソースコード一式はtokorom/ChameleonUIKitDemo - GitHubからダウンロード可能。

今回は、UIViewControllerで画面を1つ作り、そこにUIImageViewやUIButtonを貼りつけて動かしてみるところまで実験。


まずはプロジェクトにChameleonを取り込む

※上記サイトからあらかじめChameleonをダウンロードしておくこと

まずはXcodeで空のMacアプリのプロジェクトを作成する。

そのプロジェクトにChameleonの中のUIKit.xcodeprojだけ追加する。

次に、Build PhasesのLink Binary With LibrariesUIKit.frameworkを追加する。

↓このあたりの実施後の画面が↓になります↓

f:id:tokorom:20111128103910p:image


UIKitを他のコードから使えるようにimport

■ChameleonUIKitDemo-Prefix.pch

プロジェクトの中にはじめからあるプリコンパイル済みヘッダで、上記のように

UIKit/UIKit.h

UIKit/UIKitView.h

をimportしておく。

もちろん、各ソースコードから個別にimportしても構わない。


UIKit用のAppDelegateを作っておく

■ChameleonAppDelegate.h

■ChameleonAppDelegate.m


ここは普段のiOS用アプリ開発とほぼ同じでOK!

唯一違うのは、windowやViewControllerのautoresizingMaskを明示的に指定するコードを追加するくらい。

UIViewControllerがそのまま使えるというのは素晴らしい!


はじめに表示するRootViewControllerを作っておく

ここはiOS用アプリ開発と全く同じ!

ここではUIImageViewとUIButtonを追加している。

※必要な画像はあらかじめプロジェクトに追加しておくこと

■RootViewController.h

■RootViewController.m


Mac用のAppDelegateをUIKit用AppDelegateに接続

■AppDelegate.h

■AppDelegate.m


ここでは、UIKitViewと先ほど作成したChameleonAppDelegate(UIKit用のAppDelegate)を追加する。

UIKitViewは後ほどInterface Builderで接続するためIBOutletを付けておく。

そして、applicationDidFinishLaunchingの中で、UIKit用AppDelegateに移管するため、UIKitViewのインスタンスに対してlaunchApplicationWithDelegateをコールして引数にUIKit用AppDelegateのインスタンスを指定する。


Interface Builder で最後の仕上げ

f:id:tokorom:20111128104037p:image

最後に、Interface BuilderでMainMenu.xibを開き、WindowsのメインViewにUIKitViewを追加する。

UIKitViewというオブジェクトはツール上は存在しないので、代わりにCustom Viewというのを貼り付ける。

そして、そのClass名をUIKitViewに変更する。

※このあたりは↑のスクリーンキャプチャのとおりです


これができたら、そのUIKitViewをApp Delegateに作っておいたOutlet(chameleonView)に接続する。

以上で完了。


ビルド&ラン!

f:id:tokorom:20111128103636p:image

実行してみたところUIImageViewUIButtonもきっちり動き、ボタン押下のアクションも問題なくハンドリングされていました。

なかなか面白いですね!

UIKit.frameworkをappの中に含める設定

なお、このままだとappとして出力した後にUIKit.frameworkが見つからなくて動作しなくなってしまう。

そのため、Builde PhasesCopy Filesという項を追加し、そこにUIKit.frameworkを設定しておく必要がある。

f:id:tokorom:20111130030943p:image

2011-07-09

VimでのiOSアプリ開発に便利なsnippetsファイル作りました

01:48 | はてなブックマーク -  VimでのiOSアプリ開発に便利なsnippetsファイル作りました - iPhoneアプリ開発まっしぐら★

VimでのiOSアプリ開発生活を快適にするために、

  • Foundation.framework
  • UIKit.framework

のクラス、メソッドなどをヘッダーファイル*1から抜いてVim用のsnippetsファイルを作成しました。


snipMate.vim や neocomplcache で利用できる見込み*2です。

もし必要なかたがいらっしゃいましたら

からダウンロードをお願いします。


例えば、pushViewController:animated: のsnippetは、

snippet pushViewController:animated:
	pushViewController:${1:(UIViewController*)viewController} animated:${2:(BOOL)animated}

のように定義されており、どのメソッドも引数のところがプレースホルダになっています*3


なお、スニペット名をメソッドの名前そのままにして長いので、neocomplcasheによるスニペット補完の利用が推奨されます*4


以下、自分用メモ。


各フレームワークのtagsの作成

例えば、Foundation.frameworkとUIKit.frameworkのtagsを作成する場合、

$ ctags -R --language-force=ObjC /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/Foundation.framework/Headers /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/UIKit.framework/Headers

とする。

ObjCに対応したctagsを利用する必要あり。

他のフレームワークのやつも欲しければ、ヘッダーファイルが入っているフォルダのパスを加えればOK。


tagsからsnippetsファイルを作成

今回は、rubyでスクリプトを書いて対応。

  • tags2vimsnippets.rb
inputFileName = ARGV[0]
outputFileName = ARGV[1]

result = []
snippets = []
IO.foreach( inputFileName ) {|l|
    result = l.scan(/^[+-]([:a-zA-Z0-9_-]+).*\/\^[+-] \([^)]*\)([^;]+);/)
    if !result.empty?
        next if 6 > result[0][0].length 
        snippets.push( sprintf("snippet %s", result[0][0]) )
        t = result[0][1]
        t = t.gsub(' *', '*').gsub(' (', '(')
        count = 0
        while /([^{][^0-9]+?:)(\(.+?\)[^() ]+)/ =~ t
            count = count + 1
            t = t.sub(/([^{][^0-9]+?:)(\(.+?\)[^() ]+)/, sprintf('\1${%d:\2}', count))
            break if 64 < count
        end
        snippets.push( sprintf("\t%s", t) )
        next
    end
    result = l.scan(/^([:a-zA-Z0-9_]+)/)
    if !result.empty?
        next if 6 > result[0][0].length 
        snippets.push( sprintf("snippet %s", result[0][0]) )
        snippets.push( sprintf("\t%s", result[0][0]) )
    end
}

snippets.uniq!

out = open( outputFileName, 'w' )
snippets.each {|k|
    out.puts k
}
out.close

このスクリプトに先ほど作ったtagsファイルを渡してやる。

$ ruby tags2vimsnippets.rb tags objc.snippets

これでsnippetsファイル完成。

変な部分があったら正規表現を見直す必要あり。

*1:iOS4.3のSDKのもの

*2:手元ではsnipMate.vimのほうでしかチェックしてません

*3:全部チェックしているわけではないので不備はあるかも

*4:スニペット補完を使うなら生のメソッド名のままのほうが便利だと思います

KalynKalyn2011/09/16 19:19You know what, I'm very much inlcnied to agree.

ppoyzurtppoyzurt2011/09/16 20:49dShnsD <a href="http://lfyzjbluhbqu.com/">lfyzjbluhbqu</a>

hgvcreushgvcreus2011/09/17 21:369XOHOe , [url=http://sggbqokkwiui.com/]sggbqokkwiui[/url], [link=http://yzdkxnyyohny.com/]yzdkxnyyohny[/link], http://yommpsvjyaop.com/

gifkjbjbrygifkjbjbry2011/09/19 00:08HVLjji <a href="http://wwxbkjkabkiw.com/">wwxbkjkabkiw</a>

2011-04-25

じつはもっと簡単なAdHocアプリ(ipa)の作成方法があった件... for Xcode4

| 01:05 | はてなブックマーク -  じつはもっと簡単なAdHocアプリ(ipa)の作成方法があった件... for Xcode4 - iPhoneアプリ開発まっしぐら★

昨日AdHoc版の作り方について記事を書いたが...

この後、Twitterでnovi_さんから

あれ、これって普通にReleaseビルドして、Shareの時にCode Signじゃだめでしたっけ。

というご指摘。

要するに、

  • AdHoc用Configurationとかは作らなくてもOK
  • Edit SchemeとかしなくてもOK

ということ。

実際、novi_さんの言うとおりやってみると、たしかにそれだけでOKでした。

ご指摘ありがとうございます!

以下、AdHocアプリ(ipa)作成の短縮版、まとめさせていただきます。


事前準備

前回と同様、

  • 自分のPCにDistribution(配信用)の証明書がインストールされていること
  • iOS Provisioning Portal に、AdHoc用アプリをインストールする対象端末のUDIDが登録されていること
  • 対象端末のUDIDが登録されたDistribution用provisioningファイルを取得してあること

の3つの準備がされていることを前提とする。

オーガナイザにAdHoc用provisioningファイルを登録する

この手順はまだやってなければ前回と同様に必要。

Xcode4の画面の右上のほうに[Organizer]というボタンがあるのでそれをクリックし、オーガナイザを表示する。

次に、オーガナイザの[Devices]タブを選択し、オーガナイザの左側のリストから[LIBRARY]→[Provisioning Profiles]を選択する。

すると、オーガナイザには現在登録されているprovisioningファイルの一覧が表示されるため、そこに今回用のAdHoc用provisioningファイルをドラッグ&ドロップして登録しておく。

f:id:tokorom:20110425013730p:image

Let's Archive!

デフォルトではArchiveを実行すると、Release用Configurationが利用されるようになっている。

AdHoc用として特に変更する内容がなければそのままRelease用設定を使えばOKということ。

ということで、Release用Configurationが利用されるデフォルト設定のまま、Xcode4のメニューから[Procust]→[Archive]と選択してArchiveを実行する*1

すると下図のようにArchiveした結果の一覧画面みたいのが表示される。

f:id:tokorom:20110425015414p:image

ここで[Share...]ボタンをクリックすると、出力方式として「ipa」が選択可能になっている。

f:id:tokorom:20110425015415p:image

ここでipaを選択し、Identityの項で"AdHoc用provisioning"をきちんと選択さえすれば、あとは[Next]ボタンを押して出力するだけ。

ipaファイルが出力できたら、これまでどおりiTunesに

  • ipa
  • provisioning

の2ファイルをドラック&ドロップして同期をとってAdHocアプリのインストール完了です!

まとめ

なんと、こんな簡単なことだったのか!

要するに、novi_さんからのご指摘どおり、Archive実行後にAdHoc用のprovisioningファイルを選択してSignするだけ。

前回記事に書いたように、律儀にAdHoc用のConfigurationを作るのは

  • Release用ConfigurationとAdHoc用ConfigurationでCode Signing以外に変更すべき箇所がある場合

のみということでしょう。

*1:Archiveが実行できない場合、Destinationが実機向けになっていることを要確認

NamfonNamfon2012/02/05 11:56You're on top of the game. Thanks for sahirng.

pwuoiniipwuoinii2012/02/05 23:27XoApRo <a href="http://uwonnqrubqkf.com/">uwonnqrubqkf</a>

ksjaflupngwksjaflupngw2012/02/07 22:05x6auYW , [url=http://ummczstbdrte.com/]ummczstbdrte[/url], [link=http://ptymfcamjcyn.com/]ptymfcamjcyn[/link], http://eorivdakqmdq.com/

gwniqecldvigwniqecldvi2012/02/10 23:51GShFEG <a href="http://rjcdrwhnwmkq.com/">rjcdrwhnwmkq</a>

wmochxucwmochxuc2012/02/12 01:35frSunT , [url=http://ntbtunnxrjmu.com/]ntbtunnxrjmu[/url], [link=http://ktoerrvgkfkb.com/]ktoerrvgkfkb[/link], http://hehaidhlfyno.com/

2011-04-24

Xcode4でのAdHocアプリ(ipa)の作成方法詳解

| 01:57 | はてなブックマーク -  Xcode4でのAdHocアプリ(ipa)の作成方法詳解 - iPhoneアプリ開発まっしぐら★

はじめに注意!

この記事を書いた後にTwitterでもっと簡単な方法があるよというのをご教示いただきました。

そのため、より簡単な方法については、次の記事をご参照願います。

こちらの記事は、

  • AdHoc配信用に専用の設定(Configuration)が必要な場合

のみご参照ください。

Xcode4になって

どうもこれまでと同じかんじではAdHocアプリを作成できないとか。

自分の場合、今日までXcode4さえインストールできていなかったので、これを機会にXcode4をインストールしてAdHoc用ビルドをしてipa形式で出力するところまでを試してみようと思い立った。

なお、基本的には

のとおりにやっただけ。

そのため、英語がNGでないかたは↑をそのまま見てもらったほうがよいかと。

以下、基本的にはXcode3→Xcode4の差分しか記載しておりません。

事前準備

この記事は、Xcode3以前でAdHocなアプリをビルドした経験のあるかたを対象とし、provisioningファイルの作成やCertificationについては言及しない。

そのため事前に

  • 自分のPCにDistribution(配信用)の証明書がインストールされていること
  • iOS Provisioning Portal に、AdHoc用アプリをインストールする対象端末のUDIDが登録されていること
  • 対象端末のUDIDが登録されたDistribution用provisioningファイルを取得してあること

の3つの準備がされていることを前提とする。

オーガナイザにAdHoc用provisioningファイルを登録する

この手順は基本的にはXcode3までと同じである。

Xcode4の画面の右上のほうに[Organizer]というボタンがあるのでそれをクリックし、オーガナイザを表示する。

次に、オーガナイザの[Devices]タブを選択し、オーガナイザの左側のリストから[LIBRARY]→[Provisioning Profiles]を選択する。

すると、オーガナイザには現在登録されているprovisioningファイルの一覧が表示されるため、そこに今回用のAdHoc用provisioningファイルをドラッグ&ドロップして登録しておく。

f:id:tokorom:20110425013730p:image

AdHoc用Configurationを作成する

まずはXcode3のときと同様にAdHoc用のConfigurationを作成する必要がある。

Xcode4では、まず、該当PROJECTを選択し、[Info]タブを選択する。

すると、下図のようにDebugとReleaseというConfigurationが並んでいるはずなので、ReleaseをベースにしてAdHocを追加することにする。

それには、Configurationsの下にある[+]ボタンをクリックし、「Duplication "Release" Configuration」を選択する。

f:id:tokorom:20110425013731p:image

すると、Releaseをベースとした新しいConfigurationが作成されるので名前を "AdHoc" に設定する。

AdHoc用ConfigurationにAdHoc用のprovisioningファイルを設定する

次に、AdHoc用ビルドの際に、AdHoc用のprovisioningファイルが利用されるように設定する。

該当PROJECTを選択し、今度は[Build Settings]タブを選択する。

Xcode3までと違い、各Configurationの設定項目がマージされた形で表示される。

この中の[Code Signing]→[Code Signing Identity]→[AdHoc]→[Any iOS SDK]の値の部分をクリックし、先ほどオーガナイザに登録したAdHoc用provisioningファイルを選択する。

f:id:tokorom:20110425014016p:image

SchemeのArchiveの項でAdHoc用Configurationが利用されるよう設定する

次に、Schemeの設定を変更する。

Schemeの設定変更は、Xcode4の左上の下図の赤枠のところをクリックし「Edit Scheme...」を選択することで可能。

f:id:tokorom:20110425015238p:image

こうして表示した編集ダイアログの左から「Archive」を選択し、「Build Configuration」の項をさきほど作成した「AdHoc」に変更する。

あとは、Desitinationも「iOS Device」にしておく*1

これで[OK]ボタンをクリックすれば、Schemeの設定変更は完了。

f:id:tokorom:20110425015239p:image

Let's Archive!

あとは出力するだけ*2だ。

Xcode4のメニューから[Procust]→[Archive]と選択してArchiveを実行する*3

すると下図のようにArchiveした結果の一覧画面みたいのが表示される。

f:id:tokorom:20110425015414p:image

ここで[Share...]ボタンをクリックすると、出力方式として「ipa」が選択可能になっている。

f:id:tokorom:20110425015415p:image

ここでipaを選択し、Identityできちんと該当のAdHoc用provisioningを選択さえすれば、あとは[Next]ボタンを押して出力するだけ。

ipaファイルが出力できたら、これまでどおりiTunesに

  • ipa
  • provisioning

の2ファイルをドラック&ドロップして同期をとってAdHocアプリのインストール完了です!

*1:実機が接続してある場合には違う名前になってるかも

*2:もちろんふつうにアプリがビルド可能な状態であること

*3:Archiveが実行できない場合、Destinationが実機向けになっていることを要確認