IOS学習ノート--Objective-Cのプロトコル、コードブロック、分類

17753 ワード

概要
ObjCの構文は主にsmalltalkに基づいて設計されており,従来のオブジェクト向け特性を提供するほか,多くの他の特性が追加されているが,この節ではObjCでよく用いられるいくつかの構文特性に重点を置く.もちろんこれらの内容は他の高級言語の命名とは違いますが、私たちはその中で彼らの影を見つけることができます.文章の中で私も他の言語と比較して紹介します.この節の重点内容は以下の通りです.(原文の住所:http://www.cnblogs.com/kenshincui/p/3869639.html)
  • プロトコルprotocol
  • ブロックブロックブロック
  • 分類category

  • プロトコルprotocol
    ObjCで@protocolを使用してメソッド仕様のセットを定義し、このプロトコルを実装するクラスは対応するメソッドを実装する必要があります.オブジェクト向けの子供靴を熟知していると、インタフェース自体がオブジェクトの動作記述のプロトコル規範であることがわかります.つまり、ObjCでは@protocolと他の言語のインタフェース定義は類似していますが、ObjCではinterfaceキーワードがクラス定義に使用されているため、C#、Javaではinterface定義インタフェースを使用することはありません.
    動物のプロトコルAnimalDelegateを定義したとします.Personというクラスはこのプロトコルを実現する必要があります.次のコードを見てください.
    AnimalDelegate.h
    //
    
    //  AnimalDelegate.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    
    
    //      
    
    @protocol AnimalDelegate <NSObject> @required //        -(void)eat; @optional //        -(void)run; -(void)say; -(void)sleep; @end

     
    Person.h
    //
    
    //  Person.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> #import "AnimalDelegate.h" @interface Person : NSObject<AnimalDelegate> -(void)eat; @end

    Person.m
    //
    
    //  Person.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import "Person.h" @implementation Person -(void)eat{ NSLog(@"eating..."); } @end

    ここでは、次の点について説明します.
  • あるプロトコルは別のプロトコルから拡張することができ、例えば上のAnimalDelegateはNSObjectから拡張し、複数のプロトコルを拡張する必要がある場合はカンマで区切る.
  • と他の高度な言語のインタフェースが異なるのは、プロトコルで定義された方法が必ずしも実現されなければならないとは限らない.キーワードで@requiredと@optionalを設定することができ、設定しなければデフォルトは@requiredである(ObjCは弱い構文であることに注意し、必須の方法のコンパイル実行を実現しなくてもエラーはならない).
  • プロトコルは<>によって実現され、1つのクラスは同時に複数のプロトコルを実現することができ、中間はカンマで区切られる.
  • プロトコルの実装はクラスの宣言のみであり、クラスの実装には入れない(すなわち@interface Person:NSObjectと書かなければならず、@implementation Person).
  • プロトコルでは属性、メンバー変数などを定義できず、方法しか定義できない.

  • 実際にObjCでのプロトコルの多くの役割は、クラスが何らかの方法を実装しなければならないことを制約することであり、オブジェクト向けの観点から、このクラスとインタフェースには必ずしも自然な関係があるとは限らず、完全に異なる意味での2つのものである可能性があります.このモードはエージェントモード(Delegation)と呼ばれています.Cocoaフレームワークでは,このモードを多く採用してデータとUIの分離を実現し,基本的にすべてのプロトコルがDelegateで終わる.
    次に、ボタンを設計する必要があると仮定します.ボタンはすべてクリックする必要があることを知っています.他の言語では、ユーザーがクリックイベントを購読すれば、クリック時にこのイベントの実行がトリガーされます(これはオブジェクト間のデカップリングの方法です:コード注入).しかしObjCではイベントの定義はなく,エージェントを用いてこの問題を扱う.まず、ボタン内のボタンのエージェントを定義し、プロトコルを使用してこのエージェント(イベントのトリガ)がプロトコル内のいくつかの方法を実装しなければならないことを制約し、ボタン処理中にエージェントがこの方法を実装したかどうかを確認し、実装した場合にこの方法を呼び出す.
    KCButton.h
    //
    
    //  KCButton.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> @class KCButton; //             ,  KCButtonDelegate   NSObject   @protocol KCButtonDelegate <NSObject> @required //@required          -(void)onClick:(KCButton *)button; @optional //@optional            -(void)onMouseover:(KCButton *)button; -(void)onMouseout:(KCButton *)button; @end @interface KCButton : NSObject #pragma mark -    #pragma mark     ,               KCButtonDelegate   @property (nonatomic,retain) id<KCButtonDelegate> delegate; #pragma mark -      #pragma mark      -(void)click; @end

    KCButton.m
    //
    
    //  KCButton.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import "KCButton.h" @implementation KCButton -(void)click{ NSLog(@"Invoke KCButton's click method."); //  _delegate       onClick:  (      "onClick:",    :) //     ButtonDelegate     KCButton    if([_delegate respondsToSelector:@selector(onClick:)]){ [_delegate onClick:self]; } } @end

    MyListener.h
    //
    
    //  MyListener.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> @class KCButton; @protocol KCButtonDelegate; @interface MyListener : NSObject<KCButtonDelegate> -(void)onClick:(KCButton *)button; @end

    MyListener.m
    //
    
    //  MyListener.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import "MyListener.h" #import "KCButton.h" @implementation MyListener -(void)onClick:(KCButton *)button{ NSLog(@"Invoke MyListener's onClick method.The button is:%@.",button); } @end

    main.m
    //
    
    //  main.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> #import "KCButton.h" #import "MyListener.h" int main(int argc, const char * argv[]) { @autoreleasepool { KCButton *button=[[KCButton alloc]init]; MyListener *listener=[[MyListener alloc]init]; button.delegate=listener; [button click]; /*   : Invoke KCButton's click method. Invoke MyListener's onClick method.The button is:<KCButton: 0x1001034c0>. */ } return 0; }

    Javaにおけるイベントの実装メカニズムに少し似たボタンのクリックプロセスを例によってシミュレートした.この例では、次の点に注意する必要があります.
  • idは、任意のObjCオブジェクトタイプを表すことができ、タイプの後にある"<プロトコル名>"このプロパティとして制約するオブジェクトは、プロトコルを実装する必要があります(注意:idで定義されたオブジェクトタイプには"*"を追加する必要はありません).
  • MyListenerはイベントトリガーとしてKCButtonDelegateエージェント(ObjCにはネーミングスペースやパッケージの概念がなく、通常は接頭辞でクラスの区分を行い、「KC」は私たちがカスタマイズした接頭辞)
  • を実現した.
  • .hファイルで別のファイルのクラスまたはプロトコルを使用している場合は、@classまたは@protocolで宣言し、このファイルをインポートする必要がなく、コンパイル効率を向上させることができます.(注意@classまたは@protocolを使用する必要がある場合があります.たとえば、上のKCButton.hで宣言したKCButtonDelegateプロトコルではKCButtonクラスが使用されていますが、このファイルの下のKCButtonクラス宣言ではKCButtonDelegateが使用されており、1つのファイルで相互に使用関係が形成されています.この場合、@classまたは@protocol宣言を使用する必要があります.そうしないとコンパイルフェーズでエラーが発生します).を選択します.ただし、.mファイルでは、対応するクラス宣言ファイルまたはプロトコルファイルをインポートする必要があります(インポートしない場合は、構文チェックは通過できますが、コンパイルリンクはエラーを報告します).
  • respondsToSelectorメソッドを使用して、オブジェクトがメソッドを実装したかどうかを判断できます(メソッド名は「onClick」ではなく「onClick:」、コロンもメソッド名の一部です).
  • 属性の(nonatomic,retain)はこの文章の重点ではありません.次の文章では具体的に紹介します.
    コードブロックBlock
    C#非同期プログラミングでは、関数コールバックが頻繁に行われます.関数コールは非同期で実行されるため、ある操作を実行した後に別の関数を実行させたい場合は、前の方法がいつ実行されるか分からないため、通常のコードの書く順序でプログラミングすることはできません.匿名委任やlambda式でこの操作はパラメータとして伝達されます.実はObjCにも似たような方法があり、コードブロック(Block)と呼ばれています.Blockは関数体(匿名関数)で、ObjCが閉パケットの実現に対して、ブロック内で局所変数を保持または参照することができます(lambda式を思い浮かべます).Blockを使用すると、1つの操作をパラメータとして渡すことができます(C言語の関数ポインタを思い出したのではないでしょうか).次の例では、Blockを使用して、上記のクリックリスニング操作を実現します.
    KCButton.h
    //
    
    //  KCButton.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> @class KCButton; typedef void(^KCButtonClick)(KCButton *); @interface KCButton : NSObject #pragma mark -    #pragma mark        @property (nonatomic,copy) KCButtonClick onClick; //                //@property (nonatomic,copy) void(^ onClick)(KCButton *); #pragma mark -      #pragma mark      -(void)click; @end

    KCButton.m
    //
    
    //  KCButton.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import "KCButton.h" @implementation KCButton -(void)click{ NSLog(@"Invoke KCButton's click method."); if (_onClick) { _onClick(self); } } @end

    main.m
    //
    
    //  main.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> #import "KCButton.h" int main(int argc, const char * argv[]) { KCButton *button=[[KCButton alloc]init]; button.onClick=^(KCButton *btn){ NSLog(@"Invoke onClick method.The button is:%@.",btn); }; [button click]; /*  : Invoke KCButton's click method. Invoke onClick method.The button is:<KCButton: 0x1006011f0>. */ return 0; }

    上のコードではBlockと同様にボタンのクリックイベントが実現されており、Blockについては以下のようにまとめられている.
  • Blockタイプ定義:戻り値タイプ(^変数名)(パラメータリスト)(Blockもタイプであることに注意);
  • Blockのtypedef定義:戻り値タイプ(^タイプ名)(パラメータリスト);
  • Blockの実装:^(パラメータリスト){操作本体};
  • Blockではブロック外で定義された変数を読み取ることができるが、修正できない.修正する場合、この変数は_block修飾を宣言しなければならない.
  • 分類Category
    既存のコードを1つのクラスに変更せずに他の機能を拡張する場合は、このクラスを継承して実装することも考えられますが、これを使用する場合は、拡張の新しい機能を持つために新しい実装のサブクラスとして定義する必要があります.既存のクラスを変更せずに新しい機能を拡張し、使用時に新しいタイプを定義する必要はありませんか?C#で使用できる場合拡張方法は、ObjCにおいても同様の実装があり、Categoryを分類することである.分類を利用すると、ObjCにおいて既存のクラスに新しい動作(特にシステムやフレームワークのクラス)を動的に追加することができる.C#には文字列にTrim()があるメソッドは文字列の前後のスペースを削除するために使用され、特に便利ですが、ObjCではこのメソッドはありません.ここではCategoryを通じてNSStringにstringByTrim()メソッドを追加してもいいです.
    NSString+Extend.h
    //
    
    //  NSString+Extend.h
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> @interface NSString (Extend) -(NSString *)stringByTrim; @end

    NSString+Extend.m
    //
    
    //  NSString+Extend.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import "NSString+Extend.h" @implementation NSString (Extend) -(NSString *)stringByTrim{ NSCharacterSet *character= [NSCharacterSet whitespaceCharacterSet]; return [self stringByTrimmingCharactersInSet:character]; } @end

    main.m
    //
    
    //  main.m
    
    //  Protocol&Block&Category
    
    //
    
    //  Created by Kenshin Cui on 14-2-2.
    
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    
    //
    
    
    
    #import <Foundation/Foundation.h> #import "NSString+Extend.h" int main(int argc, const char * argv[]) { NSString *name=@" Kenshin Cui "; name=[name stringByTrim]; NSLog(@"I'm %@!",name); //  :I'm Kenshin Cui! return 0; }

    以上の出力結果から、@"Kenshin Cui"の両端のスペースを削除することに成功したことがわかります.分類ファイル名は一般的に「既存クラス名+分類名」であり、分類の定義は既存クラス名の後に」(分類名)を付けることによって定義されています(宣言ファイル.hと実装ファイル.mはいずれもそうであることに注意).