327

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

ARCに変更はありますか?

4

8 に答える 8

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

複数の場所で同じブロックを繰り返す場合は、型定義を使用します

typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
于 2012-10-23T15:20:35.587 に答える
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 つのオブジェクトを渡す必要がある場合は、これを this:typedef int (^IntBlock)(id, id)に変更し、ブロックを次のように変更します。

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

これが役立つことを願っています。

編集 2012 年 3 月 12 日:

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

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

于 2010-10-14T17:09:29.467 に答える
20

後世/完全性のために… このばかばかしいほど多目的な「物事のやり方」を実装する方法の完全な例を2つ示します。@Robertの答えは至福の簡潔で正しいですが、ここではブロックを実際に「定義」する方法も示したいと思います。

@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

馬鹿な?はい。 使える?ええ。 これは、プロパティを設定する別の「よりアトミックな」方法です..そして、途方もなく便利なクラスです.</p>

@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 内部ではありますが、議論の余地がある危険な慣行です..)。どちらの場合でも…「ハードコードされた」実装は、インスタンスごとにいつでも上書きできます.. a lá..

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]; 
};

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

于 2013-05-31T21:11:28.527 に答える
8

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

typedef void(^TestBlock)(void);

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

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

于 2015-07-16T09:08:50.417 に答える
6

免責事項

この質問は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!")
    }
}
于 2014-06-17T13:46:08.990 に答える
-1

こんにちは、スイフト

@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)
于 2014-07-21T17:56:53.583 に答える
-4

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

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

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

詳細については、こちらをご覧ください

于 2014-01-24T06:41:20.500 に答える