Home > 技術 > はじめてのiPhoneアプリ - カメラアプリを作る際にハマったことのメモ

はじめてのiPhoneアプリ - カメラアプリを作る際にハマったことのメモ

とにかく情報はStackOverFlowたよりです。
なので必然的に、ググるときのキーワードも英語になっていきます。

Webに比べるとあまりにもまだまだiPhoneアプリの解説や資料は少ないので、以下の記事がどなたかの助けになれば幸いです。

AppCodeというIDEを使ってみる

appcode.png

JetBrains AppCode: an Objective-C IDE That Makes a Difference

もう、これはすごかった。
さすが1万もするアプリだなーという感じです。

XCodeでもAppCodeと同じようなことができるんだとは思いますが、あまりにも直感的に使えるIDEなので、これがないと厳しいです。
地味ですがダブルコートを打つと自動的に@が補完されるのとか助かります。

あとはコードフォーマッター、どうもXCodeにはないらしいので、これはかなり重宝しました。
リファクタリングもすごくいいです。

gitとも連携できるので、ファイルのヒストリーも見れます。
あとはもう触ればとりこですよ。

ただ一点だけ、
imageView.contentMode =
こう打ったら、この右側で補完されるものはcontentModeで絞り込んで欲しい。
すごいいっぱい出てくるから、毎回ググッて貼り付けてます。

あとは、
2011-12-11 - 人工無脳が作りたい
こちらを読むとさらに使いたくなります。

gitにも対応していてdiffをしてくれるので比較も便利!


AppCodeのカラースキーマをMonokaiにする

以下がカラースキーマの置き場所。
ここにxmlファイルを配置して、PreferencesのColorから選べばOK!

~/Library/Preferences/appCode10/colors/

WebStormで使っていたMonokaiテーマだと、うまくObjective-Cのカラーリングがされなかったので、

hezi/monokai-appcode

こちらを使ってみた。すごく良い感じ。


XCodeのアシスタントエディターがうまく機能しない

アシスタントエディターを使うと、画面がセパレートされ、.hファイルと.mファイルを同時にみたり、
storyboardとそれに紐付くControllerファイルを見ることができますが、これがたま〜〜に機能しなくなるときがあります。

具体的にはstoryboardで選択したViewとそれに設定しているCustomClassのViewControllerが開かない現象です。

これは、何回かXCodeを再起動すれば紐付くようになりました。
とくにstoryboardに配置するViewControllerの量が増えてくるとこの現象が発生するような気がします。

何が原因かはまだ不明。


Objective-Cでシングルトン

Objective-C でシングルトンパターン | Sun Limited Mt.
こちらの記事を参考に書いてみました。

■gist
Objective-Cでシングルトン -- Gist

使い方は、

プロパティの場合はこんな感じですね。


UILabelにPaddingを入れたい

UILabel のテキストのmargin やらpadding やらを設定して表示位置を調整する - laiso - iPhoneアプリ開発グループ
に書かれている方法でバッチリでした。

■gist
UILabelにPadding入れる -- Gist


画像をリサイズしたい

写真アプリなら必ずやりたくなる処理。
必要な分はメソッド化しておきました。

■gist
Objective-Cで画像のリサイズ系 -- Gist

こう書いてみたものので、画像まわりでハマったので、メモしておきます。
UIImageViewのサイズは320だとしても、その中に収める画像UIImageのサイズは640にしないとretinaでぼやけます。
はじめ分からなくて両方に320を指定していたのですが、ぼやけてしまってハマりました。

UIImageViewのリサイズコードは以下の感じです。

そしてさらに、imageView.contentMode = UIViewContentModeScaleToFillにします。
これでInstagramなどと同じようにキレイな画像が出るようになりました。
おー!


UILabelにセットした文字列が長い場合にfontが自動で小さくなるのを防ぐ

XCodeのAutoshrinkのチェックを外す。
或いは、UILabelをプログラムからViewにaddする。


カメラ機能の実装

今回の主人公であるカメラ部分の実装になります。
はじめ、ダレもがやるUIImagePickerControllerを使った実装をしていたのですが、どうもInstagramやPathと違う、何かが違う...
となりまして、いろいろ調べてみると、AVCaptureDeviceクラスを使うとそれっぽいことが可能になると。
(なんだかややこしそう...)

ざっくりデザインを無視して、

  1. カメラが撮れる
  2. アルバムから選べる
  3. 前面カメラを選べる
  4. Flashを切り替えられる
  5. フォーカスできる
  6. 閉じるボタンがある

これらの機能をシンプルに1個のViewControllerで実装してみました。
カメラを撮ると画面右下にちっちゃく表示するようにしています。

あとは確認画面に行ってフィルター掛けてもいいし、ゴニョゴニョしてもいいし。

意外と日本語のサイトでも海外でもそうですが、シンプルにまとまって一つのプロジェクトになっているのがなかったので、
今後これをもとにいろいろ遊んでみようと思います。

■github
hisasann/AVCaptureDeviceCamera


絵文字をMySQLに入れる方法

MySQLで4バイトのUTF-8文字を扱ってみる - HHeLiBeXの日記 正道編

utf8mb4にするというお話


カメラのフラッシュのオートが効かない

はじめ以下のようにtouchModeAVCaptureTorchModeAutoを指定していたんですが、
暗いところで撮影してもいっこうにフラッシュがつかなくてハマっってしまいました。

実際にはflashModeAVCaptureFlashModeAutoを入れるとうまいこと行きました。

たしかにトーチじゃないよな。


iOSで使えるFilterの種類が知りたい!

出力結果:

properties - (
CIAdditionCompositing,
CIAffineTransform,
CICheckerboardGenerator,
CIColorBlendMode,
CIColorBurnBlendMode,
CIColorControls,
CIColorCube,
CIColorDodgeBlendMode,
CIColorInvert,
CIColorMatrix,
CIColorMonochrome,
CIConstantColorGenerator,
CICrop,
CIDarkenBlendMode,
CIDifferenceBlendMode,
CIExclusionBlendMode,
CIExposureAdjust,
CIFalseColor,
CIGammaAdjust,
CIGaussianGradient,
CIHardLightBlendMode,
CIHighlightShadowAdjust,
CIHueAdjust,
CIHueBlendMode,
CILightenBlendMode,
CILinearGradient,
CILuminosityBlendMode,
CIMaximumCompositing,
CIMinimumCompositing,
CIMultiplyBlendMode,
CIMultiplyCompositing,
CIOverlayBlendMode,
CIRadialGradient,
CISaturationBlendMode,
CIScreenBlendMode,
CISepiaTone,
CISoftLightBlendMode,
CISourceAtopCompositing,
CISourceInCompositing,
CISourceOutCompositing,
CISourceOverCompositing,
CIStraightenFilter,
CIStripesGenerator,
CITemperatureAndTint,
CIToneCurve,
CIVibrance,
CIVignette,
CIWhitePointAdjust
)

[via]
iphone - CIColorPosterize in iOS - Stack Overflow

フィルターはこれから実装するのですが、
『第3回 iphone_dev_jp 東京iPhone/Mac勉強会』で vImage について発表してきました - Over&Out その後
こちらを見て、vImage vs CoreImage vs OpenCV といろいろやり方があるのが分かりました。
この道もそれはそれは大変そうです。


UIImageViewにUITapGestureRecognizerをセットしてもイベントが発生しない

UIImageViewに設定したGestureイベントが発生しない - 日々精進
こちらの記事のとおりなんですが、

こんな感じでUIImageViewにUITapGestureRecognizerをセットしても、イベントが発生しない。
これはUIImageViewのuserInteractionEnabledをYESにすれば発生するようになります。


メモリ不足によりviewDidUnloadが呼ばれた場合に状態を復元する方法

Cocoaの日々: UIViewController - 状態保存復元パターン

今回、とってもハマったのが、このviewDidUnloadだ。

落ちないiPhoneアプリが作りたい自分のための、押さえておくべきポイントたくさん。 - かってぃのブログ | choilog [チョイログ]
【iPhone】メモリ不足時のシミュレートとデバッグ | iPhoneアプリで稼げるのか
2009-01-21 - f-shinの日記 - iPhoneアプリ開発グループ
iPhoneアプリ開発まっしぐら★ - iPhoneアプリ開発グループ
Safx: ViewControllerにおけるビュー管理サイクルとメモリ警告シミュレーションによるアンロード処理について
このあたりをよく読むと理解できるか、今回発生した現象はあまりググっても出て来なかったのでここにメモしておきます。

今回使ったPathライクなUIライブラリはECSlidingViewControllerですが、
これは最上位となるViewControllerがいて、それが実際のECSlidingViewControllerになります。

つまり、

  • ViewController
  • TopViewController

の2つがいて、ViewControllerはECSlidingViewControllerをインプリメントしているだけ、
本当のトップViewはTopViewControllerになります。

TopViewControllerにあるUITableViewをタップするとサムネイル画面に遷移する仕様なので、
ThumbnailViewControllerとしときましょう。

このThumbnailViewController画面でカメラをModalで開きます。
(下からニュルっと出てきます)

こんな感じのコードです。
よくあるコードですね。

このCameraViewControllerを開くまでに、画像を結構表示しているとして、viewDidUnloadが発生しやすい状態にしておきます。(またはシミュレータででメモリワーニングを出す)
そして、CameraViewControllerをModalで開いてTumbnailViewControllerのviewDidUnloadが発生し、CameraViewControllerを閉じたときに画面が真っ白になってしまう現象が発生しました。
ここが真っ白になってしまうは、ECSlidingViewControllerの影響かもしれません。

いろいろな解説記事を読むと、viewDidUnload後にビューが表示されるタイミングでもう一度viewDidLoadが発生すると書かれているんですが、これが発生しませんでした。

結論を言うと最も親であるViewControllerもviewDidUnloadされてしまっていました。

これにより、TumbnailViewController意外のTopViewControllerやViewControllerもなくなってしまっていたので、真っ白の画面になってしまいました。
そこでViewControllerのviewDidUnloadが発生したタイミングでTumbnailViewControllerまでを復元するようにするコードを書きました。

つまり、メモリワーニングでModal以外のViewが破棄されても、Modalが閉じられて後ろ側が見えるようになる瞬間で、

  • ViewControllerのviewDidLoadが呼び出される
  • TopViewControllerを復元
  • ThumbnailViewControllerを復元
  • サムネイル画面があたかもずうっとあったかのように見せる。

こうなりました。(ややこしい!)

ちなみに、ModalではなくNavigationでやった場合、同じようにメモリワーニングが起こると、TopViewControllerまで強制的に戻される動作をするので、あまりにも不自然な動作ゆえにやめました。

iPhoneアプリはメモリまわりがかなり難しいですが、viewDidUnloadもこれまた難しい。
以下に、真っ白にはならないのですが、メモリワーニング後に適切な画面が表示されないパターンを作ってみました。
参考までに。

■github
hisasann/ModalDidReceiveMemoryWarning


UIImagePickerControllerでフォトライブラリーを開いたときのナビゲーションバーの色を変えたい

navigationBar.tintColorにUIColorを入れると変えられるようです。


PullToRefreshしたい!

sonnyparlin/PullToRefresh
これを使えばほんとうに簡単に実現できました。

サンプルのプロジェクトを作成したのでこちらを参考にしてみてください。

■ArcPullToRefresh
hisasann/ArcPullToRefresh

だいたい以下のようなコードを追加するだけになります。


自分のアプリのメモリ使用量をアプリ内で知る方法

こんな感じ。

[via]
自分のアプリが使用しているメモリサイズを取得するには - The iPhone Development Playground


データを保存する場所について

ディレクトリいろいろ。

/アプリ名.app
メインバンドルと呼ばれている。アプリのリソースファイルを保存するためのディレクトリ。読み取り専用。

/Documents
アプリがファイルを作成して保存することができるディレクトリ。永続化したデータを格納する場合ここを使うのが一般的

/Library/Caches
アプリが一時的に使う情報を保存するディレクトリ

/Library/Preferences
アプリケーションの設定を保存するディレクトリ。NSUserDefaults のデータが保存される

/tmp
一時ファイルを保存するディレクトリ。アプリが動作してないときに消される可能性がある

[via]
iOS でデータを永続化する方法 - A Day In The Life

パスの取得いろいろ。

NSHomeDirectory()
/var/mobile/Applications/FBAD8D21-CFFC-4804-85AC-7F6F02DCB836

NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
/var/mobile/Applications/FBAD8D21-CFFC-4804-85AC-7F6F02DCB836/Documents

NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)
/var/mobile/Applications/FBAD8D21-CFFC-4804-85AC-7F6F02DCB836/Library

NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)
/var/mobile/Applications/FBAD8D21-CFFC-4804-85AC-7F6F02DCB836/Library/Caches

NSTemporaryDirectory()
/private/var/mobile/Applications/FBAD8D21-CFFC-4804-85AC-7F6F02DCB836/tmp/

[via]
アプリのsandboxをURLを取り出す - iPhoneアプリケーション開発


Exif情報を付与する - これは苦労しました...

もうこれはとにかくハマりました。
Exifなどのメタデータを自由に操作するにはどうするか - AppStair
JPEGファイルのExif情報を読み書きする - 強火で進め
無題メモランダム: [iOS]画像にEXIFなどのメタデータを付加する方法メモ
[AssetsLibrary] フォトライブラリの写真にExif情報を付けて保存する - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ
このあたりのすばらしい記事を読んでいろいろ試してみたんですが、どうしてもサーバーに画像をPostするとExifが入らない現象に悩まされました。

結果的に、PostするときにUIImageからNSDataに変換していたんですが、ここでExif情報を抜け落ちているのが分かりました。
こんなコードです。

これをやってしまうと、Exifが抜け落ちてしまう...
以下に2種類のExif付与の方法を書いておきます。

  1. 画像ファイルは作らずにNSDataを作る方法
  2. 画像ファイルを作る方法

たとえば、Exifを付与した画像ファイルを、また別のViewでも使いたい場合など、ファイルに保存しておくと良いかもしれません。

Exifを付与しNSDataを作成するサンプルコード

以下のコードでは、

  • kCGImagePropertyExifDateTimeOriginal
  • kCGImagePropertyExifDateTimeDigitized

の2つのExif情報を付与しています。

■gist
Exifを付与しNSDataを作成するサンプルコード -- Gist

Exifを付与し、/tmpに画像を保存するサンプルコード

以下のコードでは、


  • kCGImagePropertyExifDateTimeOriginal

  • kCGImagePropertyExifDateTimeDigitized

  • の2つのExif情報を付与しています。

    ■gist
    Exifを付与し、/tmpに画像を保存するサンプルコード -- Gist

    そして、これをサーバーにPostする際に

    のようにしてdataを作りこれをPostしています。
    ちなみにExifを抜き出す作業はそこそこメモリ資料率が上昇します。
    これはどうも画像サイズが大きければ大きいほど上昇するようです。なので、小さいサイズにしたUIImageから抜くとかすると抑えられる感じです。

    ■参考リンク
    Exif TAG

    フォトライブラリーから選んだ画像のExifから撮影日を取得したい

    これもたいそうハマったんですが、
    UIImagePickerControllerのsourceTypeにUIImagePickerControllerSourceTypePhotoLibraryを指定してフォトライブラリー画面を出し、そこから写真を選ぶと

    こんなメソッドが呼ばれます。

    このときにUIImagePickerControllerのallowsEditingをNOにしていると、Exif情報を持っているはずのeditingInfoがnilになってしまいます。

    YESにするとExifが入った状態になります。

    [via]
    [iOS][Objective-C]カメラを使ってみようの巻 | なんだかんだ言って甘いもの好きでしょ?日記

    UIImagePickerControllerでイメージピッカーからExif情報を抜き出す

    ALAssetsLibraryを使う以下の方法で落ち着きました。

    ■gist
    UIImagePickerControllerでイメージピッカーからExif情報を抜き出す -- Gist

    [via]
    UIImagePickerController経由でカメラロールの写真のExifにアクセスしたいねん | Eudyptes Chrysocome

    このメソッドの引数のimageのサイズは以下。(iPhone4の場合)

    image.size.width - 640.000000
    image.size.height - 640.000000

    editingInfoから抜き出したeditedImageのサイズは以下。

    editedImage.size.width - 0.000000
    editedImage.size.height - 0.000000

    editingInfoから抜き出したoriginalImageのサイズは以下。(つまりもとの画像サイズ)

    originalImage.size.width - 1434.000000
    originalImage.size.height - 1920.000000

    [via]
    iphone - UIImagePickerController allowsEditing=YES Issue - Stack Overflow

    位置情報へのアクセスを許可していない場合に、ALAssetsLibraryからフォトライブラリーにアクセスすると出るエラー

    どうもALAssetsLibraryを使ってExifを抜き出すコードがあると?、位置情報を取得しても大丈夫ですか的なダイアログで出るんですが、そこでいいえを選択するか、はいにはしたけどあとからシステム環境設定でいいえにした場合に、ALAssetsLibraryのfailureBlockに入ってしまうようです。

    2012-08-01 18:32:48.531 HogeAPP[15197:707] error - Error Domain=ALAssetsLibraryErrorDomain Code=-3311 "User denied access" UserInfo=0x2c81e0 {NSLocalizedFailureReason=The user has denied the application access to their media., NSUnderlyingError=0xceae140 "The operation couldn't be completed. (PersistentURLTranslator error 1.)", NSLocalizedDescription=User denied access}

    はじめ原因が分からなかったんですが、よくよく自分がやったことを思い出したら許可しないにしたなーと。

    iPhoneアプリ開発塾
    iPhoneアプリ開発塾
    posted with amazlet at 12.09.11
    カワサキ タカシ
    技術評論社
    売り上げランキング: 1998

    DataPickerを下からニュルっと!

    [iPhone] 下からニュッと出てくるDatePicker | BuGcloUd.com
    こちらの記事を参考に作りました。

    でも、DatePickerって出現すると10MBぐらいメモリ使用量があがるんですね。


    縦長のUIScrollViewをstoryboard上でデザインしたい!

    iPhone画面サイズを超えるViewをStoryBoardに配置する方法について - Google グループ
    こちらで議論されていますが、結果的にぼくも便乗して、
    StoryBoard&スクロールビューで画面サイズ調整 - Object for cutie
    この方法でやってみることにしました。

    つまり、

    「親となるViewControllerがあり、その中にContentSizeの高さが長いstoryboard上でデザインしたUIScrollViewを配置することができるようになる。」

    ということになります。

    たとえば、入力→確認という画面遷移を考えたときに、
    入力画面の項目が多ければ、460pxの中には収まりません。
    そうするとデザインが目で確認しながらではなく、プログラムから行う必要が出てきてしまうので、この方法で助けられました。

    ただし、親のUIViewControllerで、

    のようにする必要があるので、不必要なUIViewControllerのインスタンスが生成されているかもしれません。
    とはいえ、メモリを監視しているとそこまで上がらないので大丈夫だと思われます。


    RGBをUIColorとして使いたい

    これは僕が知らないだけかもしれませんが、XCode上の色を選ぶことができるColorsビューにて、
    Web Safe Colorsのところで指定したい16進を入力しても、ないみたいに言われて指定できないので
    プログラムから行うことにしました。

    16進数から10進数のRGBに変換は以下のサイトがいいです。
    もうここに助けられた!
    RGB変換 (16進数→10進数) (16進数→10進数)

    こんな感じで使います。


    UITextViewをタップしたい!

    覚え書き☆iPhoneプログラミング UITextViewでタップイベント
    こちらのとおり、カスタムなUITextViewなクラスを作って、touchesBeganに処理を書くようにしました。

    これはUITextViewをタップしたときにDatePickerを表示するときに使いました。
    ちなみに、DatePickerって表示すると10MB近くメモリが上昇するんですが、結構重いんですね。


    UITextFieldをタップしたときにキーボードの下に隠れないようにする

    iPhone SDKで、コンテンツがキーボードで隠れないようにする。 - このブログは証明できない。
    TextFieldにテキストを入力したとき画面の下が隠れてしまわないようにする方法 - ガジェット充まゆたまのiPhone開発知恵袋
    こちらの記事を参考にさせていただきました。

    そして最終的なコードが以下。

    とその前に、現在アクティブなUITextFieldを取得するために
    iPhone SDKで、アクティブな(フォーカスのある)UITextFieldを取得するには? - このブログは証明できない。
    こちらのコードも参考にさせていただいた。

    サンプルコード

    ■gist
    UITextFieldをタップしたときにキーボードの下に隠れないようにする -- Gist

    ちなみにですが、UITextViewの場合は、かってにフォーカス?するので、あえてここでUITextViewの表示位置にスクロールするということはしていないです。
    あと、いろいろ検索して見つけたコードを試してみたところ、上記のパターンじゃない方法(忘れちゃいました)で書かれたコードを使うと、UITextFieldをタップしてもアクティブにならない現象が発生しました。
    何度かタップするとアクティブになるんですが、その挙動がいやで、上記のパターンでは起こらなかったのでこちらを採用しております。


    UINavigationControllerを指定したView、または一番初めのViewに戻したい

    UINavigationControllerを2ページ目に戻す方法

    UINavigationControllerを最初のページに戻す方法

    [via]
    UINavigationControllerを使って、指定したビューまで戻ってメソッドも実行する|成長の果実

    もどると、ちゃんとdeallocも呼ばれます。


    複数開いたモーダルビューを一気に閉じたい!

    モーダルビューをすべて閉じる方法を教えてください « 寺子屋サルでき旧館 | iPhoneアプリ開発をもっともっと楽しく!困ったらみんなで解決!
    presentModalViewControllerで表示した2階層以上の画面から一気に戻る方法 - Faith and Brave - C++で遊ぼう

    これなかなか難しいですね。
    そもそもiOS5からなのか、viewWillAppear時に[self dismissModalViewControllerAnimated:YES];を呼んでも消えてくれなくて、
    viewDidAppearなら消えてくれます。

    なので、一気に消えるというよりは、パラパラと消えていくという感じになってしまいました。
    そこでぼくは、

    1. 親となる画面から1つ目の子画面を開く
    2. 1つ目の子画面から2つ目の子画面を開くんだが、その命令は親画面に移譲する
    3. 親画面が1つ目の画面を消して2つ目の子画面を開く
    4. 2つ目の子画面から1つ目の子画面に戻るときも同じように親画面に移譲する
    5. 2つ目の画面を閉じると1枚しか開いていないので、あたかも2枚閉じたように見える

    この挙動は、カメラを撮る→確認画面に行く→(ナビゲーション)→投稿画面に行くというInstagramの挙動と同じ感じかなーと思っています。

    親画面のサンプルコード

    1つ目の子画面のサンプルコード

    2つ目の子画面のサンプルコード

    こんな感じです。
    ここに行き着くまでに結構ハマりましたね。

    ※この方法は最終的に使いませんでした。
    とくにモーダルを開くシーンで使うのですが、メモリワーニングなイベントが起きた場合に親のViewControllerが破棄されると、2つ目の画面への遷移が親からなので、遷移できなくなるためです。
    なので、一気に2枚のViewを閉じる画面はなくしました。


    Could not find a storyboard named 'iPhone' in bundle NSBundleが出る場合

    なんでか不明ですが、storyboardがうまくロードされない場合がある。
    それは、誰かにプロジェクトを渡して、その人のXCodeでビルドしたときに起こりました。

    2012-08-03 13:29:13.804 HogeAPP[1403:1a303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'iPhone' in bundle NSBundle (loaded)'
    *** First throw call stack:
    (0x1d09022 0x1979cd6 0xf069f2 0xadbd60 0xadbff8 0xadb17f 0xaea183 0xaeac38 0xade634 0x26c3ef5 0x1cdd195 0x1c41ff2 0x1c408da 0x1c3fd84 0x1c3fc9b 0xadac65 0xadc626 0x24cd 0x2435)
    terminate called throwing an exception

    解決策は以下の通りで、いったんstoryboardの参照を削除して、Finderからもう一度XCode上にドラッグしてあげる。
    これでビルド時に上記エラーがでなくなりました。

    1. Right-click on your MainStoryboard.storyboard in the Project Navigator.

    2. Select Delete and click Remove References Only.

    3. Right click on InfoPlist.strings and select "Show In Finder". (This is just my lazy way to open the folder containing the storyboard in finder.)

    4. Drag the MainStoryboard.storyboard from Finder back into the project.

    via: ios - XCode 4.2 MainStoryBoard Not Found - Stack Overflow

    [via]
    ios - XCode 4.2 MainStoryBoard Not Found - Stack Overflow


    メモリリーク箇所を発見する、そして循環参照をなくす!

    これはそれはそれは大変な作業です。

    静的アナライザ

    メニューバーの「Product→Analyze」よりXCodeに発見してもらう。
    ただし、以下のようにあくまでも補助として使うほうがよさそうです。

    Clang Static Analyzer を使うとメモリリーク箇所を解析して教えてくれます。完璧じゃないので油断は禁物ですけど。ちなみに Xcode3.2 からは標準で Clang Static Analyzer が組み込まれています。Build > Build and Analyze で解析できます。


    via: iPhoneアプリ開発時のメモリ管理で気をつけること - A Day In The Life

    ■参考リンク
    iPhoneアプリ開発時のメモリ管理で気をつけること - A Day In The Life

    Instruments

    InstrumentsUserGuide.pdfを読みながらがんばっているが、やはりInstrumentsの使い方はなかなか難しい。

    特にActivitiMonitorのReal Memがどれぐらいリアルなのかが気になるw
    聞いた情報だと、開放されたメモリもここに表示されている?とかで、結構なメモリ量になっている。

    Instruments.png

    だいたいアプリ起動時は8MBほどで、いろいろなViewやコントロールを開くとどんどん上がっていく。
    このどんどん上がっていくViewControllerにdeallocのメソッドを書いて、本当に開放されているかをチェックしてみたら、これまた開放されていない箇所が盛りだくさんだった。

    また、ActivitiMonitorではなくAllocationsでメモリ状態を監視した場合の各列の意味は、
    Instruments の Leaks の見方(Live Bytes や Living の意味) - Over&Out その後
    こちらに詳しく書かれていました。

    Live Bytes



    The Live Bytes column indicates how many of this type of object have been allocated and still are around in memory.


    該当するタイプのオブジェクトの現在のメモリ使用量

    via: Instruments の Leaks の見方(Live Bytes や Living の意味) - Over&Out その後

    こちらが実メモリということなんですかね。
    でもActivitiMonitorでは起動時8MBぐらいだったのが、こちらでは1MBほどになっている。
    ここはまた別で調査しないと。

    循環参照なサンプルコード

    たとえば、UIViewController内で何かLogicを呼び出し、このコールバックとしてさらに何かを実行した場合、
    自分自身を表すselfをブロック内に渡したくなる。

    ただし、これだと

    • UIViewControllerがLogicの参照
    • Logicがselfを通じてUIViewControllerを参照

    という循環参照が発生してしまう。
    最悪、deallocが呼ばれずにメモリ開放されなくなってしまう。

    循環参照しないサンプルコード

    まるでJavaScriptのようなコードだが、これで循環参照を防ぐことができます。

    __weakで弱い参照状態にして、あくまでも参照カウンターは上げないのがポイントです。

    ブロック内にselfを渡したり、インスタンス変数を渡したりするとメモリが開放されないということを知るまでに結構な時間を費やしました。
    この作業を地道に行なっていったところ、目的のほぼすべてのViewControllerでdeallocが呼ばれ、Real Memも増えるけど下がるという状態にまでもって行けました。

    余談ですが、facebookもinstagramも50MBを超える量のメモリをガンガン使っているので、最終的にメモリが開放された時点で50MB以下になっていればいいのかなーなんて思いました。

    ■参考リンク
    メモリリークを調べたい - ちくわプログラマにっき
    Instruments の Leaks の見方(Live Bytes や Living の意味) - Over&Out その後
    deallocが呼ばれない - てきとーブログ


    InstagramやTweetbotのようなカスタムなタブにしたい!

    【iPhone】任意のデザインにタブバーをカスタマイズする - 坊やがゆく
    こちらの記事の、
    PoohKid/FreeDesignedTabBar2 · GitHub
    この方法がすばらしいです。

    このカスタムタブの方法も地味〜にあんまりネットになかったりするので、助かりました。
    すべてのタブを画像にするなど、本当に独自にやりたい場合にこれで行けます。


    同時タップによる多重遷移のエラーに対処する

    指2本で複数のオブジェクトを一緒にタップする場合になります。

    Modalを複数開いてしまうケースのエラー

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active

    UITableViewで複数のCellをタップした場合のエラー

    Unbalanced calls to begin/end appearance transitions

    回避サンプルコード

    かなり力技ではあるが、以下のようにフラグを設けて、すでにオープンしている場合はそれ以降の呼び出しを無効にする。

    今回はUITapGestureRecognizerを使って、画像たちにModalを開くイベントをセットしていたので、このような同時タップで
    イベント自体が2回走ってしまい、2回目のModalOpenでエラーになってしまう。

    こうゆうことにも配慮が必要なのだとすごく勉強になったエラーでした。


    ライブラリを追加したときにエラーになる場合

    以下のようなエラーが出る場合がある。
    これはライブラリをプロジェクトに追加したんだが、XCodeの「Target Membership」に追加していないときに発生する。

    Undefined symbols for architecture armv7:
    "_OBJC_CLASS_$_Stats", referenced from:
    objc-class-ref in AppDelegate.o
    ld: symbol(s) not found for architecture armv7
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    ライブラリの.mファイルを選択し、XCode上から追加してあげよう。
    ライブラリでなくても普通にViewControllerのクラスを追加したときに、別のターゲットにチェックを付けていないと同じ現象は発生する。


    リジェクトされたくないので一読

    iPhoneアプリ審査での111の禁止項目(意訳) | fladdict


    各種アイコンのサイズ

    [iOS] アプリに必要なアイコン・起動画面の画像サイズ&ファイル名 | tande lab.

    使ったライブラリ

    R9HTTPRequest

    HTTP通信を簡単にしてくれたライブラリで、国産ということもあり、すごく馴染みやすかったです。
    非同期でブロックをコールバックに渡すスタイルなので、JavaScriptっぽくて使ってみました。

    iPhoneアプリ開発がはじめてだったので、このライブラリにはほんと助けられました。

    一点、画像をPostする際にメモリリークしている箇所があったので、nilを入れるように修正しました。

    pull requestした。
    Pull Request #1: 画像Post時のメモリリーク解消 by hisasann · glassonion1/R9HTTPRequest

    ■ダウンロード
    glassonion1/R9HTTPRequest

    ■解説
    シンプルで簡単に HTTP 通信が出来るライブラリを公開しました - A Day In The Life

    この後、結局のところ通信のタイムアウトがR9HTTPRequestだと240秒以下にできない問題があったので、ASIHTTPRequestに乗り換えました。
    どうもこれがデファクト?っぽい感じだったので。
    あかばね式 [iPhone]POSTする場合のタイムアウト設定値が無視される(場合がある)件

    R9HTTPRequestとほぼ同じようなコードなので、乗り換えはかなり簡単でした。
    ただしASIHTTPRequestはARCに対応していないので、
    ARCを有効にしたプロジェクトでASIHTTPRequestを使用する|成長の果実
    こちらで紹介されている、「-fno-objc-arc」を記述する必要があります。

    もう開発が止まっているライブラリですが、これはなかなかよいです。

    ■リンク
    ASIHTTPRequest Documentation - All-Seeing Interactive

    ECSlidingViewController

    PathなUIを実現する必要があって、でも自分で一からは厳しいなと思っていて、
    Facebookアプリを真似たメニュー機能を実現「ECSlidingViewController」 - MOONGIFT|オープンソース・ソフトウェア紹介を軸としたITエンジニア、Webデザイナー向けブログ
    こちらの記事を読んで採用してみました。

    実際には、導入もすごく楽で、スムーズだし、これもまたすばらしいライブラリでした。
    おそらくPathライクなUI用のライブラリとしては、だんとつ人気なんじゃないでしょうか

    ■ダウンロード
    edgecase/ECSlidingViewController

    MBProgressHUD

    比較的よく使われているLoadingレイヤーを出すライブラリです。
    なかなか海外でも評価が高いので、採用してみたのですが、少し使い方に注意が必要でした。

    Demoのコードを見てみると、以下の様にバンバンaddSubviewしているんですが、

    これはviewを渡しているかなのかわかりませんが、dealloc後にも開放されずに残ってしまうようでした。
    そして、Demoをよく見てみたら、こんなコードをimplementしていて、ここでremoveしていました。

    ただこのコードはLoadingの表示が終わったら呼ばれるようなので、それだとLoadingを出すたびにインスタンスが作られるので、

    ぼくは、viewWillAppearで追加して、viewWillDisappearでremoveするようにしました。
    これでメモリは開放されます。

    これでLoadingを出すときにメッセージも渡しつつshowする感じになります。

    ■ダウンロード
    jdg/MBProgressHUD

    SBJson

    Objective-Cではこれ!というぐらい調べていくとこのライブラリになりました。
    JSON用のライブラリです。

    とくに問題なく使えました。

    ■ダウンロード
    SBJson

    Underscore

    まさかのUnderscore.jsのUnderscore.m版がありました。
    でもwhithoutしか使いませんでしたw

    ■ダウンロード
    Underscore.m

    [via]
    iPhoneアプリ開発で使えるObjective-Cライブラリの紹介 | SmartAppsCreative

    このUnderscoreの面白いところは、[obj hoge]のように本来ならブラケットで囲われるのが普通だが、丸括弧で呼び出す方法を提供している。
    それの解説ではないが、小さくまとめたものをgithubのほうに上げておきました。

    ■github
    hisasann/Objective-CnoSquareBracketMethodCall


    スマホアプリのデザインをいっぱい見たい!

    Recent / iOS UI Patterns (beta)
    もうここのサイトが本当に参考になります。
    比較的有名なアプリの画面が、画面のジャンルごとにわかれているのでとっても見やすい。


    読み物

    iPhoneアプリで食べていく――「ぐんまのやぼう」ができるまで (1/3) - ITmedia ニュース


    まとめ

    今回、あまりまわりに聞ける人がいない環境で、頼れるのはグーグルさんだけだったのですが、けっこうたいへんでした。
    とくにググっても有力な情報が出てこないばかりか、XCodeが古い時代の記事はそもそも間違っていたりと玉石混交過ぎました。

    なので、とにかく実験、実験を重ねて、なんとかつかむことができましたが。

    ほんと、こうゆうハマる作業が好きなのはプログラマーゆえですよね。

    ハマってなんぼ!

    どうもありがとうございました!


    今回読んだ本

    よくわかるiPhoneアプリ開発の教科書【iOS 5&Xcode 4.2対応版】
    森巧尚
    マイナビ
    売り上げランキング: 1615

    この本はほんと初歩の初歩ですが、それすらも分からなかったので、まずはこれをざっくりやってstoryboardの感触を掴みました。

    詳解 Objective-C 2.0 第3版
    荻原 剛志
    ソフトバンククリエイティブ
    売り上げランキング: 2386

    この本はすごい!
    けど、なかなかなハードルであります。気長に読んでいこうかと。

    Trackback:0

    TrackBack URL for this entry
    http://hisasann.com/cgi-bin/mt/mt-tb.cgi/1292
    Listed below are links to weblogs that reference
    はじめてのiPhoneアプリ - カメラアプリを作る際にハマったことのメモ from HouseTect, JavaScriptな情報をあなたに

    Home > 技術 > はじめてのiPhoneアプリ - カメラアプリを作る際にハマったことのメモ

    Tag cloud
    月別アーカイブ
    Powered by
    Powered by
    Movable Type Commercial 4.261

    Page Top