0

NSURLConnectionのラッパーであるクラスを作成し、接続のさまざまな段階で呼び出されるクラスパスブロックのユーザーを作成しました。ラッパークラスを以下に示します。簡潔にするために省略します。

typedef void (^ConnectionFinishedWithErrorBlock)(NSError* error);

@interface MYHTTPConnection : NSObject <NSURLConnectionDelegate>
@property (copy, nonatomic)     ConnectionFinishedWithErrorBlock   theErrorBlock;
@property (strong, nonatomic)   NSURLConnection *connection;

- (void) establishConnectionForURL:(NSURL*) url 
      andConnectionFinishedWithErrorBlock: (ConnectionFinishedWithErrorBlock) errorBlock;
@end

これまで、インラインで宣言されたブロックで使用してきました。

[self.httpConnection establishConnectionForURL: url
            andConnectionFinishedWithErrorBlock:^(NSError* error)
            {
                ...
            }]; 

しかし、今では非常に長いエラー処理ブロックがあり、そのすべてのコードをインラインに配置すると面倒になります(APIはここに示されていない他のブロックを使用するため)。

私はこのようなことをすることができることを知っています:

void (^httpConnectionFinishedWithError)(NSError*) = 
^(NSError* error) 
{
}

次に、httpConnectionFinishedWithErrorを渡して、ConnectionForURLを確立します。ただし、httpConnectionFinishedWithErrorにはselfへの呼び出しが含まれています。ブロックがインラインで宣言されている場合、selfの呼び出しは問題ありませんが、上記のように行われた場合は、明らかにコンパイルエラーが発生するため、問題ありません。

だから私は、ブロックを呼び出し元のクラスのブロックプロパティにすることができるかどうか/どのようにできるのか疑問に思いましたか?(上記のMYHTTPConnectionクラスで行われているように、以前はブロックをクラスのプロパティとして使用しましたが、この状況では、establishConnectionForURLに渡されるブロックをのプロパティとして追加する構文を正しく取得できません。呼び出し元のクラス)。

または、httpConnectionFinishedWithErrorで別の関数を呼び出すこともできますが、その場合、selfをパラメーターとして渡す必要があります。この場合、ブロック内でselfを取得するにはどうすればよいですか。

他にもっとエレガントな解決策はありますか?

どうもありがとう

編集:コンパイルエラーの例を示すために更新されました

void (^httpConnectionFinishedWithError)(NSError*) = 
^(NSError* error) 
{
    self.boolProperty = NO; // here
}
4

2 に答える 2

2

ARCを使用している場合は、自己を参照する弱い変数を定義できます。

__weak ClassOfSelf *_self = self;

ブロックでそれを使用します。__blockARCを使用していない場合は、weakではなく使用したいと思います。どちらの場合も、ブロックは自己を保持しないため、保持サイクルを回避できます。

クラスでブロックを保存/使用するために私が知っている最良の方法は、最初にそれをtypedefすることです。

typedef void (^HttpConnectionFinishedWithError)(NSError*);

次に、ブロックストレージのインスタンス変数を作成します。

HttpConnectionFinishedWithError httpCompletionBlock;

initでブロックを作成し、それをインスタンス変数に割り当てます。最初にブロックをコピーしてヒープに移動することを忘れないでください(ブロックはスタック上に作成されます。つまり、作成されたスコープがなくなるとブロックは消えます)。

- (id) init{
  if (self = [super init]){
    __weak id _self = self; //or use class of self instead of id
    httpCompletionBlock = [^(NSError *err){
      .....code here, use _self instead of self
    } copy];
  }
  return self;
}

(ARCを使用した例)

httpCompletionBlockこれで、クラス内で通常のように使用できます。ブロックコードの_self代わりに使用し、ブロック内のインスタンス変数を直接参照ない限り、保持サイクルは作成されません。参照はと同じであることを忘れないでください。したがって、ブロックはをキャプチャします。代わりに:を使用するか、プロパティ/get-methodを作成してインスタンス変数にアクセスします。self_iVarself->_iVarself_self->_iVar

ARCを使用しているが、iOS 4をターゲットにしている場合は、利用できないため使用できません。また、ブロックがARCで変数を保持するのを妨げないため、__weak使用できません。__block代わりに、を使用できます__unsafe_unretained。変数が参照するオブジェクトが割り当て解除された場合、それ__unsafe_unretainedは解決されないことを覚えておいてください...したがって、安全ではありません。nil使い方には注意が必要です。たとえば、別のオブジェクトをこのブロックに渡すとself、割り当てが解除されます。_selfを参照すると、エラーが発生します。これを回避する方法は、ブロックが必要な間だけ自己を保持するインラインの「中間」ブロックを作成することです。たとえば、ブロックを直接渡す代わりに、次のようにします。

self.httpConnection establishConnectionForURL: url andConnectionFinishedWithErrorBlock:^(NSError* error)
            {
                httpCompletionBlock(error);
            }];

上記の場合、httpcompletionBlockはインスタンス変数であるため、selfgetはローカルスコープのブロックによってキャプチャされます。これで、実行されるselfまで消えることはありません。保存されたブロックでhttpCompletionBlockはなく、インラインブロックのみが保持されるため、保持サイクルを回避できます。self

于 2012-05-24T19:12:40.333 に答える
0

プロパティを使用する代わりに、次のようにメソッドからブロックを返すこともできます。

-(ConnectionFinishedWithErrorBlock)connectionFinishedWithErrorBlock {
    return ^(NSError* error) {
        self.boolProperty = NO;
    };
}

[self.httpConnection establishConnectionForURL:url
           andConnectionFinishedWithErrorBlock:[self connectionFinishedWithErrorBlock]]; 

クラスがブロックのインスタンスを保持していないため、保持サイクルはありません。httpConnectionコールバック後にブロックを破棄せず、オブジェクトがリークするリスクがあります。何も所有していない場合httpConnectionは、そのセマンティクスが何であるかを理解し、Instrumentsでテストして、接続終了ブロックがリークしないことを確認してください。

于 2012-05-25T17:32:11.797 に答える