【Objective-C】iOS6で画像編集処理をした際に起こるエラーと対処法

iOS 6

iOS5までは大丈夫!でもiOS6では…。そんな状況を何度耳にしたことでしょう。

カメラアプリや画像加工系アプリで不具合に当たった方も多いのではと思います。今回は画像のピクセルデータにアクセスしようとすると、EXC_BAD_ACCESSが起きてクラッシュするようになった件についてです。

2012/10 プログラムの変更必要箇所について、修正及び注釈を加えました。

UIImageに入った画像のデータにアクセスする

画像を加工編集する方法の一つとして、CGImageを取得&ごにょごにょしてピクセルデータを参照するという方法があります。詳しくはこちら。

参考: 【iPhone】カメラアプリ系の画像処理をする

大変お世話になった上記の方法。iOS5まで大丈夫だったんですが、iOS6で、“ビットマップに効果を与える”(参考サイト上)の処理にある、ピクセルのポインタへアクセスするときに、EXC_BAD_ACCESSが発生する場合があります。



CGDataProviderCopyDataとCFDataGetBytePtr

結論から言うと、この CGDataProviderCopyDataCFDataGetBytePtr の部分を修正すれば解決しました。ピクセルのポインタ参照時にEXC_BAD_ACCESSが起きた場合は、以下のように変更します。

//before
// CFDataRef inputData = CGDataProviderCopyData(dataProvider);
// UInt8 *pxData = (UInt8 *)CFDataGetBytePtr(inputData);


//after 2012/10 修正前の内容
// CFMutableDataRef inputData = CFDataCreateMutableCopy(0, 0, CGDataProviderCopyData(dataProvider));
// UInt8 *pxData = (UInt8 *)CFDataGetMutableBytePtr(inputData);

//after 2012/10 修正
CFDataRef tmpData = CGDataProviderCopyData(dataProvider);
CFMutableDataRef inputData = CFDataCreateMutableCopy(0, 0, tmpData);
UInt8 *pxData = (UInt8 *)CFDataGetMutableBytePtr(inputData);

//tmpDataとinputDataは使い終わったら必要に応じてCFRelease()

参考: EXC_BAD_ACCESS in iOS 6 but not in iOS 5

どうやら可変長の型で扱うようにすると大丈夫のようです。

参考にあった内容をそのまま使うとCGDataProviderCopyData(dataProvider)部分がメモリリークすることを確認したので、外に出して使用後CFReleaseしています。

エラーになるパターンとならないパターン

私の場合はiOS6で初めて試したときにはもうエラーで撃沈。そこで問題を切り分けていたところ、落ちるパターンと落ちないパターンが出てきました。

その要因となるところが、元のUIImageです。

リソースやカメラロールの画像を読み込み、何も手を加えていないUIImageからCGImageを取得する場合はピクセル参照時にエラーになりませんでした。

ところが、UIImageの拡大縮小から合成などにも使えるグラフィック用のContextから取得したデータを使うと、今回のエラーになるということがわかりました。

パターン1: エラーにならなかった
self.myImage = [UIImage imageNamed:@"image.png"];

CGImageRef cgimage = [self.myImage CGImage];

//以下ごにょごにょするもiOS6でもエラー無し
パターン2: エラーになった(EXC_BAD_ACCESS)
self.myImage = [UIImage imageNamed:@"image.png"];

//視覚的には同じ内容を描画して取得しているだけ
UIGraphicsBeginImageContext(self.myImage.size);
[self.myImage drawInRect:CGRectMake(0,0,self.myImage.size.width,self.myImage.size.height)];
self.myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

CGImageRef cgimage = [self.myImage CGImage];

//以下ごにょごにょでiOS6はEXC_BAD_ACCESS

型としては同じUIImageなのに、片方のCGImageはOKでもう片方はダメということで、理解が難しいところ。ちなみにごにょごにょへ続かなければ、両方とも画像表示も問題なく、保存しても閲覧できました

気付いた当初はスマートな解決策が思いつかず、Context上で作った画像を、一度端末に保存して再度読み込み無理やりパターン1にするという、おまじない的手法で回避させていました。

どちらも個別に使用すればエラーにならないけど、お互い特に干渉もなさそうなのに組み合わせたらエラーになる、という迷宮に陥りやすい現象です。

なぜiOS5では動きiOS6では動かないのか。解決策はわかっても、根本的なその原因と解決できる理由が理解できないと、なかなか前に進みにくいのが悩みの種。今後も注意深く扱っていこうと思います。

タグ: iOS6 Xcode 開発 不具合 画像処理 UIImage CGImage

プロフィール
Daisuke Suzuki
ウェブサイトの制作から運営管理、アプリ開発など、様々な挑戦をして試行錯誤の日々を送っているフリーランスです。対外業務をするお仕事生活から抜け、現在は個人制作でご飯を食べています。 最近はアプリ攻略サイト AppGames を制作・公開し、そちらも手がけています。詳しいプロフィール
サイト内検索
自主制作したiOSアプリ
脱出ゲーム TAKUMI
脱出ゲーム Mobile Shop
付箋メモ帳型ToDoリスト
タブ式メモ帳
このブログについて
一から制作したお手製ブログ ver1.23
2012年9月11日に開始。自分で見て触って動かして、日々コツコツと調整しています。