关于ios:我可以使用Objective-C块作为属性吗?

Can I use Objective-C blocks as properties?

是否可以使用标准属性语法将块作为属性?

ARC有什么变化吗?


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

如果要在多个地方重复同一个块,请使用类型def

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


下面是您如何完成这项任务的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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)()。如果需要传递两个对象,请将其更改为:typedef int (^IntBlock)(id, id),并将块更改为:

1
2
3
4
^ (id obj1, id obj2)
{
    return rand();
};

我希望这有帮助。

编辑:2012年3月12日:

对于ARC,不需要进行特定的更改,因为ARC将为您管理块,只要它们被定义为"复制"。您也不需要在析构函数中将属性设置为nil。

欲了解更多信息,请查看此文档:http://clang.llvm.org/docs/automaticreferencecounting.html


对于swift,只需使用闭包:例如。

在Objtovi-C中,

@属性(副本)void(^dostuff)(void);

就这么简单。

苹果的文档,充分解释了这个问题:

苹果博士。

在.h文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 -(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+版)系统,请按此处所示操作。就是这么简单。希望它能帮助别人。2013年圣诞快乐!


为了子孙后代/完整性……这里有两个完整的例子,说明如何实现这种不可思议的通用"做事方式"。@罗伯特的答案非常简洁和正确,但在这里我还想展示如何真正"定义"这些块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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

傻?对。有用吗?地狱啊。这里有一种不同的"原子"设置属性的方法。还有一个非常有用的班级…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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内部,这是一种有争议的不确定做法)与第一个示例的"非原子的""getter"机制来设置block属性。在这两种情况下,…每个实例的"硬编码"实现总是可以被覆盖。一个…

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

也。。如果要在类别中添加块属性…比如说你想用一个街区而不是一些老学校的目标/动作"动作"…您可以使用关联的值来,好吧..关联块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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戏剧。只需将要在创建时完成的工作关联起来…

1
2
3
4
_button.actionBlock = ^(NSControl*thisButton){

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

这个模式可以反复应用到CocoaAPI中。使用属性使代码的相关部分更紧密地结合在一起,消除复杂的委托模式,并利用对象的力量,而不仅仅是充当一个愚蠢的"容器"。


当然,可以使用块作为属性。但请确保它们声明为@property(copy)。例如:

1
2
3
4
5
typedef void(^TestBlock)(void);

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

在MRC中,捕获上下文变量的块在堆栈中分配;当堆栈帧被破坏时,它们将被释放。如果复制了它们,则会在堆中分配一个新的块,稍后在弹出堆栈帧后可以执行该块。


诋毁者

这并不是"好答案",因为这个问题明确要求客观。当苹果在WWDC14上引入Swift时,我想分享在Swift中使用块(或闭包)的不同方法。

你好,Swift

您有许多方法可以通过一个相当于swift中函数的块。

我找到了三个。

为了理解这一点,我建议您在操场上测试这段代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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针对异步开发进行了优化,苹果在闭包方面做了更多的工作。第一个问题是可以推断出函数签名,因此不必重写它。

按数字访问参数

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

带命名的参数推断

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

尾部合拢

只有当块是最后一个参数时,这种特殊情况才有效,称为尾随闭包。

下面是一个例子(与推断的签名合并以显示迅速的力量)

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

最后:

使用所有这些电源,我将混合尾随闭包和类型推断(命名为可读性)。

1
2
3
4
5
6
7
8
9
10
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!")
    }
}


你好,Swift

补充Francescu @的回答。

添加额外参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)

您可以遵循下面的格式,并且可以在类中使用testingObjectiveCBlock属性。

1
2
3
4
5
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);

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

更多信息请看这里