-->

2020年5月3日日曜日

JSONデータを取り込んでRealmに保存する

プロジェクト内のJSONデータをRealmのモデルに変換して保存する。
xmlやplistを使うことも考えたが、codableを使うことで独自のモデルにも容易に対応させることができた。

環境


  • Xcode 11.4.1
  • Swift 5
  • iOS 13.4.1

StringやIntなどは自動Codableに準拠しているので、encodeやdecodeメソッドを使う必要はないが、
今回はその他のデータ型にも対応できるようにencode、decodeメソッドを実装した。

モデル[Recipe]

import Foundation
import RealmSwift

class Recipe: Object,Decodable {
    @objc dynamic var name =  ""
    @objc dynamic var category = ""
    @objc dynamic var country = ""
    @objc dynamic var season = ""
    var ingredients = List<Ingredient>() //RecipeとIngredientは1対多
    
    override static func primaryKey() -> String? {
        return "name"
    }
    
    private enum CodingKeys:String,CodingKey{
        case name
        case category
        case country
        case season
        case ingredients
    }
    required convenience public init(from decoder:Decoder) throws{
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)

        name = try container.decode(String.self, forKey: .name)
        category = try container.decode(String.self, forKey: .category)
        country = try container.decode(String.self, forKey: .country)
        season = try container.decode(String.self, forKey: .season)
        
        //一度配列に格納した後、Listに変換する
        let ingredientsArray = try container.decode([Ingredient].self, forKey: .ingredients)
        ingredients = ingredientsArray.reduce(List<Ingredient>()) {$0.append($1); return $0}
    }
}

モデル[ingredient]

import Foundation
import RealmSwift

class Ingredient: Object,Decodable {
    @objc dynamic var name = ""
    let recipes = LinkingObjects(fromType: Recipe.self, property: "ingredients")
    
    private enum CodingKeys: String, CodingKey {
         case name
     }
    
    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)

        name = try container.decode(String.self, forKey: .name)

    }
}

JSONファイル

[
        {
        "name": "トマトパスタ",
        "category": "パスタ",
        "country": "イタリアン",
        "season": "Any",
        "ingredients":[
            {
                "name": "トマト"
            },
            {
                "name": "にんにく"
            }
        ]
        },
        {
        "name": "カツ丼",
        "category": "丼ぶり",
        "country": "和食",
        "season": "Any",
        "ingredients":[
            {
                "name": "たまねぎ"
            },
            {
                "name": "卵"
            },
            {
                "name": "豚肉"
            }
        ]
        },
        {
        "name": "ペペロンチーノ",
        "category": "パスタ",
        "country": "イタリアン",
        "season": "Any",
        "ingredients":[
            {
                "name": "にんにく"
            },
            {
                "name": "鷹の爪"
            }
        ]
        },
        {
        "name": "カレー",
        "category": "カレー",
        "country": "和食",
        "season": "Any",
        "ingredients":[
            {
                "name": "人参"
            },
            {
                "name": "たまねぎ"
            },
            {
                "name": "じゃがいも"
            },
            {
                "name": "牛肉"
            }
        ]
        },
        {
        "name": "シチュー",
        "category": "シチュー",
        "country": "和食",
        "season": "Any",
        "ingredients":[
            {
                "name": "人参"
            },
            {
                "name": "たまねぎ"
            },
            {
                "name": "じゃがいも"
            },
            {
                "name": "牛肉"
            }
        ]
        },
        {
        "name": "炒飯",
        "category": "ご飯",
        "country": "中華",
        "season": "Any",
        "ingredients":[
            {
                "name": "人参"
            },
            {
                "name": "たまねぎ"
            },
            {
                "name": "卵"
            },
            {
                "name": "にんにく"
            }
        ]
        },
        {
        "name": "炊き込みご飯",
        "category": "ご飯",
        "country": "和食",
        "season": "Any",
        "ingredients":[
            {
                "name": "人参"
            },
            {
                "name": "椎茸"
            },
            {
                "name": "鶏肉"
            },
            {
                "name": "大葉"
            }
        ]
        }
]

JSONデータの取り込み

        //JSONファイルのパスを取得
        guard let path = Bundle.main.path(forResource: "RecipesList", ofType: "json") else { return }
        let url = URL(fileURLWithPath: path)
        
        do {
            
            let realm = try! Realm()

            let data = try Data(contentsOf: url)
            let recipe_obj = try! JSONDecoder().decode([Recipe].self, from: data)

            try! realm.write {
                realm.deleteAll()
                realm.add(recipe_obj, update: true)
                
                //人参を使うレシピを取得・表示
                var objects: Results<Recipe>
                  objects = realm.objects(Recipe.self).filter("SUBQUERY(ingredients, $ingredient, $ingredient.name = %@).@count >= 1","人参")
                  print("objectsの中",objects)
            }
            
        } catch {
            print("エラー")
        }

参考にしたサイト

https://qiita.com/cottpan/items/4bb5ad8bce2e28c0220a

2020年5月2日土曜日

モデルが持つ配列内を検索し、条件に該当するモデルを取得する

モデルが持つ配列内を検索し、条件に該当するモデルを取得する
モデルデータ
//レシピモデル
class Recipe: Object {
    @objc dynamic var name =  ""
    @objc dynamic var category = ""
    @objc dynamic var country = ""
    @objc dynamic var season = ""
    let ingredients = List() //レシピと材料は1対多の関係
    override static func primaryKey() -> String? {
        return "name"
    }
}

//材料モデル
class Ingredient: Object {
    @objc dynamic var name = ""
    let recipes = LinkingObjects(fromType: Recipe.self, property: "ingredients")
    
}

モデルデータの格納、検索、表示
do {
            //トマトパスタ、カツ丼、カレーの3つのレシピのモデルを作成
            let realm = try Realm()
            let dictionary1: [String: Any] =
                ["name": "トマトパスタ",
                 "category": "パスタ",
                 "country" : "イタリア",
                 "season" : "春",
                 "ingredients":[["name": "トマト"],["name": "にんにく"]]
                ]

            let dictionary2: [String: Any] =
                ["name": "カツ丼",
                  "category": "丼",
                  "country" : "日本",
                  "season" : "夏",
                  "ingredients":[["name": "たまねぎ"],["name": "卵"]]
                 ]
            let dictionary3: [String: Any] =
                ["name": "カレー",
                  "category": "カレー",
                  "country" : "日本",
                  "season" : "夏",
                  "ingredients":[["name": "たまねぎ"],["name": "にんじん"],["name":"じゃがいも"]]
                 ]
            let recipe1 = Recipe(value: dictionary1)
            let recipe2 = Recipe(value: dictionary2)
            let recipe3 = Recipe(value: dictionary3)
            
            try! realm.write {
                realm.deleteAll() //既に保存済のモデルを一旦削除
                realm.add(recipe1)//レシピモデルの保存
                realm.add(recipe2)//レシピモデルの保存
                realm.add(recipe3)//レシピモデルの保存

                //材料にたまねぎを使用するレシピモデルを取得する
                var objects: Results<Recipe>
                objects = realm.objects(Recipe.self).filter("SUBQUERY(ingredients, $ingredient, $ingredient.name = %@).@count >= 1","たまねぎ")
                print("objectsの中",objects)
            }
            
        } catch {
            print("エラー")
        }

出力結果
objectsの中 Results <0x10461f0e0> (
 [0] Recipe {
  name = カツ丼;
  category = 丼;
  country = 日本;
  season = 夏;
  ingredients = List <0x280756760> (
   [0] Ingredient {
    name = たまねぎ;
   },
   [1] Ingredient {
    name = 卵;
   }
  );
 },
 [1] Recipe {
  name = カレー;
  category = カレー;
  country = 日本;
  season = 夏;
  ingredients = List <0x280756880> (
   [0] Ingredient {
    name = たまねぎ;
   },
   [1] Ingredient {
    name = にんじん;
   },
   [2] Ingredient {
    name = じゃがいも;
   }
  );
 }
)

2020年4月30日木曜日

エラー"No viable overloaded ="の解決方法

環境
  • Xcode 11.4.1
  • Swift 5
  • iOS 13.4.1
RealmSwiftを使う際にエラー"No viable overloaded ="が出た場合の対処方法


Realmのバージョンを指定して再インストールする
Podファイルを開き、次のように書き換える
pod 'RealmSwift', '~> 3.18.0'
ターミナルで以下を実行
pod repo update
pod install

XcodeのCreateMLを使ってMLモデルファイルを作成

今回は野菜の画像認識を実装したいので、野菜の画像データを集めてMLモデルファイルを作成する。
環境
  • Xcode 11.4.1
  • Swift 5
  • iOS 13.4.1

参考にしたサイト

1.Open Image Dataset のすべてのデータをダウンロードするには重すぎるため、指定したタグの画像のみダウンロードする。
まず、Explore画面にて、画像のタグを確認する
2.次に「OIDv4-Tooklit」を使って画像をダウンロードする。
Gitはこちらhttps://github.com/EscVM/OIDv4_ToolKit
レポジトリをクローン
git clone https://github.com/EscVM/OIDv4_ToolKit.git
環境設定
cd OIDv4_ToolKit
pip install -r requirements.txt
main.py を実行
python3 main.py downloader --classes Carrot --type_csv train
--classesのあとにダウンロード対象のタグを指定する。上記の例の場合はCarrot(人参)
--type_csvのあとに訓練データ(train)、検証データ(validation)、テストデータ(test)を指定できる。すべての場合はall。
ダウンロードしたファイルのフォルダ構成は以下
train
├─Bell Pepper
├─Cabbage
├─Carrot
├─Potato
└─Tomato
test
├─Bell Pepper
├─Cabbage
├─Carrot
├─Potato
└─Tomato
3.MLファイルを作成する。
xcodeのOpen Develper Tool > CreateMLを起動
NewDocumentを選択したのち、Image Classifierを選択
ProjectName等を記入
Training DataとTesting Dataそれぞれに画像が入ったtrainフォルダ、testフォルダをドラッグ&ドロップ

最後にTrainボタンを押す
古いMacですが、Training Dataが2,836枚、Testing Dataが566枚で15分くらいで完了
作成したOutputファイルはドラッグ&ドロップで取り出せます


カメラ画像からリアルタイムで画像認識

環境
  • Xcode 11.4.1
  • Swift 5
  • iOS 13.4.1

参考にしたサイト

SceneDelegate.swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        //guard let _ = (scene as? UIWindowScene) else { return }
        
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        self.window = window
        window.rootViewController = ViewController(nibName: nil, bundle: nil)
        window.makeKeyAndVisible()
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }


}
UIButtonの場合
 
import UIKit
import AVFoundation
import Vision

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
    
    let label: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "Label"
        label.font = label.font.withSize(30)
        return label
    }()
    
    override func viewDidLoad() {
        // call the parent function
        super.viewDidLoad()
        
        // establish the capture session and add the label
        setupCaptureSession()
        view.addSubview(label)
        setupLabel()
    }
    
    override func didReceiveMemoryWarning() {
        // call the parent function
        super.didReceiveMemoryWarning()
        
        // Dispose of any resources that can be recreated.
    }
    
    func setupCaptureSession() {
        // create a new capture session
        let captureSession = AVCaptureSession()
        
        // find the available cameras
        let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices
        
        do {
            // select a camera
            if let captureDevice = availableDevices.first {
                captureSession.addInput(try AVCaptureDeviceInput(device: captureDevice))
            }
        } catch {
            // print an error if the camera is not available
            print(error.localizedDescription)
        }
        
        // setup the video output to the screen and add output to our capture session
        let captureOutput = AVCaptureVideoDataOutput()
        captureSession.addOutput(captureOutput)
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.frame
        view.layer.addSublayer(previewLayer)
        
        // buffer the video and start the capture session
        captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
        captureSession.startRunning()
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // load our CoreML Pokedex model
        guard let model = try? VNCoreMLModel(for: MobileNetV2().model) else { return }
        // run an inference with CoreML
        let request = VNCoreMLRequest(model: model) { (finishedRequest, error) in
            // grab the inference results
            guard let results = finishedRequest.results as? [VNClassificationObservation] else { return }
            
            // grab the highest confidence result
            guard let Observation = results.first else { return }
            
            // create the label text components
            let predclass = "\(Observation.identifier)"
            let predconfidence = String(format: "%.02f%", Observation.confidence * 100)
            // set the label text
            DispatchQueue.main.async(execute: {
                self.label.text = "\(predclass) \(predconfidence)"
            })
        }
        
        // create a Core Video pixel buffer which is an image buffer that holds pixels in main memory
        // Applications generating frames, compressing or decompressing video, or using Core Image
        // can all make use of Core Video pixel buffers
        guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        
        // execute the request
        try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
    }
    
    func setupLabel() {
        // constrain the label in the center
        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        // constrain the the label to 50 pixels from the bottom
        label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
    }
}

2016年1月7日木曜日

Add-Hocで公開

最近作ったアプリをAd-Hoc版としてwebからインストールできるようにした! Provisioning周りを久しぶりにいじってたけど、完全に忘れていた、、、

導入としては、下記リンク先の概念図が非常にわかりやすいと思う
http://qiita.com/fujisan3/items/d037e3c40a0acc46f618

iOS devの操作→Google Driveを使ってAd-Hoc公開までの手順は、
忘れないうちにまとめておこう…

2015年12月19日土曜日

UILabel, UIView, UIButtonの角を丸くする

UILabelとUIViewの場合
 
UILabel *label =  [UILabel alloc]init];
label.clipsToBounds = true;
label.layer.cornerRadius = 3.0f;

UIView *view = [UIView alloc]init];
view.clipsToBounds = true;
view.layer.cornerRadius = 3.0f;
UIButtonの場合
 
UIButton *button = [UIButton alloc]init];
button.layer.cornerRadius = 3.0f;

2015年12月14日月曜日

Xcodeで使える特殊タグ

// TODO: ToDoリストを記述する

// FIXME: 修正項目などを記述する

// !!!: 重要項目を記述する

// ???: 不明点を記述する

#pragma mark
//メソッドの区切りに使う

#warning 警告メッセージ

#error エラーメッセージ

2015年12月7日月曜日

継承したクラスのdelegateメソッドを表示

Xcodeを使ってて今更知ったこと

例えば、UITableViewを継承したカスタムクラスを作ったときに、
必要なdelegateメソッドを知る方法


「UITableViewDelegate」をcommandキーを押しながらクリック


必須のメソッドは
@required

オプションのメソッドは
@optional
で書いてあります。

2015年11月28日土曜日

新しいXcodeでカスタムセルを作ろうと…

久しぶりにアプリを作り始める

UITableViewCellを継承したカスタムセル作るぞー

 
Next(ぽち)

ん…?なんか違うような…
 
Next(ぽち)

えっ!!



正しくはこちら




 



2014年10月13日月曜日

UINavigationBarのtitle、backButtonのフォントを変える

まず、「AppDelegate.m」の『- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions』に、
 
    UIFont *barButtonTitleFont = [UIFont fontWithName:@"mplus-2p-light" size:13];
    UIColor *barButtonTitleColor = [UIColor whiteColor];
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{UITextAttributeFont:barButtonTitleFont, UITextAttributeTextColor:barButtonTitleColor} forState:UIControlStateNormal];
を記述。これで、BackButtonのフォントが変わる

次に、NavigationControllerの『ViewDidLoad』に、
    UILabel* label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 44)];
    label.backgroundColor = [UIColor clearColor];
    label.font = [UIFont fontWithName:@"mplus-2p-light" size:22.0f];
    label.textColor = [UIColor flatWhiteColor];
    label.text = @"タイトル";
    label.textAlignment = NSTextAlignmentCenter;
    self.navigationItem.titleView = label;
を記述。これで、タイトルのフォントが変わる。

それはそうと、m+フォントってとても美しいよね

アプリ起動時に指定した画像を表示させる

アプリを起動した時に指定した画像を表示させる
(Twitterの公式クライアントアプリとかであるやつ)
  1. プロジェクト中にある「images.xcassets」を開いて、起動時に表示したい画像をドラッグアンドドロップで設定する。

    ちなみに、必要な画像サイズはinspectorに書いてある。


  2. 「info.plist」に「Status bar is initially hidden」 という項目を追加して、Boolean型のYESに設定する。


  3. 同じく、「View controller-based status bar appearance」という項目を追加して、Boolean型のNOに設定する。
     これで、起動時からステータスバーが表示されなくなる。


  4.  「AppDelegate.m」の-『 (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions』に、
    [NSThread sleepForTimeInterval:1.0];
    [UIApplication sharedApplication].statusBarHidden = NO;
    
    を記述する。


    これで、起動から1秒間、設定した画像が表示される
    あとは、フェードアウトするアニメーションを追加したい・・・

2014年10月11日土曜日

インスタンスメソッドの定義方法

今更ながらObj-Cの文法まとめ
あってるかなあ?

きっちりしたインスタンス変数の定義
sampleClass.h
@interface sampleClass:NSObject
{
   NSString *_string; //インスタンス変数の定義
   double _number; //インスタンス変数の定義
}
@property (nonatomic) NSString* string; //プロパティを記述すると、getterとsetterが自動で生成される
@end
sampleClass.m
@implementation sampleClass

@synthesize string = _string; //インスタンス変数とプロパティを明示的に紐付ける

//プロパティを記述していない場合、自分でgetterとsetterを作る
- (double)number{
   return _number;
}
- (void)setNumber:(double)number{
   _number = number;
}
@end
setter/getterを自分で作る場合の利点は、setter/getter内に好きなことを記述できる
例えば、_stringをセットしたついでに、UILabel.textに_stringをセットしたり

・self.propertyの使いどころ
getter/setter/init/deallocのみ_propertyを使い、
その他はself.propertyを使う

2014年10月6日月曜日

JASidePanelControllerでサイドパネルを開くたびに呼ばれるメソッド

JASidePanelControllerを使ってみて、サイドパネルを開くたびにビューを更新しようと思ったけど、デフォルトでその機能がないみたいなのでメモ

JASidePanelController.hに
- (void)prepareShowRightPanel;
を追記

JASidePanelController.mで
- (void)_showRightPanel:(BOOL)animated bounce:(BOOL)shouldBounce {
    self.state = JASidePanelRightVisible;
    [self _loadRightPanel];
    
    [self _adjustCenterFrame];
    
    if (animated) {
        [self _animateCenterPanel:shouldBounce completion:nil];
    } else {
        self.centerPanelContainer.frame = _centerPanelRestingFrame; 
        [self styleContainer:self.centerPanelContainer animate:NO duration:0.0f];
        if (self.style == JASidePanelMultipleActive || self.pushesSidePanels) {
            [self _layoutSideContainers:NO duration:0.0f];
        }
    }
    
    if (self.style == JASidePanelSingleActive) {
        self.tapView = [[UIView alloc] init];
    }
    [self prepareShowRightPanel];//これを追加
    [self _toggleScrollsToTopForCenter:NO left:NO right:YES];
}
次に、JASidePanelControllerのサブクラスに
- (void)prepareShowRightPanel {
    NSLog(@"show right panel");
}
を記述

これで、右のサイドパネルを開くたびに
show right panel
とログが表示される

2014年9月29日月曜日

2014年9月7日日曜日

2014年6月8日日曜日

【HTMLParser】HTMLのパース

HTMLParserを使うときのメモ

#import "HTMLParser.h"でインポートして、
プロジェクトのGeneralを選択

その中のLinked Frameworks and Librariesにて、
libxml2.dylibを追加する

次は、Build SettingsのSerchPathsのHeader Search Pathsに
$(SDKROOT)/usr/include/libxml2を追加する
(行の最後に半角スペースを入れて↑をコピペ)

完成です

2014年6月6日金曜日

【HTML】テキストの画像回り込み

webページで、テキストを画像に回りこませる方法
http://www.keni-customize.net/img-mawarikomi-681/

<p><img src="hoge.jpg" class="alignleft" /></p>
<p class="mawarikomi">回りこませるテキスト</p>
<p class="clear">回り込み解除</p>


p.mawarikomi{
overflow: auto;
}

p.clear{
clear: both;
}


HTML初心者なので、htmlにスタイルを書き込む方法メモ
<div style = "overflow:auto; color:red;">hogehoge</div>



2014年5月13日火曜日

【Apache2】webページのBasic認証についてのメモ

.htaccessを有効にするためには、
httpd.confでAllowOverrideをOnにする

Ubuntuの場合は、httpd.confじゃなくて、
/etc/apache2/apache2.conf
です

2014年4月30日水曜日

【マルチスレッド】Objective Cのマルチスレッド実装方法

ObjCのマルチスレッドについてちょっと詳しくなったからメモ

1. GCDを使う
 前にも書いたけど、スレッドはGCDを使うのが楽
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
    [self doSomething]; //別スレッド
    dispatch_sync(dispatch_get_main_queue(), ^{
        [self doSomething]; // メインスレッド
    });
});
この場合、別スレッドに重い処理を書いて、メインスレッドはUIの描画が一般的らしい
PRIORITYで優先度も設定できる
そして、インスタンスメソッドはスレッドを指定して実行することができる
[self performSelectorOnMainThread:@selector(refresh)
                        withObject:nil
                    waitUntilDone:NO];
2. NSOperationを使う
NSOperationQueue *queue = [NSOperationQueue new];
    MyOperation *operation = [MyOperation new];//NSOperationを継承したクラスを作成
    [queue addOperation:operation];
 
3. NSInvocationOperationを使う
 この場合、NSOperationの継承クラスを作らなくてもいい
NSInvocationOperation *invOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(aaa) object:nil];
    [queue addOperation:invOperation];
4. とりあえずバックグラウンドに投げる
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{


        // ここに非同期でバックグラウンドに投げたい処理を書く

    });