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

gur*_*het 319 objective-c ios objective-c-blocks automatic-ref-counting

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

ARC有什么变化吗?

Rob*_*ert 297

@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
Run Code Online (Sandbox Code Playgroud)

如果您要在几个地方重复相同的块,请使用类型def

typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
Run Code Online (Sandbox Code Playgroud)

  • @Robert:你又好运了,因为没有输入`@ synthesize`,默认是你在做什么`@synthesize name = _name;`http://stackoverflow.com/a/12119360/1052616 (7认同)
  • 使用xCode 4.4或更新版本,您无需合成.这将使它更加简洁.[Apple Doc](http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/WhatsNewXcode/Articles/xcode_4_4.html) (3认同)

Ric*_*III 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;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你需要改变比较类型,唯一需要改变的就是typedef int (^IntBlock)().如果需要将两个对象传递给它,请将其更改为:typedef int (^IntBlock)(id, id),并将块更改为:

^ (id obj1, id obj2)
{
    return rand();
};
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助.

编辑2012年3月12日:

对于ARC,不需要进行任何特定更改,因为只要将ARC定义为副本,ARC就会为您管理块.您也不需要在析构函数中将属性设置为nil.

有关更多信息,请查看此文档:http: //clang.llvm.org/docs/AutomaticReferenceCounting.html


Fat*_*tie 156

对于Swift,只需使用闭包:例子.


在Objective-C中,

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

就这么简单.

Apple的文档,完整地解释了这个问题:

Apple doco.

在你的.h文件中:

@property (copy)void (^doStuff)(void);
Run Code Online (Sandbox Code Playgroud)

这是你的.m文件:

// 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".
Run Code Online (Sandbox Code Playgroud)

注意过时的示例代码.

使用现代(2014+)系统,执行此处显示的内容.就这么简单.希望它可以帮助某人.2013年圣诞快乐!


Ale*_*ray 20

为了后人/完整性的缘故...以下是两个如何实现这种荒谬的多功能"做事方式"的完整例子.@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
Run Code Online (Sandbox Code Playgroud)

愚蠢?是. 有用?地狱啊. 这是一种不同的,"更原子"的设置属性的方式..以及一个非常有用的类......

@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
Run Code Online (Sandbox Code Playgroud)

这说明了通过访问器设置块属性(虽然在init内,这是一个令人讨厌的冒险行为......)与第一个例子的"非原子""getter"机制相比.在任何一种情况下...... 每个实例都可以覆盖"硬编码"实现.一个...

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

另外..如果你想在一个类别中添加一个块属性...说你想使用一个Block而不是一些老派目标/动作"动作"...你可以使用相关的值,好吧..关联块.

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
Run Code Online (Sandbox Code Playgroud)

现在,当你创建一个按钮时,你不需要设置一些IBAction戏剧.只需关联创建时要完成的工作......

_button.actionBlock = ^(NSControl*thisButton){ 

     [doc open]; [thisButton setEnabled:NO]; 
};
Run Code Online (Sandbox Code Playgroud)

这种模式可以应用于Cocoa API,不是OVER和OVER.使用属性可以使代码的相关部分更加紧密,消除复杂的委托范例,并利用对象的力量,而不仅仅是充当愚蠢的"容器".

  • “原子”对于属性来说是正确的做法是非常罕见的。在一个线程中设置块属性并_同时_在另一个线程中读取它,或者从多个线程同时设置块属性将是一件非常奇怪的事情。因此,“原子”与“非原子”的成本并没有给您带来任何真正的优势。 (2认同)

Min*_*ndy 8

当然你可以使用块作为属性.但要确保它们被声明为@property(copy).例如:

typedef void(^TestBlock)(void);

@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
Run Code Online (Sandbox Code Playgroud)

在MRC中,捕获上下文变量的块在堆栈中分配; 它们将在堆栈框架被销毁时被释放.如果它们被复制,则将在堆中分配新块,这可以在堆栈帧被加载后稍后执行.


Fra*_*scu 7

Disclamer

这不是"好答案",因为这个问题明确要求ObjectiveC.当Apple在WWDC14上介绍Swift时,我想分享在Swift中使用块(或闭包)的不同方法.

你好,斯威夫特

你有很多方法可以在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)
Run Code Online (Sandbox Code Playgroud)

Swift,针对封盖进行了优化

由于Swift针对异步开发进行了优化,因此Apple在闭包方面的工作量更大.首先是可以推断出函数签名,因此您不必重写它.

按号码访问参数

let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
Run Code Online (Sandbox Code Playgroud)

参数推理与命名

let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Run Code Online (Sandbox Code Playgroud)

尾随关闭

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

这是一个例子(与推断签名合并以显示Swift电源)

let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Run Code Online (Sandbox Code Playgroud)

最后:

使用所有这些功能,我要做的是混合尾随闭包和类型推断(为了便于阅读命名)

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!")
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

113115 次

最近记录:

5 年,10 月 前