我可以使用 Objective-C block 作为属性吗?

是否能使用标准属性语法将 blocks 作为属性来用?

ARC有任何变化吗?

124597 次浏览

下面是一个如何完成这样一个任务的例子:

#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),并将块更改为:

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

我希望这能有所帮助。

2012年3月12日编辑:

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

欲了解更多内容,请查看此文档: http://clang.llvm.org/docs/AutomaticReferenceCounting.html < / p >

@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;

为了子孙后代/完整性的缘故,这里有两个完整的例子,如何实现这种荒谬的多功能“做事方式”。@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

傻吗?是的。有用吗?这里有一个不同的,“更原子”的设置属性的方法。还有一个非常有用的类……

@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中,这是一个有争议的冒险实践..)设置block属性与第一个例子的“非原子”“getter”机制。在任何一种情况下,“硬编码”的实现总是可以被覆盖,每个实例..一个拉. .

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

也. .如果你想在一个类别中添加一个block属性…比如你想要使用Block而不是一些老式的目标/动作“action”…你可以使用相关的值。关联这些块。

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。使用属性将代码的相关部分得更近,消除复杂的委托范例,并利用对象的强大功能,而不仅仅是充当愚蠢的“容器”。

@ property(副本)无效

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

就是这么简单。

以下是实际的苹果文档,它精确地说明了使用什么:

Apple doco.

你的。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+)系统,执行如下所示的操作。就是这么简单。

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

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


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

有关更多信息,请查看在这里

Disclamer

这并不是“好的答案”,因为这个问题明确地要求objective - c。苹果在WWDC14上介绍了Swift,我想分享一下在Swift中使用block(或闭包)的不同方式。

你好,迅速

在Swift中,你有很多方法来传递一个相当于函数的块。

我找到了三个。

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

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针对异步开发进行了优化,苹果在闭包方面投入了更多精力。 首先,函数签名可以被推断出来,所以你不必重写它。< / p >

通过数字访问参数

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!")
}
}

你好,迅速

补充@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)

当然,您可以使用块作为属性。但是要确保它们被声明为@ property(复制)。例如:

typedef void(^TestBlock)(void);


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

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