プロパティとしてObjective-Cブロックを使用できますか?


321

標準のプロパティ構文を使用してプロパティとしてブロックを持つことは可能ですか?

ARCに変更はありますか?


1
まあ、とても重宝するから。構文が正しく、NSObjectのように動作する限り、それが何であるかを知る必要はありません。
gurghet 2010年

5
それが何であるかわからない場合、それが非常に便利であることをどのようにして知っていますか?
スティーブンキャノン

5
それらが何であるかわからない場合は、使用しないでください:)
リチャードJ.ロスIII

5
@Mosheがここで頭に浮かぶいくつかの理由があります。ブロックは、完全なデリゲートクラスよりも実装が簡単であり、ブロックは軽量であり、そのブロックのコンテキスト内にある変数にアクセスできます。イベントコールバックは、ブロックを使用して効果的に実行できます(cocos2dは、ほとんど排他的にブロックを使用します)。
リチャードJ.ロスIII

2
完全に関連しているわけではありませんが、一部のコメントは「醜い」ブロック構文について不平を言っているので、ここに構文を第一原理から導き出した素晴らしい記事があります:nilsou.com/blog/2013/08/21/objective-c-blocks-syntax
ポールレクグラー2013

回答:


305
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);

複数の場所で同じブロックを繰り返す場合は、タイプdefを使用します

typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;

3
xCode 4.4以降では、合成する必要はありません。これにより、さらに簡潔になります。Apple Doc
Eric

うわー、知らなかった、ありがとう!...私はよくしますが@synthesize myProp = _myProp
ロバート

7
@Robert:あなたが原因かけることなく、再び運にある@synthesizeデフォルトでは、あなたがやっていることである@synthesize name = _name; stackoverflow.com/a/12119360/1052616
エリック・

1
@CharlieMonroe-はい、おそらく正しいですが、ARCなしでブロックプロパティをnilしたり解放したりするには、dealloc実装が必要ですか?(私が非ARCを使用してからしばらく経っています)
Robert

1
@imcaptor:はい、他の変数と同様に、deallocで解放しないと、メモリリークが発生する可能性があります。
チャーリーモンロー2013年

210

このようなタスクを実行する方法の例を次に示します。

#import <Foundation/Foundation.h>
typedef int (^IntBlock)();

@interface myobj : NSObject
{
    IntBlock compare;
}

@property(readwrite, copy) IntBlock compare;

@end

@implementation myobj

@synthesize compare;

- (void)dealloc 
{
   // need to release the block since the property was declared copy. (for heap
   // allocated blocks this prevents a potential leak, for compiler-optimized 
   // stack blocks it is a no-op)
   // Note that for ARC, this is unnecessary, as with all properties, the memory management is handled for you.
   [compare release];
   [super dealloc];
}
@end

int main () {
    @autoreleasepool {
        myobj *ob = [[myobj alloc] init];
        ob.compare = ^
        {
            return rand();
        };
        NSLog(@"%i", ob.compare());
        // if not ARC
        [ob release];
    }

    return 0;
}

ここで、比較のタイプを変更する必要がある場合に変更する必要があるのは、だけですtypedef int (^IntBlock)()。2つのオブジェクトを渡す必要がある場合はtypedef int (^IntBlock)(id, id)、次のように変更し、ブロックを次のように変更します。

^ (id obj1, id obj2)
{
    return rand();
};

これがお役に立てば幸いです。

2012年3月12日編集:

ARCでは、コピーとして定義されている限り、ARCがブロックを管理するため、特別な変更は必要ありません。デストラクタでプロパティをnilに設定する必要もありません。

詳細については、次のドキュメントをご覧ください。http//clang.llvm.org/docs/AutomaticReferenceCounting.html


158

Swiftの場合は、クロージャーを使用するだけです:例。


Objective-Cの場合:

@property(コピー)void

@property (copy)void (^doStuff)(void);

とても簡単です。

これが実際のAppleのドキュメントであり、何を使用するかを正確に示しています。

アップルドコ。

.hファイルで:

// Here is a block as a property:
//
// Someone passes you a block. You "hold on to it",
// while you do other stuff. Later, you use the block.
//
// The property 'doStuff' will hold the incoming block.

@property (copy)void (^doStuff)(void);

// Here's a method in your class.
// When someone CALLS this method, they PASS IN a block of code,
// which they want to be performed after the method is finished.

-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater;

// We will hold on to that block of code in "doStuff".

これが.mファイルです。

 -(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater
    {
    // Regarding the incoming block of code, save it for later:
    self.doStuff = pleaseDoMeLater;

    // Now do other processing, which could follow various paths,
    // involve delays, and so on. Then after everything:
    [self _alldone];
    }

-(void)_alldone
    {
    NSLog(@"Processing finished, running the completion block.");
    // Here's how to run the block:
    if ( self.doStuff != nil )
       self.doStuff();
    }

古いコード例に注意してください。

最新の(2014+)システムで、ここに示されていることを実行します。とても簡単です。


たぶん、今(2016年)のstrong代わりに使用してもよいということも言う必要がありますcopyか?
Nik Kov 2016年

プロパティがプロパティをnonatomic使用する他のほとんどの場合のベストプラクティスと異なるべきではない理由を説明できますか?
Alex Pretzlav 2016年

AppleのWorkingwithBlocks.html "プロパティ属性としてコピーを指定する必要があります。理由は..."
Fattie

20

後世のために/完全性のために…これは、途方もなく多用途な「物事のやり方」を実装する方法の2つの完全な例です。@ロバートの答えは至福のように簡潔で正しいですが、ここでは実際にブロックを「定義」する方法も示したいと思います。

@interface       ReusableClass : NSObject
@property (nonatomic,copy) CALayer*(^layerFromArray)(NSArray*);
@end

@implementation  ResusableClass
static  NSString const * privateScope = @"Touch my monkey.";

- (CALayer*(^)(NSArray*)) layerFromArray { 
     return ^CALayer*(NSArray* array){
        CALayer *returnLayer = CALayer.layer
        for (id thing in array) {
            [returnLayer doSomethingCrazy];
            [returnLayer setValue:privateScope
                         forKey:@"anticsAndShenanigans"];
        }
        return list;
    };
}
@end

愚かな?はい。 有用?ええヘルズ。 ここに、プロパティを設定する別の「よりアトミックな」方法と、とんでもなく便利なクラスがあります…

@interface      CALayoutDelegator : NSObject
@property (nonatomic,strong) void(^layoutBlock)(CALayer*);
@end

@implementation CALayoutDelegator
- (id) init { 
   return self = super.init ? 
         [self setLayoutBlock: ^(CALayer*layer){
          for (CALayer* sub in layer.sublayers)
            [sub someDefaultLayoutRoutine];
         }], self : nil;
}
- (void) layoutSublayersOfLayer:(CALayer*)layer {
   self.layoutBlock ? self.layoutBlock(layer) : nil;
}   
@end

これは、最初の例の「非アトミック」「ゲッター」メカニズムと比較して、アクセサを介してブロックプロパティを設定することを示しています(ただし、initの内部では、議論の余地なく危険な慣行です)。どちらの場合でも、「ハードコードされた」実装は、インスタンスごとに常に上書きできます。

CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
  [layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;

また、カテゴリにブロックプロパティを追加したい場合は、昔ながらのターゲット/アクション「アクション」の代わりにブロックを使用したいと言います。関連付けられた値を使用することもできます。ブロックを関連付けます。

typedef    void(^NSControlActionBlock)(NSControl*); 
@interface       NSControl            (ActionBlocks)
@property (copy) NSControlActionBlock  actionBlock;    @end
@implementation  NSControl            (ActionBlocks)

- (NSControlActionBlock) actionBlock { 
    // use the "getter" method's selector to store/retrieve the block!
    return  objc_getAssociatedObject(self, _cmd); 
} 
- (void) setActionBlock:(NSControlActionBlock)ab {

    objc_setAssociatedObject( // save (copy) the block associatively, as categories can't synthesize Ivars.
    self, @selector(actionBlock),ab ,OBJC_ASSOCIATION_COPY);
    self.target = self;                  // set self as target (where you call the block)
    self.action = @selector(doItYourself); // this is where it's called.
}
- (void) doItYourself {

    if (self.actionBlock && self.target == self) self.actionBlock(self);
}
@end

ボタンを作成するときに、IBActionドラマを設定する必要はありません。作成時に行う作業を関連付けるだけです...

_button.actionBlock = ^(NSControl*thisButton){ 

     [doc open]; [thisButton setEnabled:NO]; 
};

このパターンは、OVERおよびOVERを Cocoa APIに適用できます。プロパティを使用して、コードの関連部分を互いに近づけ複雑な委任パラダイムを排除し、オブジェクトの能力を、単なる「コンテナ」として機能する能力を超えて活用します。


アレックス、素晴らしい関連例。あなたは知っている、私は非原子について疑問に思っている。考え?
Fattie、2014

2
「アトミック」がプロパティに対して行う正しいことであることは非常にまれです。あるスレッドでブロックプロパティを設定し、それを別のスレッドで同時に読み取るか、複数のスレッドから同時にブロックプロパティを設定するのは非常に奇妙なことです。したがって、「アトミック」と「非アトミック」のコストは、実際の利点にはなりません。
gnasher729 2014年

8

もちろん、ブロックをプロパティとして使用することもできます。ただし、それらが@property(copy)として宣言されていることを確認してください。例えば:

typedef void(^TestBlock)(void);

@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end

MRCでは、コンテキスト変数をキャプチャするブロックはスタックに割り当てられます。スタックフレームが破壊されると解放されます。それらがコピーされると、新しいブロックがヒープに割り当てられ、スタックフレームがポップされた後で実行できます。


丁度。コピーだけを使用する理由についての実際のApple docoを次に示します。 developer.apple.com/library/ios/documentation/cocoa/conceptual/...
Fattie

7

Disclamer

この質問はObjectiveCに明示的に要求するため、これは「良い答え」になることを意図したものではありません。AppleがWWDC14でSwiftを紹介したので、Swiftでブロック(またはクロージャー)を使用するさまざまな方法を共有したいと思います。

こんにちは、スイフト

Swiftのfunctionと同等のブロックを渡す方法はたくさんあります。

3つ見つけました。

これを理解するには、プレイグラウンドでこの小さなコードをテストすることをお勧めします。

func test(function:String -> String) -> String
{
    return function("test")
}

func funcStyle(s:String) -> String
{
    return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle)

let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle)

let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" })


println(resultFunc)
println(resultBlock)
println(resultAnon)

Swift、クロージャー用に最適化

Swiftは非同期開発用に最適化されているため、Appleはクロージャーにさらに取り組みました。1つ目は、関数のシグネチャを推測できるため、書き換える必要がないことです。

数値でパラメータにアクセスする

let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })

名前付けによるパラメーターの推論

let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })

末尾の閉鎖

この特別なケースは、ブロックが最後の引数である場合にのみ機能します。これは、トレーリングクロージャーと呼ばれます。

ここに例があります(Swiftパワーを示すために推定署名とマージされます)

let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }

最後に:

このすべての力を使用して、私がやろうとしていることは、末尾のクロージャーと型推論を(読みやすいように名前を付けて)混合することです

PFFacebookUtils.logInWithPermissions(permissions) {
    user, error in
    if (!user) {
        println("Uh oh. The user cancelled the Facebook login.")
    } else if (user.isNew) {
        println("User signed up and logged in through Facebook!")
    } else {
        println("User logged in through Facebook!")
    }
}

0

こんにちは、スイフト

@Francescuの回答を補完する。

追加のパラメーターを追加する:

func test(function:String -> String, param1:String, param2:String) -> String
{
    return function("test"+param1 + param2)
}

func funcStyle(s:String) -> String
{
    return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle, "parameter 1", "parameter 2")

let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle, "parameter 1", "parameter 2")

let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" }, "parameter 1", "parameter 2")


println(resultFunc)
println(resultBlock)
println(resultAnon)

-3

以下の形式に従ってtestingObjectiveCBlock、クラスのプロパティを使用できます。

typedef void (^testingObjectiveCBlock)(NSString *errorMsg);

@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end

詳細はこちらをご覧ください


2
この回答は、すでに提供されている他の回答に本当に何かを追加しますか?
リチャードJ.ロスIII 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.