Action Extensionをデバッグする

新しいiPhoneやiOS8のリリースも正式にアナウンスされドタバタしています。

iOS8の新機能、Extension。
例えば「写真」アプリで写真を選択し、アクションボタンをタップすると
iOS Simulator Screen Shot 2014.09.12 8.43.47-01このようにTwitterやFacebookに投稿したり、印刷したりする機能が現れます。
iOS8ではアプリケーションが提供するExtensionを追加出来るようになりました。
例えばiOSが提供している以外のSNSに投稿するShare Extensionを追加したり、画像にフィルターを掛けるAction Extensionを提供したり出来るようになりました。

さて、このExtension、Appleのアナウンスではホスト・アプリを選んでXcodeで起動させればソースコードレベルでデバッグ出来る事になっているのですが、私がドキュメントをちゃんと読みこなせていないのか、なかなかうまくデバッグする事が出来ません。

そこで強引にデバッグする手法です。

まず、Extensionが稼働しているプロセスを特定するため、PIDを取得します。
viewDidLoadの最初にでも下記のようなコードを埋め込みます。

- (void)viewDidLoad {
    [super viewDidLoad];

#ifdef DEBUG
    pid_t pid;
    pid = getpid();
    NSLog(@"%s : pid = %ld", __func__, (long)pid);
#endif

次にXcodeからExtensionを実行します。
スクリーンショット 2014-09-12 9.09.04
するとホストアプリをどれにするか聞いてきます。
スクリーンショット 2014-09-12 9.09.20

UIActivityViewControllerを使ってAction Extensionを起動出来るアプリを選んでやります。

次にコンソールのメッセージを見るためにXcodeのWindowメニューからDeviceを選びます。

先ほどでバッグ用に起動したデバイスで目的のExtensionをタップし、Active Extensionを起動します。

スクリーンショット 2014-09-12 9.19.30のコピー
コンソールには先ほどviewDidLoadに仕込んだメッセージが表示され、Prosess IDが 4924 だと判明しました。

“Debugメニュー” -> “Attache To Process” -> “By Process ID(Identifier) or Name..”
で先ほどのPIDを指定して目的のプロセスにAttacheします。

スクリーンショット 2014-09-12 9.27.34

以上でブレークポイントを掛けてデバッグ出来るようになります。

スクリーンショット 2014-09-12 9.36.41のコピー

 

ただ、この方法ではExtensionが起動した直後からデバッグを行う事は出来ません。
今回の事で分かったのは、Action Extensionはホスト・アプリの中で動いているのではなく、別プロセスとして立ち上げられていると言う事です。
これによるメリットはホスト・アプリが非常にメモリーを使う物であっても、それに影響されずメモリーなどの資源を使う事が出来ると言う事です。またAction Extensionにバグがあり、強制終了させられた場合でもホスト・アプリの動作には影響が無いのでしょう。

Swiftで試行錯誤④ #defineが無い!

Swiftには#defineがありません。
#ifdef 〜 #else 〜 #endifも#if 〜 #else 〜 #endif に置き換わっています。

何が問題かと言うと、例えば Precompiled Header で下記のように記述する事が出来ません。

#ifdef DEBUG

#else

#endif

まぁSwiftではヘッダファイルという概念が無いのですからしょうがないと言えばしょうがないのですが。

またこの DEBUG も今まではBuild Configuration をDebugにすればObjective-Cのコンパイルオプションとして -DDEBUG=1 が付くのですが、Swiftのコンパイルではこれは意味が無いようです。

しかしこれはBuild Settingで下記のように”Swift Compiler – Custom Flags”に “Other Swift Flags” に追加すれば解決します(リリース時にはデフォルトで設定しておいてもらいたいですね)。

スクリーンショット 2014-06-26 17.01.43

では例えばデバッグ時にメッセージを出力して出荷時にはそれを自動的に消すには?

Swiftではクラスに属さない「関数」を記述する事も出来るようです。
例えば “GlobalFunction.swift” というようなソースにデバッグ用の関数を定義しておけば、Swiftはヘッダファイルを必要としていないため、自動的に全てのコードの中で参照出来るようになります。

#ifdefの中で#defineとしてマクロを組むのとは違って、リリース版にも何もしないLOG()が呼び出されてしまいますが、無いよりは良いですね。

もしかしたらもっと良い方法があるかもしれませんが…

import Foundation

#if DEBUG

    func LOG(msgs:Any) {
        println("LOG: \(msgs)")
    }

#else

    func LOG(msgs:Any) {
    }

#endif

Swiftで試行錯誤③ Objective-CのクラスをSwiftで使ってみる

CoreFoundationを含めてiOSやOSXのAPIのほとんどはNSObjectのサブクラスとして提供されています。そのため新しい画面をSwiftで追加する場合にはUIViewControllerのサブクラスを作るしかありません。UIViewも同じですね。
ですのでどうしてもNSObjectとは切っても切れません。言い換えればObjective-Cのクラスとは切り離せません。

とはいえ前回のコラムでも書きましたがSwiftは親クラスを持たないクラスを作成する事が出来ますので、プログラミング言語と実行環境(Framework)が分離出来たと言えると思います。その事はiOSやOSX以外の環境でももしかしたらSwiftが主流の開発言語になる可能性も否定出来ないって事だと思います。

さて、Objective-CのクラスをSwiftで使う方法です。
標準のFrameworkを使うにはimport文でフレームワークを指定してやれば良いだけです。

import UIKit
import CoreData

しかし自前のFrameworkやクラスをSwiftで使うためには最初のコラムで紹介しましたように<#ProductModuleName#>-Bridging-Header.h に #import 文を追加してやる必要があります。
このファイルはObjective-CのプロジェクトにSwiftのソースを追加してやる時に自動的に作成されます。
Objective-CのクラスをSwiftで使うための前準備はこれだけです。

インスタンス化

しかしSwiftとObjective-Cではクラスのインスタンス化の方法が違うので注意する必要があります。
Swiftではalloc やinit メソッドを呼び出す必要はありません。

    var obj:ObjectiveC-Class!
    obj = ObjectiveC-Class()

これだけです。
これだけでObjective-Cのクラスのallocやinitを自動的に呼び出してくれます。
しかしクラスによってはinitメソッドに引数を持たせているものもあります。標準Frameworkではそのようなinitメソッドは下記のようなフォーマットになっています。

    -(id)initWithParam:(classname)parameter;

WithParam が付いています。
Objective-CのこのようなinitメソッドをSwiftから呼び出す場合には、withのあとの文字列をパラメータ名としたクラス・メソッドに置き換えてくれます。

    var obj:ObjectiveC-Class!
    obj = ObjectiveC-Class(param:parameter)

iOSでViewを作るような場合で比較してみます。

    CGRect viewRect = CGRectMake(10, 10, 100, 100);
    UIView* myView = [[UIView alloc] initWithFrame:viewRect];
    let viewRect = CGRect(x: 10, y: 10, width: 100, height: 100)
    let myView = UIView(frame: viewRect)

これは自分で作ったクラスがWith〜が付いたinitメソッドを持っていても、自動的に変換されます。

いきなり出てきたSwiftですので、今はやはり取っ付きにくい部分も多くあります。ですがかなり柔軟な開発言語だと思います。使いこなせるようになれば便利じゃないかと感じています。swiftは現在のところXcode 6.0 betaでしか使えません。beta版のXcodeでは製品をAppStoreに上げる事は出来ませんが、古いiOSに対応したアプリも作成できるようですので、是非習得しておきたいと思います。

Swiftで試行錯誤② SwiftのクラスをObjective-Cで使ってみる

前回はNSObjectから派生したSwiftのクラスをObjective-Cで使うための方法を紹介しました。
Swiftは親クラスを持たないクラスを作る事も出来ます。その場合には気をつけなければいけない事があります。

@objc(<#name#>)

まず、クラスの宣言の前に@objc(<#name#>)を付けて、Objective-Cから<#name#>というクラス名で使用出来るようにしなければなりません。マニュアルによると@objc(<#name#>)はSwiftで宣言したのとは違うクラス名でアクセス出来るようにも出来ると書かれているのですが実際に試してみると上手くいきません。とりあえず問題はありませんのでクラス名にします。

alloc, init

Objective-Cでクラスを扱う際には全てのクラスはNSObjectのサブクラスになります。従ってインスタンス化する時には

以下のようにしていました。

    OneClass* obj;
    obj = [OneClass alloc];
    obj = [obj init];

しかしこのような方法でNSObjectを親クラスに持たないクラスをインスタンス化する事は出来ません。なぜならallocとinitはどちらもNSObjectクラスのメソッドだからです。

ではどうするか?

Swiftのクラスにクラス・メソッドを作ってやり、その中でクラスのインスタンスを返してやります。

import Foundation

@objc(SwiftClass02)
class SwiftClass02 {
    class func create() -> SwiftClass02 {
        return SwiftClass02()
    }
}
#import "ObjectiveC-Class.h"
#import "Swift03-Swift.h"

@implementation ObjectiveC_Class

-(void)method {
    SwiftClass02* swiftClass02;
    swiftClass02 = [SwiftClass02 create];
}
@end

 

 

 

次はSwiftの中でObjective-Cのクラスを使う方法を考えてみます。

Swiftで試行錯誤① SwiftのクラスをObjective-Cで使ってみる。

WWDC 2014で現れた新しいプログラミング言語のSwift。正直なところ、こんなのが出てくるとは思いませんでした。
特徴については様々ところで書かれていますので紹介しません。
とりあえずどんなモンか試していきます。

全く新しいアプリを書くなら全てSwiftで書くという事も可能でしょうが、バージョンアップの場合に全てを一からSwiftで書き直すと言うのはシンドイ話です。
徐々にSwiftにシフトしていく事を前提に、新しいSwiftのクラスをObjective-Cで使う事を試してみます。

まず新しくSwiftで書かれたクラスを追加します。
Swiftのクラスを作成するにはLanguageをSwiftにしてやるだけで作成されます。

Screen1

最初にSwiftのクラスを作成するとこのようにBridge-Headerを作成して設定を変更するかどうかを聞いてきますので迷わず「Yes」をクリックします。

Screen2

すると”ProductModuleName-Bridging-Header.h”と言うファイルが作成され、プロジェクトに追加されます。またBuild Settingの中の”Objective-C Bridging Header”に追加されます。これはSwiftの中でObjective-Cのクラスを使用する場合に必要になるもので、今回は説明を省きます。

Screen3

必要なのは”ProductModuleName-Swift.h” というファイルです。マニュアルにはこのファイルをObjective-Cのコードの先頭で#importしろと書かれているのですが、面倒なのはこのファイルはプロジェクトの中には現れません。ビルドを行わなければ(クリアなどをしても)ファイル自体が存在しないようです(多分)。

ですので構わずObjective-Cのコードの先頭に追加してしまいます。”ProductModuleName”は各プロジェクトの名前ですので必要に応じて置き換えてください。

この方法で、NSObjectをルートクラスとするようなクラスは使えるようになります。

新しくSwiftのクラスを追加したりメソッドやプロパティを追加削除したり、クラスを削除した場合にはXcodeがこのヘッダファイルを自動的に編集してくれるようです。勝手に編集されないようにファイルを見えないようにしているのでしょう。またSwiftのクラスを編集するとアップデートするまで若干時間がかかるようで、すぐにObjective-Cの方で新しいメソッドを参照しようとするとエラーが出ますが、ビルドすればすぐに解決するようです。

import Foundation

class SwiftClass01: NSObject {

    func method01() {
        println("method01 has benn called");
    }
}
#import "ObjectiveC-Class.h"
#import "Swift03-Swift.h"

@implementation ObjectiveC_Class

-(void)method {
    SwiftClass01* swiftClass01;

    swiftClass01 = [SwiftClass01 alloc];
    swiftClass01 = [swiftClass01 init];

    [swiftClass01 method01];
}
@end

しかしSwiftは親クラスを持たないクラスを作成する事も出来ます。その場合にはちょっと変わってきます。

それはまた次に。

iOS 7.1からEnterprise DistributionにHTTPSが必要になりました

iOS7.1からEnterprise Distributionを行うにはHTTPSを使う必要が出てきました。 開発中のアプリケーションを実機でテストする場合にもとても便利な機能なので残念です。今までは手元にあるWindows XPのIISを手軽に使っていたのですが、私自身IISの深い知識は無いのでどのようにHTTPSを稼働させるのか良くわかりません。そのため、Mervericsに標準でインストールされているapatchでHTTPSサーバを立てる事にしました。
以下はその手順です。

まずはスーパーユーザーになり以下の手順で公開鍵などを作成してきます。

1. 秘密鍵の作成
パスフレーズの入力を求められますが、これは忘れないようにして下さい。その後の処理にも必要になります。

root# openssl genrsa -out server.key -aes128 1024
Generating RSA private key, 1024 bit long modulus
......................++++++
......++++++
e is 65537 (0x10001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:

2. 秘密鍵でCSRを作成
Common Nameは本来はドメイン名を使いますが、ここではDNSで管理していないマシンを使うため、IPアドレスをそのまま指定しています。

root# openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Saitama
Locality Name (eg, city) []:Saitama
Organization Name (eg, company) [Internet Widgits Pty Ltd]:FuturesVision
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:192.168.11.3
Email Address []:xxx@futuresvision.net

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

3. 証明書の認証要求の作成
毎年更新するのも面倒ですので、この例では有効期限を約100年にしました。

root# openssl x509 -in server.cer -days 36500 -req -signkey server.key -out server.crt
Signature ok
subject=/C=JP/ST=Saitama/L=Saitama/O=FuturesVision/CN=192.168.11.3/emailAddress=xxx@futuresvision.net
Getting Private key
Enter pass phrase for server.key:

4. apatchの設定
/private/etc/apache2/httpd.conf を編集し、HTTPSを有効にします。490行目のコメントを外します。

# Various default settings
#Include /private/etc/apache2/extra/httpd-default.conf

# Secure (SSL/TLS) connections
Include /private/etc/apache2/extra/httpd-ssl.conf
#
# Note: The following must must be present to support

/private/etc/apache2/extra/httpd-ssl.confを編集し、先ほど作った証明書を読み込むなのどの設定をします。設定が必要なのは以下の項目です。

  • ServerName
  • SSLCertificateFile
  • SSLCertificateKeyFile

ServerNameはCSRの作成時に指定したCommon Nameと同じにします。

#   General setup for the virtual host
 DocumentRoot "/Library/WebServer/Documents"
 ServerName <span style="color: #ff0000;">192.168.11.3:443</span>
 ServerAdmin <span style="color: #ff0000;">xxx</span>
 ErrorLog "/private/var/log/apache2/error_log"
 TransferLog "/private/var/log/apache2/access_log"

 
下記の設定でもわかりますように、先ほど作成したファイルは /private/etc/apache2/config/ の下に置いてあります。

#   Server Certificate:
#   Point SSLCertificateFile at a PEM encoded certificate.  If
#   the certificate is encrypted, then you will be prompted for a
#   pass phrase.  Note that a kill -HUP will prompt again.  Keep
#   in mind that if you have both an RSA and a DSA certificate you
#   can configure both in parallel (to also allow the use of DSA
#   ciphers, etc.)
<span style="color: #ff0000;">SSLCertificateFile "/private/etc/apache2/config/server.crt"</span>
#SSLCertificateFile "/private/etc/apache2/server-dsa.crt"

#   Server Private Key:
#   If the key is not combined with the certificate, use this
#   directive to point at the key file.  Keep in mind that if
#   you've both a RSA and a DSA private key you can configure
#   both in parallel (to also allow the use of DSA ciphers, etc.)
<span style="color: #ff0000;">SSLCertificateKeyFile "/private/etc/apache2/config/server.key"</span>
#SSLCertificateKeyFile "/private/etc/apache2/server-dsa.key"

5. apatchを起動します
起動時にはパスフェーズの入力が必要です。

oot# /usr/sbin/httpd -k restart
httpd: Could not reliably determine the server's fully qualified domain name, using MacBook-Air.local for ServerName
httpd not running, trying to start
Apache/2.2.26 mod_ssl/2.2.26 (Pass Phrase Dialog)
Some of your private key files are encrypted for security reasons.
In order to read them you have to provide the pass phrases.

Server 192.168.11.3:443 (RSA)
Enter pass phrase:

6. 証明書をiOSデバイスにインストール
iPhone構成ユーティリティを起動します。
「構成プロファイル」を選び、ファイルメニューから新規作成します。
「一般」で名前などを入力したら 「資格情報」から先ほど作成したCSRを読み込みます。

デバイスをUSB接続していれば「デバイス」に表示されますので、 インストールしたいデ
バイスを選んでインストールします。

スクリーンショット 2014-03-20 10.18.45のコピー

以上で設定は完了です。
apacheの標準の設定ではDocumentRootは “/Library/WebServer/Documents” になっていま
すので、適当なHTMLを置いて、アプリケーションをダルンロード出来るようにします。

sqlite の PRAGMA Statements 備忘録

最近CoreDataを使ったアプリケーションの開発を行っていて「?」と思った事がありました。
.sqliteファイルのあるディレクトリに .sqlite.wall と言う名前のファイルがいつの間にか出来ていました。
最初は「まあいいか」と無視をしていたのですが、CoreDataからの更新がちゃんと反映されているかを確認するために .sqlite ファイルを sqlite3 コマンドで開いても全く更新されていない事があり調べてみました。

結論から言うと、iOS7からSQLiteのpersistent storeにjournal_modeというのがデフォルトで使われるようになったとの事です。
https://developer.apple.com/library/ios/releasenotes/DataManagement/WhatsNew_CoreData_iOS/
上記のリンクを開くと書いてありますが、これによりロールバックのジャーなリングにおいて、信頼性とパフォーマンスが向上するそうです。しかし読み込み専用やiOS4以前との互換性のためには推奨されないようです。

で、その journal_mode を使わないようにする方法です。
NSPersistentStoreCoordinator に sqlite の Persistent Store を追加する際に、optionで指定してやります。

    NSMutableDictionary* sqlLitePragmaOptions;
    sqlLitePragmaOptions = [NSMutableDictionary dictionaryWithCapacity:0];
    [sqlLitePragmaOptions setObject:@"DELETE"
                             forKey:@"journal_mode"];

    NSMutableDictionary* options;
    options = [NSMutableDictionary dictionaryWithCapacity:0];
    [options setObject:sqlLitePragmaOptions
                forKey:NSSQLitePragmasOption];

    NSError *error;
    if (![psc addPersistentStoreWithType:NSSQLiteStoreType
                           configuration:nil
                                     URL:storeUrl
                                 options:options
                                   error:&error]) {
		// Update to handle the error appropriately.
		exit(-1);  // Fail
    }

試してはいませんが、それ以外の sqlite の PRAGMA Statements も同様の方法で指定してやる事が出来るようです。
sqllite の PRAGMA Statements の一覧はこちらです。

opencv-2.4.6 on iOS 7.0.3 (64 bit)

iOS7と同時にリリースされたiPhone5sは64bitのCPUを搭載しています。
今までのアプリもそのままで動作し、高速ではあるのですが、やはり64bitのネイティブアプリを試してみたくなります。
一つのプロジェクトで構成されていれば簡単に出来てしまいますが、外部のフレームワークやライブラリなどを使っている場合にはそれらが64bit対応していなければなりません。

そこでまずOpenCVの64bit対応を試してみました。

定量的な測定はまだしていませんが、iPhone5sでは32bit版アプリでもかなり高速ですが、64bitアプリにすると更に高速になっている感じがあります。
つい先日発表されたiPad airやRetina版のiPad miniも64bit CPUを登載しているようですので、今後は64 bitアプリも多く出てくるのではないかと思います。

 

●環境

xcode 5.0.1
Darwin Kernel Version 13.0.0 (Mervericks)

platforms/ios/build_framework.py

def build_framework(srcroot, dstroot):
    "main function to do all the work"

    targets = ["iPhoneOS", "iPhoneOS", "iPhoneSimulator"]
    archs = ["armv7", "armv7s", "i386"]
    for i in range(len(targets)):
        build_opencv(srcroot, os.path.join(dstroot, "build"), targets[i], archs[i])
    put_framework_together(srcroot, dstroot)

112,113行目に以下のように編集し、ターゲットとアーキティクチャを追加する。

    targets = ["iPhoneOS", "iPhoneOS", "iPhoneOS", "iPhoneSimulator",  "iPhoneSimulator"]
    archs = ["arm64", "armv7", "armv7s", "i386", "x86_64"]

platforms/ios/cmake/Modules/Platform/iOS.cmake

# Hidden visibilty is required for cxx on iOS
set (CMAKE_C_FLAGS "")
set (CMAKE_CXX_FLAGS "-stdlib=libc++ -headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden")

set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -fomit-frame-pointer -ffast-math")

if (HAVE_FLAG_SEARCH_PATHS_FIRST)
    set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
    set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
endif (HAVE_FLAG_SEARCH_PATHS_FIRST)

45行目を以下のように編集し、最適化オプションを変更する。

    set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -fomit-frame-pointer -ffast-math")

modules/legacy/src/dpstereo.cpp

#define CV_IMAX3(a,b,c) ((temp3 = (a) >= (b) ? (a) : (b)),(temp3 >= (c) ? temp3 : (c)))
#define CV_IMIN3(a,b,c) ((temp3 = (a) <= (b) ? (a) : (b)),(temp3 <= (c) ? temp3 : (c)))

このままではマクロでエラーになるため下記のように修正する。

 
#define CV_IMAX3(a,b,c) (temp3 = (a >= b ? (a >= c ? a : (b >= c ? b : c)) : (b >= c ? b : c)))
#define CV_IMIN3(a,b,c) (temp3 = (a <= b ? (a <= c ? a : (b <= c ? b : c)) : (b <= c ? b : c)))

最後に以下のコマンドでフレームワークを作成します。

python ../opencv/platforms/ios/build_framework.py ios

Time Machineの障害対応 備忘録

開発用マシンのMacはTime Machineでバックアップしています。
ですが時々エラーが起こり、再度のフル・バックアップからやり直してくれと言ってきます。

これまでは諦めて支持通りに新規のフル・バックアップを行っていました。
(プロジェクト関連はTime Machineとは別に個別のバックアップは取っています)
しかしこのところ頻繁に起こるようになりましたので、何とか修復は出来ないかと試してみました。
フル・バックアップが始まるとMacにもかなりの負荷がかかり開発等も効率が悪くなりますので。

結論から言うとFix Time Machine Sparsebundle NAS Based Backup Errorsの記述通りに行うと修復出来たようです。

記述は全て英語なので、簡単に訳してみます。

  1. ターミナルを開き以下コマンドでrootになって下さい。

    sudo su -
  2. Verificationが既に実行されてsparsebundleがbadにマークされていますので、まずそれをnormalにする必要があります。
    コマンドラインから以下のコマンドを実行して下さい。

    chflags -R nouchg /Volumes/{name of your network share}/{name of}.sparsebundle

    このコマンドは少し時間が掛かります。
  3. 次に以下のコマンドを実行して下さい。

    hdiutil attach -nomount -noverify -noautofsck /Volumes/{name of your network share/{name of}.sparsebundle

    以下のような実行結果が表示されます。

    /dev/diskx Apple_partition_scheme
    /dev/diskxs1 Apple_partition_map
    /dev/diskxs2 Apple_HFSX

    x は外部ディスクのディスクIDです。
    Apple_HFSX または Apple_HFS と出力されたものが対象のディスクです。
  4. この時点でファイルシステム・チェックのプロセス(fsck)が既に起動されています。
    下記のコマンドでfsckの実行状況を追跡する事が出来ます。

    tail -f /var/log/fsck_hfs.log

    fsckが起動されていれば sparsebundleを修復するはずです。このコマンドは数時間かかります.
  5. fsckが終了すると以下のどちらかが表示されます。

    ‘The Volume was repaired successfully’


    ‘The Volume could not be repaired’
  6. もし後者の場合にはもう一度ディスク修復のために以下のコマンドをもう一度実行します。

    fsck_hfs -drfy /dev/diskxs2
    もし十分なRAMがある場合には以下のようにRAMキャッスを指定する事で高速かする事が出来ます。

    fsck_hfs -drfy -c 750 /dev/diskxs2

    上記の例では750MBのRAMを使用します。システム構成により変更して下さい。もしよく分からない場合には最初の例を使用してください。x は環境により置き換えて下さい。
  7. 1〜2時間かかります。
    もう一度下記のコマンドを実行して下さい。

    tail -f /var/log/fsck_hfs.log

    修復が成功していれば下記のように表示されます。

    ‘The Volume xxxxx was repaired successfully’
  8. 以下のコマンドを実行して下さい。

    hdiutil detach /dev/diskxs2
  9. 上記の処理を何度か実行して下さい。
  10. 上記処理が完了したら、バックアップの状態を記録している sparsebundle 内の plist ファイルを編集する必要があります。sparsebundle の直下にある com.apple.TimeMachine.MachineID.plist を編集します。
  11. 以下の2行を削除します

    <key>RecoveryBackupDeclinedDate</key>
    <date>{whatever-the-date}</date>

    (私の環境では上記の行はありませんでした)
  12. 以下の行を変更します。
    変更前

    <key>VerificationState</key>
    <integer>2</integer>

    変更後

    <key>VerificationState</key>
    <integer>0</integer>

上記の作業で無事、Time Machineを復旧する事が出来ました。
所要時間は約3時間。
過去のバックアップも見る事が出来ます。

自力でStoryboardをコンパイルしてアプリで読み込む 備忘録

1. ibtoolをカスタムで起動させる。

まず、 “Build Phases” -> “Copy Bundle Resource” から拡張子が “.storyboard” となっているファイルを外します。
次に ”Build Phases” -> “Run Script” に追加して以下のように書き込みます。

ibtool --errors --warnings --notices --minimum-deployment-target 5.0 
       --output-format human-readable-text --compile
        'Sample/Storyboard.storyboardc' 
        'Sample/Storyboard.storyboard'

このコマンドの出力ファイルは Storyboard.storyboardc です。最後に “c” が追加されています。
シュミレータでアプリがインストールされるディレクトリを参照して頂ければ、Storyboardのファイルはこのように最後に “c” の付いたファイルになっている事が確認出来ると思います。
Storyboard.storyboardc を追加します。

また念のために “Build Phases” -> “Copy Bundle Resource” でこのファイルがコピー対象になっている事も確認します。

2. AppMainAppDelegate.mを書き換えます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSString *reqSysVer = @"7.0";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
        // iOS 7.0 or later
        UIStoryboard* storyboard;
        NSString* storyboardFile;
        storyboardFile = @"Storyboard";
        storyboard = [UIStoryboard storyboardWithName:storyboardFile bundle:nil];

        self.tabBarController = [storyboard instantiateInitialViewController];

    }
    else {
        // Override point for customization after application launch.
        UIViewController *viewController1 = [[[AppMainFirstViewController alloc] initWithNibName:@"AppMainFirstViewController" bundle:nil] autorelease];
        UIViewController *viewController2 = [[[AppMainSecondViewController alloc] initWithNibName:@"AppMainSecondViewController" bundle:nil] autorelease];
        self.tabBarController = [[[UITabBarController alloc] init] autorelease];
        self.tabBarController.viewControllers = @[viewController1, viewController2];
    }

    _window = [UIWindow alloc];
    _window = [_window initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = self.tabBarController;
    [self.window makeKeyAndVisible];

    return YES;
}

この例ではxcodeのabBarのテンプレートから生成されるコードを元に追加変更しています。

3. iOS7への移行を考える

これを書いている時点ではまだWWDCも終わっていません。ですのでまだどんなサプライズがあるかはわかりません。しかし今の時点で出てきている部分だけを見てもUIに関しては大きな変更が加わっています。ある意味、総取っ替えの感もあります。もちろんUIButtonなどの今までのクラスは引き続き使う事は出来ます。ですが外観が大きく変わり、サイズも変わっています。全体の整合性を取るためにはiOS7以前と後ではUIを別の物として考えた方が良いのではないかとさえ思います。
XcodeもDeveloper Previewの段階ですのでまだ使い勝手が良くなるのかもしれません。
しかし既に幾つかのアプリをリリースしてしまっている側としてはマイグレーションの方法を早めに見つけておいた方が良いかと思います。

この例ではNIBを使った今までのコードにStoryboardを加えたわけですが、すでにStoryboardを使って開発しているアプリも、iOS7以前と以後のStoryboardを分けて実装する事も可能となります。