スレッドを作るときの注意点
__NSAutoreleaseNoPool(): Object of 0x???? class UITableView autoreleased with no pool in place - just leaking
というエラーが出たので、いろいろ調べてみると、スレッドを立ち上げた時にAutoReleasePoolを作る必要があるらしい。
-(void)threadStartAnimating:(id)data
{
[self.view bringSubviewToFront:_indicator];
[_indicator setHidden:NO];
[_indicator startAnimating];
}
-(void)threadStopAnimating:(id)data
{
[_indicator stopAnimating];
[_indicator setHidden:YES];
[self.view sendSubviewToBack:_indicator];
}
-(void)search{
//スピナーを表示する
[NSThread detachNewThreadSelector:@selector(threadStartAnimating:) toTarget:self withObject:nil];
....処理....
//スピナーを止める
[NSThread detachNewThreadSelector:@selector(threadStopAnimating:) toTarget:self withObject:nil];
}
これを実行したらエラーが出たので、以下のように書き換えた。
-(void)threadStartAnimating:(id)data
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[self.view bringSubviewToFront:_indicator];
[_indicator setHidden:NO];
[_indicator startAnimating];
[pool release];
[NSThread exit];
}
-(void)threadStopAnimating:(id)data
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[_indicator stopAnimating];
[_indicator setHidden:YES];
[self.view sendSubviewToBack:_indicator];
[pool release];
[NSThread exit];
}
-(void)search{
//スピナーを表示する
[NSThread detachNewThreadSelector:@selector(threadStartAnimating:) toTarget:self withObject:nil];
....処理....
//スピナーを止める
[NSThread detachNewThreadSelector:@selector(threadStopAnimating:) toTarget:self withObject:nil];
}
このようにNSAutoreleasePoolを作る必要があるらしい。
あと[NSThread exit];もする必要があるのかな?
Iphoneアプリの公開③アプリのアップロード
前回に続いて。
●Application Loader関連(Xcode4になって必要なくなったらしい。でも間違えてやってしまったので一応残しておく。)
◯登録したアプリの画面
「Ready to Upload Binary」を選択
◯暗号化についての質問
「No」にする→「Continue」
◯Application Loaderをダウンロード
iTunes Connectのフッター部分(下ら辺)にDownload Application Loader という部分があるのでそこでdmgをダウンロードする
◯Application Loaderのインストール
指示に従ってインストールする
◯Application Loaderの起動
/Developer/Applications/Utilities/Application Loader.appにアプリがあるのでターミナルから起動する。ユーティリティ>ターミナルで起動して、
open /Developer/Applications/Utilities/Application\ Loader.app
とすると、起動する。
●証明書の発行
◯Utili>キーチェーンアクセスというアプリ
・一番上にあるメニューから「キーチェーンアクセス>証明書アシスタント>認証局に証明書を要求」
・Apple IDのメールアドレスと自分の英字の名前を入力(CAのメールアドレスは空欄)
・「ディスクに保存」を選ぶ
・「鍵ペア情報を指定」を選ぶ
・任意の場所に保存する
◯iOS Provisioning Portalへ行く
◯Provisioning Portal>Certificate>Distribution
・Request Certificateを選ぶ
◯「Create iOS Distribution Certificate」
・先程保存した証明書要求をアップロードする
◯戻ってきたページでしばらくしてリロードするとダウンロードできるようになっている
・追加された証明書のダウンロード。
・下の方のWWDR intermediate certificateもダウンロードしておく
・ダブルクリックで開いて追加される(キーチェーンのログインと言う所で追加されたか確認できる)
●Provisioning Profilesの取得
◯Provisioning Portal>Provisioning>Distribution
・「New Profile」を選ぶ
・Distribution Method(App Store)
・Profile Name(自由な名前・・・と言われてもわからなかったのだがとりあえず、「アプリ名+profile」とかにしてみた)
・App ID(登録されているAppIDから選択。登録されてなければ左のApp IDsから登録する必要がある)
・「Submit」を選択
◯戻ってきたページでしばらくしてリロードするとダウンロードできるようになっている
・ダウンロード
・ダブルクリックするとXcodeのOrganizerが立ち上がってインストールできた
(Xcodeのメニュー>window>organizerで起動して、Provisioning Profileの部分にドラッグもできるらしい)
◯Xcode上で
・RunやStopボタンの右にあるschimeのプルダウンメニューからiOS devicesを選択する。
・Product>Archiveでビルドする
・Build Succeedと表示されたらOrganizerが起動する。
・Validateを選択し、ログインする。
・Nextを押して成功したらFinish
・Distributeを選択し、同様に進める。アップロードが始まる。
Iphoneアプリの公開② iTunes Connectに登録
前回に続いてアプリを登録するためのiTunes Connectへ行く
https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/
◯サインインする
・ところができない。
Apple ID does not have permission to access iTunes Connect.
と表示されるので、サポートから連絡。夜に登録して朝方9〜10時くらい?に返事があった。
◯サインインする。
・今度はサインインできる
◯アプリを登録するため、「Add new app」を選ぶ
You have no eligible Bundle IDs for iOS apps. Register one here.
と表示されるのでhereを選び(https://developer.apple.com/ios/manage/bundles/index.actionへ飛ぶ)AppIDを登録する。
◯「Create App ID」
Bundle Identifier (App ID Suffix)にドメイン名を逆さまにしたものを入力する。たとえば私はrupy.jpというドメインを持っているのでjp.rupy.appnameのような感じ。
◯再度アプリを登録するために「Add new app」を選ぶ
◯「App Information」で
・Default Language(後々のことを考えるとEnglishを選択するべきらしいが、japaneseにした。必ず英語版をリリースする必要があるという記事るを見かけたので。)
・App Name(マーケットに表示されるアプリの名前)
・SKU Number(ここのページで文字列の決め方が解説してあったのでそれに習って[社名略称][連番][アプリ略称]で記入)
・Bundle ID(さっき登録したものを選ぶ)
→「Continue」を押す
◯続けて入力
・Availability Date(とりあえずそのままで)
・Price Tier(値段を決める。とりあえず無料なのでFreeを選択。View Pricing Matrixで、各国の値段表を見て、値段を選ぶことができる)
・Discount for Educational Institutions(学割?とりあえずチェックをつけたままにしてみた)
・Custom B2B App(特殊な場合らしいのでチェックしないでおいた)
◯「Enter the following information 」
・アプリの情報を入力
・「Rating」では暴力的だったり性的な表現を含むアプリの場合チェックするようです。
・スクリーンショットなども登録(Retinaディスプレイ対応の解像度の画像が必要らしい)
(シュミレータでoption+ctrl+cで撮影。クリップボードにコピーされる)
◯Doneを選択し、完了
Iphoneアプリの公開①iOSデベロッパへ登録する
最近初めてIphoneアプリを公開するという状況になったのでIphoneアプリの公開の流れをメモしておきます。
①iOSデベロッパに登録(年間参加費 ¥8,400)
https://developer.apple.com/jp/programs/ios/
・「今すぐ登録」を選ぶ
◯「Enrolling in Apple Developer Programs」のページ
・「登録に進む」を選ぶ
◯「新規に登録しますか、それとも既にAppleデベロッパとして登録済みですか?」というページ
・AppleIDを持っているので「Appleデベロッパプログラムに登録するにあたり現在持っているApple IDを使用します。」を選んで「次へ」
◯「Are you enrolling as an individual or company?」のページ
・個人か法人かを聞いているので個人「individual」を選ぶ
◯ログインページ
・ログインする
◯「Enter your billing information for identity verification」
・個人情報を入力
◯「Select Your Program」
・「iOS Developer Program」を選択
◯「Review your enrollment information & submit.」
・情報を確認して「Continue」
◯ライセンスのページ
・チェックボックスにチェックを入れて「I agree」
◯「Proceed to your country’s Apple Online Store to purchase.」
「add to cart」を選んで「iOS Developer Program」をカートに入れる。以降は普通のApple商品として扱われて購入できる。
◯購入
・購入すると確認メールが2通届く(「ご注文内容の確認」「ご注文成立のお知らせ」)
◯アクティベーション
・「Apple Developer Program Activation Code」というメールが届くのでメールのアクティベーションコードを選択する。
◯エラー
We are unable to activate your Apple Developer Program membership.
We are unable to activate your Apple Developer Program membership because we are unable to successfully verify your identity. Please contact us and reference Enrollment ID ****** for further assistance.
というエラーが出たのでAppleのデベロッパのページのContact usからエラーについて英語で書いて送信した。返事はなぜか英語の文章と日本語の文章が来た。再度アクティベーションコードをクリックしてくださいとのことなのでクリックしてアクティベートされる
◯アクティベートされた
・これで完了
NSStringのisEqualとisEqualToStringの違い
文字列と文字列が等しいかどうかを判定するためにNSStringのisEqualとisEqualToStringがあるが、その違いを調べてみた。
http://stackoverflow.com/questions/1292862/nsstring-isequal-vs-isequaltostring
ここにあるようにisEqualはオブジェクトとして比較し、isEqualToStringは文字列として比較するので、2つのオブジェクトが文字列であるとわかっている場合には後者を使用する方が速度は早い、ということらしい。
特定の要素にスクロールするJavaScript
前回に続き、検索結果でハイライトした部分の要素にスクロールできるようにJavaScriptを書いてみた。
/*クラスネームclassnameを持つ要素のうちindex番目にスクロールする関数*/
function ScrollByClass(classname,index){
var elements = document.getElementsByClassName(classname);
if(elements.length ==0){
return;//一つも見つからなかったらおしまい。
}
if(index > elements.length){
index %= elements.length;//要素数以上のindexであれば要素数以内にする。
}
var top = elements[index].offsetTop - window.pageYOffset;
elements[index].style.backgroundColor="red";//背景色を赤く変更
window.scrollBy(0,top);//スクロール
}
/*上の関数で背景色を変更してしまうので戻すことができるように用意*/
function ChangeBackgroundColorOfClass(classname,index,color){
var elements = document.getElementsByClassName(classname);
elements[index].style.backgroundColor=color;
}
引数に与えるindexを1つずつ増加させることで次、次とスクロールできるようになった。
これを前回のコードと組み合わせることで、IphoneのUIWebViewでスクロールすることができる。
UIWebViewで文字列を検索し、該当する部分をハイライトする
UIWebViewを使って文字列をハイライトさせたいと思い色々と検索していたら、とてもいい記事を見つけたので紹介します。
http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/
全文英語ですごく長いのですが、比較的わかりやすい英語で、わからなければソースコードを見ればわかるという感じです。
どうやらJavaScriptで実現しているようですが、単にコピー&ペーストで動きました。でも、ちゃんと理解したいと思ったので1つずつ見ていくことにします。
ここではいくつかのファイルがあります。
- SearchWebView.js
- SearchWebView.h
- SearchWebView.m
このファイルをXcodeに追加するだけで利用可能です。
一つ一つのファイルの役割を見ていきます。SearchWebView.jsは実際にHTMLソースを書き換えるJavaScriptコードです。文字列を検索しハイライトを施したり、そのハイライトを消したりしています。
SearchWebView.hとSearchWebView.mはObjective-Cのコードで、UIWebViewに新しくカテゴリとしてメソッドを追加してJavaScriptをObjective-Cプログラムから実行できるようにしています。
まずSearchWebView.hを見ると、UIWebViewにカテゴリを定義しています。SearchWebView.mでカテゴリのメソッドを定義しています。highlightAllOccurencesOfStringメソッドでは
- JavaScriptの読み込み
- JavaScriptファイルを実行する
- グローバル変数を介して結果を受け取る
- 結果を返却
という流れですかね。
今度はSearchWebView.jsを見るとMyApp_HighlightAllOccurencesOfStringというメソッドがあり、中でMyApp_HighlightAllOccurencesOfStringForElementを呼び出しています。
MyApp_HighlightAllOccurencesOfStringForElementを見ると大きく
if (element.nodeType == 3) {
...
} else if (element.nodeType == 1) {
...
}
とありますがテキストノードが見つかった時にifが実行されて、新たな要素が見つかった場合にはelse ifの部分が実行されます。3はNode.TEXT_NODE、1はNode.ELEMENT_NODEを表します。
要素が見つかった場合にはその子供のノード全てに再帰的にMyApp_HighlightAllOccurencesOfStringForElementを実行します。
テキストノードが見つかった場合見つからなくなるまで文字列を検索して、その部分の背景色を黄色にします。
while (true) {
/*テキストノード内の文字列の検索*/
var value = element.nodeValue;//テキストノード内の文字列の取得
var idx = value.toLowerCase().indexOf(keyword);//すべて小文字にして検索。idxに見つかった位置が入る
if (idx < 0) break; // 見つからなかったので中止
/*span要素を作る*/
var span = document.createElement("span");//span要素の生成
var text = document.createTextNode(value.substr(idx,keyword.length));//見つかった文字列でできたテキストノード
//span要素の中身の設定
span.appendChild(text);//span要素に入れる
span.setAttribute("class","MyAppHighlight");//class="MyAppHighlight"
span.style.backgroundColor="yellow";
span.style.color="black";
/*ノードを並べる*/
text = document.createTextNode(value.substr(idx+keyword.length));//対象の文字が見つかった場所以降を取り出す
element.deleteData(idx, value.length - idx);//見つかった場所以降は消してしまう。
var next = element.nextSibling;//今見ているテキストノードの次のノードを得る
element.parentNode.insertBefore(span, next);//テキストノードの最後にspan要素を入れる
element.parentNode.insertBefore(text, next);//残りのテキストを加える
element = text;//残りの部分に移る
MyApp_SearchResultCount++;
}
という感じ。とても勉強になった。これを応用すればいろいろできそうな気がする。
pathForResource:メソッドでJavaScriptファイルが読み込めない
JavaScriptを読み込むときに
NSString *path = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"js"];
として実行したが、どうしてかnilが返される。
いろいろ調べてみると、どうやらJavaScriptコードはコンパイル対象になっていて、Bundleに含まれていないらしい。
Xcode4では少し仕様が以前と変わっているようだが、プロジェクトファイルを選択してTARGETSの部分を選ぶと以下のような画面が出るので、Build Phases を選択してCopy Bundle Resourcesにファイルを追加すれば良い。
これでファイルのパスが返るようになった。
Xcodeでの改行コード
どうやらXcodeでは¥n(円マーク+n)と\n(バックスラッシュ+n)は異なるらしいので、普通に円マークを追加したら改行されない。
Optionキー+¥でバックスラッシュを入力できる。
うちみたいにWindowsキーボードを使っている場合はAltキー+¥だった。






