ios 我可以使用 Objective-C 块作为属性吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3935574/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I use Objective-C blocks as properties?
提问by gurghet
Is it possible to have blocks as properties using the standard property syntax?
是否可以使用标准属性语法将块作为属性?
Are there any changes for ARC?
ARC有什么变化吗?
采纳答案by Robert
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
If you are going to be repeating the same block in several places use a type def
如果您要在多个地方重复相同的块,请使用类型 def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
回答by Richard J. Ross III
Here's an example of how you would accomplish such a task:
以下是如何完成此类任务的示例:
#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;
}
Now, the only thing that would need to change if you needed to change the type of compare would be the typedef int (^IntBlock)()
. If you need to pass two objects to it, change it to this: typedef int (^IntBlock)(id, id)
, and change your block to:
现在,如果您需要更改比较类型,唯一需要更改的就是typedef int (^IntBlock)()
. 如果您需要将两个对象传递给它,请将其更改为:typedef int (^IntBlock)(id, id)
,并将您的块更改为:
^ (id obj1, id obj2)
{
return rand();
};
I hope this helps.
我希望这有帮助。
EDIT March 12, 2012:
2012 年 3 月 12 日编辑:
For ARC, there are no specific changes required, as ARC will manage the blocks for you as long as they are defined as copy. You do not need to set the property to nil in your destructor, either.
对于 ARC,不需要进行特定更改,因为只要将块定义为副本,ARC 就会为您管理它们。您也不需要在析构函数中将该属性设置为 nil。
For more reading, please check out this document: http://clang.llvm.org/docs/AutomaticReferenceCounting.html
如需更多阅读,请查看此文档:http: //clang.llvm.org/docs/AutomaticReferenceCounting.html
回答by Fattie
For Swift, just use closures: example.
对于 Swift,只需使用闭包:示例。
In Objective-C:
在 Objective-C 中:
@property (copy)void
@property (copy)void
@property (copy)void (^doStuff)(void);
It's that simple.
就这么简单。
Here is the actual Apple documentation, which states precisely what to use:
这是实际的 Apple 文档,其中准确说明了要使用的内容:
In your .h file:
在您的 .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".
Here's your .m file:
这是您的 .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();
}
Beware of out-of-date example code.
当心过时的示例代码。
With modern (2014+) systems, do what is shown here. It is that simple.
使用现代 (2014+) 系统,执行此处显示的操作。就是这么简单。
回答by Alex Gray
For posterity / completeness's sake… Here are two FULL examples of how to implement this ridiculously versatile "way of doing things". @Robert's answer is blissfully concise and correct, but here I want to also show ways to actually "define" the blocks.
为了后代/完整性......这里有两个完整的例子,说明如何实现这种可笑的多功能“做事方式”。@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
Silly? Yes.Useful? Hells yeah.Here is a different, "more atomic" way of setting the property.. and a class that is ridiculously useful…
愚蠢的?是的。有用?地狱是的。这是一种不同的、“更原子”的设置属性的方式......以及一个非常有用的类......
@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
This illustrates setting the block property via the accessor (albeit inside init, a debatably dicey practice..) vs the first example's "nonatomic" "getter" mechanism. In either case… the "hardcoded" implementations can always be overwritten, per instance.. a lá..
这说明了通过访问器设置块属性(尽管在 init 内部,这是一个有争议的冒险实践......)与第一个示例的“非原子”“getter”机制。在任何一种情况下......“硬编码”实现总是可以被覆盖,每个实例......
CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
[layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Also.. if you want to add a block property in a category... say you want to use a Block instead of some old-school target / action "action"... You can just use associated values to, well.. associate the blocks.
另外..如果你想在一个类别中添加一个块属性......假设你想使用一个块而不是一些老式的目标/动作“动作”......你可以只使用关联的值来......关联块。
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
Now, when you make a button, you don't have to set up some IBAction
drama.. Just associate the work to be done at creation...
现在,当您制作按钮时,您不必设置一些IBAction
戏剧..只需将创建时要完成的工作关联起来......
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
This pattern can be applied OVER and OVER toCocoa API's. Use properties to bring the relevant parts of your code closer together, eliminate convoluted delegation paradigms, and leverage the power of objects beyond that of just acting as dumb "containers".
这种模式可以应用于Cocoa API 的OVER 和 OVER。使用属性将代码的相关部分更紧密地结合在一起,消除复杂的委托范式,并利用对象的强大功能,而不仅仅是充当愚蠢的“容器”。
回答by Mindy
Of course you could use blocks as properties. But make sure they are declared as @property(copy). For example:
当然,您可以使用块作为属性。但请确保将它们声明为@property(copy)。例如:
typedef void(^TestBlock)(void);
@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
In MRC, blocks capturing context variables are allocated in stack; they will be released when the stack frame is destroyed. If they are copied, a new block will be allocated in heap, which can be executed later on after the stack frame is poped.
在 MRC 中,捕获上下文变量的块分配在堆栈中;当堆栈帧被销毁时,它们将被释放。如果它们被复制,将在heap 中分配一个新块,可以在堆栈帧弹出后稍后执行。
回答by Francescu
Disclamer
免责声明
This is not intended to be "the good answer", as this question ask explicitly for ObjectiveC. As Apple introduced Swift at the WWDC14, I'd like to share the different ways to use block (or closures) in Swift.
这不是“好的答案”,因为这个问题明确要求 ObjectiveC。由于 Apple 在 WWDC14 上介绍了 Swift,我想分享在 Swift 中使用块(或闭包)的不同方式。
Hello, Swift
你好,斯威夫特
You have many ways offered to pass a block equivalent to function in Swift.
您可以通过多种方式传递与 Swift 中的函数等效的块。
I found three.
我找到了三个。
To understand this I suggest you to test in playground this little piece of code.
要理解这一点,我建议您在操场上测试这段代码。
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, optimized for closures
Swift,针对闭包进行了优化
As Swift is optimized for asynchronous development, Apple worked more on closures. The first is that function signature can be inferred so you don't have to rewrite it.
由于 Swift 针对异步开发进行了优化,因此 Apple 在闭包方面做了更多工作。第一个是可以推断函数签名,因此您不必重写它。
Access params by numbers
按数字访问参数
let resultShortAnon = test({return "ANON_" + let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
+ "__ANON" })
Params inference with naming
带命名的参数推断
let resultTrailingClosure = test { return "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!")
}
}
+ "__TRAILCLOS" }
Trailing Closure
尾随收盘
This special case works only if the block is the last argument, it's called trailing closure
这种特殊情况仅在块是最后一个参数时才有效,称为尾随闭包
Here is an example (merged with inferred signature to show Swift power)
这是一个示例(与推断签名合并以显示 Swift 功能)
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)
Finally:
最后:
Using all this power what I'd do is mixing trailing closure and type inference (with naming for readability)
使用所有这些功能,我要做的是混合尾随闭包和类型推断(为了可读性而命名)
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end
回答by Gil Beyruth
Hello, Swift
你好,斯威夫特
Complementing what @Francescu answered.
补充@Francescu 的回答。
Adding extra parameters:
添加额外参数:
##代码##