2012年9月23日 日曜日 HOME > Objective-C
【Objective-C】iOS6で画像編集処理をした際に起こるエラーと対処法
iOS5までは大丈夫!でもiOS6では…。そんな状況を何度耳にしたことでしょう。
カメラアプリや画像加工系アプリで不具合に当たった方も多いのではと思います。今回は画像のピクセルデータにアクセスしようとすると、EXC_BAD_ACCESSが起きてクラッシュするようになった件についてです。
2012/10 プログラムの変更必要箇所について、修正及び注釈を加えました。
UIImageに入った画像のデータにアクセスする
画像を加工編集する方法の一つとして、CGImageを取得&ごにょごにょしてピクセルデータを参照するという方法があります。詳しくはこちら。
大変お世話になった上記の方法。iOS5まで大丈夫だったんですが、iOS6で、“ビットマップに効果を与える”(参考サイト上)の処理にある、ピクセルのポインタへアクセスするときに、EXC_BAD_ACCESSが発生する場合があります。
CGDataProviderCopyDataとCFDataGetBytePtr
結論から言うと、この CGDataProviderCopyData と CFDataGetBytePtr の部分を修正すれば解決しました。ピクセルのポインタ参照時に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では動かないのか。解決策はわかっても、根本的なその原因と解決できる理由が理解できないと、なかなか前に進みにくいのが悩みの種。今後も注意深く扱っていこうと思います。