Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2012-10-04

ARC を有効にしているコードで NSZoneMalloc をどう置き換えればよいか

AVFoundation を用いたカメラアプリを実装していて、参考にしていたサンプルコードに次のような NSZoneMalloc を用いてメモリ確保している箇所がありました。

bitmap = NSZoneMalloc(self.zone, width * height * 4);

これを ARC オンにしているソースに書いてビルドすると、


'zone' is unavailable: not available in automatic reference counting mode


こんな「ARCではzoneプロパティは使えませんよ」エラーが。

(※zoneはNSObjectのプロパティ)


他の部分はARC前提で書いてるので、このソースファイルを非ARCにしたくない場合、ここをどうにか書き換える必要があります。


対処方法

結論としては、malloc() を使って書き換えます。


mallocは引数に確保すべきサイズを渡すだけなので、上記の例を書き換えると次のようになります。

//bitmap = NSZoneMalloc(self.zone, width * height * 4);
bitmap = malloc(width * height * 4);

解放する部分のコードも変わるので、ご注意ください。

//NSZoneFree(self.zone, bitmap);
free(bitmap);
bitmap = NULL;

(参考ページ)

http://stackoverflow.com/questions/10849251/replacing-usage-of-nszonemalloc-in-an-arc-enabled-ios5-app



2012-10-03

dispatch_get_global_queue と dispatch_queue_create の違い

他サイト様の引用ばっかりで恐縮ですが、説明がわかりやすかったのでこちらにメモっておきます。


dispatch_get_global_queue と dispatch_queue_create の違い

Nacho4d - programming notes: December 2010 より)

メインキュー : メインスレッドで実行

dispatch_queue_t main = dispatch_get_main_queue();

グローバルキュー : バックグラウンドで実行

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

プライベートキュー : バックグラウンドで実行、名前付きのキュー

dispatch_queue_t queue = dispatch_queue_create("com.nacho4d.myApp.myQueue",NULL);

他のタイプのキューとの大きな違いはブロックや関数が逐次的に実行されていくこと。他のキューではタスクをエンキューしても実際にどの順番で実行されるかが分からないが、プライベートなキューでは必ずキューの頭からタスクを実行していくのでシリアルなキューを実現できる。

プライベートなキューを作成するとRetainカウントが1なので、 最後にリリースをすることを忘れずに

dispatch_release(high);


dispatch_async と dispatch_sync の違い

Grand Central Dispatch その2 Queue - Objective-Audio より)

ayncは非同期でバラバラに、syncは同期で順番に、キューにブロックを処理をさせようとする関数です。と説明すると、キューにも同じような用途で2種類あるのに何で関数がまた同じように2種類分かれているんだと思ってしまうのですが、ちょっと役割が違います。

ブロックが処理されるスレッドを調べてみると、asyncは呼び出し元のスレッドとは別のスレッドでブロックを処理させ、syncは呼び出し元と同じスレッドで処理をさせるようです。

実際の挙動としては、グローバルキューにasyncでブロックを渡すと別スレッドで並列に実行されますが、syncで渡すと現在のスレッドをつかうのでそのまま直列に実行されます。シリアルキューにsyncで渡せばもちろんそのまま直列に実行ですが、asyncで渡すと入れていった順番に別スレッドで実行されます。




2012-10-02

【その2】UIImagePickerController を使わないカメラアプリの実装方法

前回記事の続きです。


前回は、プレビュー表示するところまで書きました。今回は、シャッター撮影して画像をカメラロールに保存するところまで。


出力の準備

プロパティ宣言
@property (nonatomic, strong) AVCaptureStillImageOutput *imageOutput;

出力の初期化

何やら長いですが、前回記事との差分は、「出力の初期化」の2行だけです。

// カメラデバイスの初期化
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

// 入力の初期化
NSError *error = nil;
AVCaptureInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice
                                                                   error:&error];

if (!captureInput) {
    NSLog(@"ERROR:%@", error);
    return;
}

// セッション初期化
AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
[captureSession addInput:captureInput];
[captureSession beginConfiguration];
captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
[captureSession commitConfiguration];

// プレビュー表示
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
previewLayer.automaticallyAdjustsMirroring = NO;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewLayer.frame = self.view.bounds;
[self.previewCtr.view.layer insertSublayer:previewLayer atIndex:0];

// 出力の初期化
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
[captureSession addOutput:self.imageOutput];

// セッション開始
[captureSession startRunning];

シャッターを押したときの処理を実装する

NSData 型を UIImage に変換して、カメラロールに保存する処理を Blocks で記述しています。

- (IBAction)pressShutter {
    
    AVCaptureConnection *connection = [[self.imageOutput connections] lastObject];
    [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection
                                                  completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
                                                      
                                                      NSData *data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                                                      UIImage *image = [UIImage imageWithData:data];
                                                      ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
                                                      [library writeImageToSavedPhotosAlbum:image.CGImage
                                                                                orientation:image.imageOrientation
                                                                            completionBlock:^(NSURL *assetURL, NSError *error) {
                                                                            }];
                                                  }];
}

次のステップ

次は、静音カメラ系のような、シャッター音を鳴らさない実装について書きます。


2012-10-01

【その1】UIImagePickerController を使わないカメラアプリの実装方法

「カメラ機能をアプリにつけたいけどシャッター音を鳴らしたくない」とか、「カメラ起動時のアニメーションが嫌だ」とかの場合は、UIImagePickerController を使うのではなく AVFoundation フレームワークを使ってカメラ機能を自作する必要があります。


といってもものすごく低レイヤからゴリゴリごにょごにょしなきゃいけないわけじゃなくて、ある程度わかりやすくラップされてるので、CoreXXXX系のフレームワークやOpenGLほどとっつきにくくはなさそうです。


以下はプレビュー表示できるところまでの手順になります。シャッター機能もデータ保存機能も省いてて、細かい設定もしてないので、これが一番シンプルな最低限の形かと思われます


準備

必要なフレームワークをプロジェクトに追加
  • AVFoundation.framework

Info.plist編集

Required device capabilities (UIRequiredDeviceCapabilities) に以下のキーを使う機能に応じて指定

  • still-camera
  • auto-focus-camera
  • front-facing- camera
  • camera-flash
  • video-camera

実装

// カメラデバイスの初期化
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

// 入力の初期化
NSError *error = nil;
AVCaptureInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice
                                                                   error:&error];


if (videoInput) {
    
    // セッション初期化
    AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
    [captureSession addInput:videoInput];
    [captureSession beginConfiguration];
    captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
    [captureSession commitConfiguration];
    
    // プレビュー表示
    AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
    previewLayer.automaticallyAdjustsMirroring = NO;
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    previewLayer.frame = self.view.bounds;
    [previewCtr.view.layer insertSublayer:previewLayer atIndex:0];

    // セッション開始
    [captureSession startRunning];
}
// エラー
else {
    NSLog(@"ERROR:%@", error);
}


2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |