首页 文章

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

提问于
浏览
307

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

ARC 有什么变化吗?

8 回答

  • 210

    Hello, Swift

    补充@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)
    
  • 278
    @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;
    
  • 8

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

    愚蠢?是 . 有用? Hells yeah. 这是一种不同的,"more atomic"设置属性的方法..以及一个非常有用的类......

    @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内部,这是一种令人沮丧的冒险行为......)与第一个示例的"nonatomic" "getter"机制相比 . 在任何一种情况下...... "hardcoded"实现总是可以被覆盖,每个实例..一个lá..

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

    另外..如果你想在一个类别中添加一个块属性...说你想使用一个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
    

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

    _button.actionBlock = ^(NSControl*thisButton){ 
    
         [doc open]; [thisButton setEnabled:NO]; 
    };
    

    这种模式可以应用于Cocoa API的OVER和OVER . 使用属性可以使代码的相关部分更加紧密,消除错综复杂的委托范例,并利用对象的力量,而不仅仅是作为哑巴"containers" .

  • 18

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

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

    在MRC中,捕获上下文变量的块在 stack 中分配;它们将在堆栈帧被破坏时被释放 . 如果复制它们,将在 heap 中分配一个新块,该块可以在堆栈帧被加载后稍后执行 .

  • 153

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


    在Objective-C中,

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

    就这么简单 .

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

    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)系统,执行此处显示的内容 . 就这么简单 . 希望它可以帮到某人 . 2013年圣诞快乐!

  • 7

    免责声明

    这不是“好答案”,因为这个问题明确要求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)
    

    Swift,针对封口进行了优化

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

    按号码访问参数

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

    以下是您将如何完成此任务的示例:

    #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

  • -3

    您可以按照以下格式使用,也可以使用类中的 testingObjectiveCBlock 属性 .

    typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
    
    @interface MyClass : NSObject
    @property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
    @end
    

    欲了解更多信息,请查看here

相关问题