Home > iPhone SDK / Cocoa / Objective-C

iPhone SDK / Cocoa / Objective-C Archive

CocoaでURLエンコード

NSStringを指定した文字コードでURLエンコードします。

一瞬 stringByAddingPercentEscapesUsingEncoding: のみで事足りそうに思うのですが、それだけだと下記コード中の escapeChars にある文字達が残ってしまいます。

@implementation NSString (URLEncoding)
 
-(NSString *)stringByURLEncoding:(NSStringEncoding)encoding
{
  NSArray *escapeChars = [NSArray arrayWithObjects:
             @";" ,@"/" ,@"?" ,@":"
            ,@"@" ,@"&" ,@"=" ,@"+"
            ,@"$" ,@"," ,@"[" ,@"]"
            ,@"#" ,@"!" ,@"'" ,@"("
            ,@")" ,@"*"
            ,nil];
 
  NSArray *replaceChars = [NSArray arrayWithObjects:
              @"%3B" ,@"%2F" ,@"%3F"
             ,@"%3A" ,@"%40" ,@"%26"
             ,@"%3D" ,@"%2B" ,@"%24"
             ,@"%2C" ,@"%5B" ,@"%5D"
             ,@"%23" ,@"%21" ,@"%27"
             ,@"%28" ,@"%29" ,@"%2A"
             ,nil];
 
  NSMutableString *encodedString = [[[self stringByAddingPercentEscapesUsingEncoding:encoding] mutableCopy] autorelease];
 
  for(int i=0; i<[escapeChars count]; i++) {
    [encodedString replaceOccurrencesOfString:[escapeChars objectAtIndex:i]
                                   withString:[replaceChars objectAtIndex:i]
                                      options:NSLiteralSearch
                                        range:NSMakeRange(0, [encodedString length])];
  }
 
  return [NSString stringWithString: encodedString];
}
 
@end

# なんでもかんでもカテゴリにすればよいというものではないのは分かっているのですが、つい…。

関連する投稿

非公開APIの使い方

実際に使うかどうかはともかくとして、開発者たるもの好奇心と探究心を大切にしなければなりません。というわけで、非公開APIの使い方です(といっても特に難しいことはないのですが)。

1. 使用するAPIの見当をつける

まずは、非公開APIのリストがなくては始まりません。後述するように簡単に自作することもできるのですが、Erica Sadun 女史のサイトに分かりやすくまとまっていますのでこちらを参照するとよいのではないでしょうか。

ericasadun.com (Site Highlights → 2.2 Framework Documentation

Cocoaのくどい(?)メソッド名のおかげで、地道に調べていけばお望みの機能の実装方法の見当をつけることはそれほど難しくないかと思います。

2. フレームワークを読み込む

使用したいクラスが決まったら、そのクラスが含まれるフレームワークを読み込みます。/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.sdk/System/Library/PrivateFrameworks 以下にありますね。

3. ヘッダファイルを用意する

続いて、必要なヘッダファイルを用意します。上記のサイトから地道にコピーしてきても良いのですが、行番号が入ってしまっているため面倒です。ここで活躍するのが、各種バイナリファイルから含まれるクラスのヘッダファイルを生成してくれる class-dump-x です。

しかし、今確認してみたところオフィシャルサイトのサーバーに接続できない状態のようです。ファイル自体はこのあたりから取得できそうな感じですが(試していません)、何かあったのでしょうか…。(追記@2009.1.16)無事復活していました。

とにかく、無事アプリケーションファイルを入手できたら、/usr/local/bin あたりのパスが通ったディレクトリに配置して、

> class-dump-x AddressBookUI.framework/AddressBookUI

といったようにすると、AddressBookUIフレームワークに含まれるクラスのヘッダファイルに相当するソースコードがずらずらと表示されます。クラスを限定したい場合には、

> class-dump-x AddressBookUI.framework/AddressBookUI -C [クラス名の正規表現]

こんな感じです。-H オプションをつければ、ヘッダファイルとして吐き出してくれるのでさらに便利です。ちなみに、カテゴリに対しては(クラス名にはヒットせず)カテゴリ名のみが検索対象のようです。

なお、必要なヘッダファイルで使用されているクラスのヘッダファイルも作成して、とやっているとキリがありませんので、直接使用しないクラスは @class で逃げておくとよいでしょう。

4. がんばって試行錯誤する

ここまでくれば、後はあーでもないこーでもないと試行錯誤するだけです。一応お約束なので書いておきますが、ご利用はくれぐれも自己責任でどうぞ。

ちなみに、英語では「undocumented API」と呼ぶのが一般的なようです。

関連する投稿

NSDataのMD5メッセージダイジェストを計算する

NSDataクラスにMD5メッセージダイジェストを計算するメソッドを追加してみました。

NSData+Digest.h

#import 
 
@interface NSData (Digest)
 
- (NSString *)MD5DigestString;
 
@end

NSData+Digest.m

#import "NSData+Digest.h"
#import "CommonCrypto/CommonDigest.h"
 
@implementation NSData (Digest)
 
- (NSString *)MD5DigestString
{
  unsigned char digest[CC_MD5_DIGEST_LENGTH];
 
  CC_MD5([self bytes], [self length], digest);
 
  char md5cstring[CC_MD5_DIGEST_LENGTH*2];
 
  for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
    sprintf(md5cstring+i*2, "%02x", digest[i]);
  }
 
  return [NSString stringWithCString:md5string length:CC_MD5_DIGEST_LENGTH*2];
}
 
@end

参考:iPhone で MD5 ハッシュ値を取得には? - 1/4dot Blog

ちなみに、最初はメソッド名を MD5Digest としていたのですが、NSDataを拡張している別のカテゴリに同名のメソッド(NSConcreteDataという非公開クラスのインスタンスとしてMD5メッセージダイジェストを返してきます)が存在するようで、そのせいで少々はまりました…。

同名のメソッドが複数のカテゴリで定義されていた場合、どのカテゴリの定義が有効になるかは保証されていません。

(「詳解 Objective-C 2.0」より)

プライベートメソッドでなく公開メソッドにしておいてくれればよかったのになー、と思ったことは言うまでもありません。

関連する投稿

iPhoneシミュレーターの写真アルバムに画像を入れる

  1. 適当な画像ファイルをシミュレータにドラッグ&ドロップ
  2. 画像がSafariで開かれるので、画像の上でマウスボタンを長押し→画像を保存

ただしサイズが小さく、画質が悪くなります。開発にあたっては特に問題ないのですが、なんとかなりませんかね?

(追記)「ただしサイズが小さく、画質が悪くなります」と思ったのですが、UIImagePickerControllerでデータを取り出してみると(オリジナル画像よりは縮小されていますが)実際にはそれなりのサイズと画質で保存されていました(おそらく、iTunes経由で転送したのと同じ状態)。iPhoneのこのあたりの処理は謎ですね…。

関連する投稿

idleTimerDisabled = NO でもスリープしない

[UIApplication sharedApplication].idleTimerDisabled = YES;

とすると自動ロックされない、というTIPSは今や常識なのですが、おかしなことにこのプロパティが NO なのに自動ロックされないという現象に悩むこと小一時間。

デバッグ中はもともと自動ロックされないのですね、というオチでした。

関連する投稿

releaseすべきところでdeallocをすると

iPhoneアプリ開発において、本来releaseするところでdeallocをすると当然ながら実行時にエラーが発生します。ただし、エラーが発生するのはdeallocした場所ではなく値を参照した場所となるので注意が必要です。
以下は今回体験したdeallocとreleaseの間違えでエラーが発生してから原因にたどり着くまでの流れです。

Continue reading

関連する投稿

NSUndoManagerをiPhoneOSで使いたい

詳解Objective-C2.0を見ると、アンドゥ機能を実現するためのクラスNSUndoManagerがFoundation Frameworkに存在すると書いてありました。ところが、XCode上でNSUndoManagerが補完されません。何か設定が必要なのでしょうか。

そこでドキュメントを調べてみるとiPhone OS Technology Overviewに次のような記述が有りました。

Undo management
The NSUndoManager class is not available, and there is no automatic support for undoing operations in iPhone OS.

残念なことに、iPhoneOSにはNSUndoManagerが存在しないようです。

では、iPhone OSでのundoの好ましい実装はどのようになるのかとさらにドキュメントを調べるとCocoa Fundamentals Guideの「What Is A Design Pattern?」に例としてあげられているCommandパターンの中でundo,redoに触れていることが分かりました。CommandパターンはMementoパターンと並んでUndo実装の定石ですので、問題になるのはCommandパターンの実装方法です。

同ドキュメントによると、Commandパターンやタイマー呼び出しなどの為にNSInvocationが用意されていると有ります。NSInvocationは事前にオブジェクト、メッセージ、引数を設定しておき後から実行(Invoke)するためのクラスです。詳しい使い方についてはoomori.com Cocoa Referenceが分かりやすく書かれていました。NSUndoManagerもこのNSInvocationを用いて実装されているようです。

関連する投稿

switch/caseの中で変数を宣言する

switch (hoge) {
  case 0:
    int i;
    break;
}

上記のように case の中で変数を宣言すると、

syntax error before ‘int’

というように怒られるのが謎だったのですが、これはスコープの問題だそうですね。

以下のように、スコープを case の中に限定してやれば良いようです。なるほど。今までは、if/else で代用したり、switch の外で宣言したりしてごまかしていたので助かりました。UITableView では switch/case が大活躍です(良いコードとは言えませんが……)。

switch (hoge) {
  case 0: {
    int i;
  }
  break;
}

参考:二流プログラマの三流な日常: switch文の途中で宣言する

関連する投稿

iPhone SDK 非公開APIの取り扱いについて

ありそうでなかったカメラロール関連アプリ「Roll Swap」 - もとまかのiPhone・iPod touch戯れ日記

上記のアプリ、どうも非公開APIを利用しているように思えるのですが、結局のところ非公開APIは黙認されているのでしょうか?

巷を賑わせたGoogleのアプリに関する以下の記事では、

あぁ我々は非公開APIを使ったよ。それがどうした? | iPhone 3G Wiki blog

この記事も、CNETの元記事も「非公開APIは利用しても良い。ただ改版されたときに使えなくなる可能性があり、その改版についていける体力があるデベロッパーのみが可能である」という内容になっています。

となっていますが、私が元のCNETの記事を読んだ限りでは、「非公開APIは利用しても良い」とは書いてないように思いました。上記エントリのコメント欄にも書かれていますが、以下のように理解しています。

  • 非公開(undocumented)APIを利用したアプリはApp Storeでは販売できないことになっている
  • しかし実際には多くの開発者が非公開APIを利用しているようだ

(以下原文の該当部分)

The problem with using undocumented APIs is that your application code could break in the future as Apple updates its software, but a lot of developers appear to have taken that risk in order to deliver a cool feature, such as Google’s verbal search prompt.

Under the original terms of the SDK, however, applications using such techniques were not supposed to make it through to the App Store. As a result, other developers who played strictly by the SDK rules would not have felt it possible to create an application that duplicated Google’s voice prompt using the proximity sensor, whereas those who had the resources to quickly rewrite anything that ran afoul of the App Store gatekeepers could push ahead and test Apple’s limits.

CNETの記事によれば、「アップルのrepresentativesの多くは現在(11月25日)休暇中のため、この件についてのコメントが得られるのはもう少し先になりそうだ」とありますので、とりあえずは様子見ですね。

しかしながら、「a lot of developers appear to have taken that risk(=将来のアップデートでAPIが変更されるというリスク)」なのであるとすれば、事実上の黙認状態と言っても良いかもしれません。ありきたりな言い方ですが、「ご利用は自己責任で」といったところでしょうか。

# 今更この件にふれたのは、最初にあげたアプリで使用していると思われる非公開APIが自分がまさに求めていた機能であったためです。

関連する投稿

iPhoneアプリ開発を始めた頃にハマりがちな様々なポイント

1. 可変個の引数を受けるメソッドの最後に nil を渡していない

NSArray *array = [NSArray arrayWithObjects:obj1 ,obj2 ,obj3];

2. (主にイニシャライザの中で)メンバ変数を retain していない

autorelease済みのインスタンスを返すイニシャライザを使ったときに、ついついretainを忘れてしまいます。そのメンバ変数にメッセージを送ろうとした瞬間クラッシュします。

3. retain, copy, assign を意識していない

例)NSMutableDictionaryのキーとしてCopyできないオブジェクトをセットしようとする(ちなみにこの場合は代わりに CFMutableDictionary を使うのがてっとり早いです)。

4. フォーマット指定子の動作を理解していない

NSLog(@"%d", 1.0);

で「0」が出力されるのが原因で小一時間悩んだ記憶があります。PHPなんかでは float(っぽい変数) を %d で出力すると、int にキャストされた値が出力されるので…。

5. NSSet の anyObject がランダムな要素を返すと勘違いする

「もっとも返しやすい要素」を返すだけで決してランダムではありません。

6. NSMutableArray, NSMutableSet, NSMutableDictionaryの初期化を忘れ、オブジェクトを add できない

この状態を、「nilに腕押し」と名付けました。

7. NSMutableDictionary の setObject:forKey: と setValue:forKey: を混同する

setValue:forKey:の方はキー値コーディング用のメソッドですね。詳細はリファレンスをご確認ください。

8. UIKit Additions なメソッドを見落とす

NSIndexPathクラスのsectionプロパティ、rowプロパティの存在に最近気付きました。

9. (引数が一つのメソッドについて)セレクタの最後の:(コロン)をつけ忘れる

doSomethingWithParameter:(int)param なのに、@selector(doSomethingWithParameter) としてしまい、そんなメソッドはないと怒られます。

10. 互いにimportしあうヘッダファイルができて悩む

@class を使って回避します(そもそも設計が間違っているという気もします)。

11. 必要なフレームワークが見つからない

CoCoa Touch、Media レイヤーのフレームワークはデフォルトで開いているフォルダにはないので。

12. 実機にインストールできない

iPhone SDK が2.2になって大分簡単になりましたが、証明書のインストールあたりの手順を飛ばしてしまってハマるのがありがちかと思います。

関連する投稿

ホーム > iPhone SDK / Cocoa / Objective-C

Search
Feeds
Meta

Return to page top