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; } |
现在,如果需要更改比较类型,唯一需要更改的是
1 2 3 4 |
我希望这有帮助。
编辑: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 |
现在,当你做一个按钮,你不必设置一些
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) |
您可以遵循下面的格式,并且可以在类中使用
1 2 3 4 5 | typedef void (^testingObjectiveCBlock)(NSString *errorMsg); @interface MyClass : NSObject @property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock; @end |
更多信息请看这里