ios objective-c automatic-ref-counting objective-c-blocks

ios - ¿Puedo usar los bloques de Objective-C como propiedades?



automatic-ref-counting objective-c-blocks (8)

Disclamador

Esto no pretende ser "la buena respuesta", ya que esta pregunta pregunta explícitamente por ObjectiveC. Como Apple presentó Swift en la WWDC14, me gustaría compartir las diferentes formas de usar el bloqueo (o cierres) en Swift.

Hola swift

Tiene muchas formas ofrecidas para pasar un bloque equivalente a funcionar en Swift.

Encontré tres.

Para entender esto, te sugiero que pruebes en el patio de recreo este pequeño código.

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, optimizado para cierres.

Como Swift está optimizado para el desarrollo asíncrono, Apple trabajó más en cierres. La primera es que la firma de la función se puede inferir para que no tenga que volver a escribirla.

Acceso a params por números.

let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })

Inferencia de parámetros con nombres

let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })

Cierre de seguimiento

Este caso especial funciona solo si el bloque es el último argumento, se llama cierre final

Aquí hay un ejemplo (fusionado con la firma inferida para mostrar el poder de Swift)

let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }

Finalmente:

Usando todo este poder, lo que haría sería mezclar el cierre final y la inferencia de tipos (con nombres para facilitar la lectura)

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

¿Es posible tener bloques como propiedades utilizando la sintaxis de propiedad estándar?

¿Hay algún cambio para ARC ?


Aquí hay un ejemplo de cómo cumplirías esa tarea:

#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; }

Ahora, lo único que tendría que cambiar si necesitara cambiar el tipo de comparación sería typedef int (^IntBlock)() . Si necesita pasarle dos objetos, cámbielo a esto: typedef int (^IntBlock)(id, id) , y cambie su bloque a:

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

Espero que esto ayude.

EDITAR 12 de marzo de 2012:

Para ARC, no se requieren cambios específicos, ya que ARC administrará los bloques por usted siempre y cuando estén definidos como copia. Tampoco es necesario que establezca la propiedad en cero en su destructor.

Para obtener más información, consulte este documento: http://clang.llvm.org/docs/AutomaticReferenceCounting.html


Para Swift, solo usa cierres: example.

En Objective-C,

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

Es así de simple.

La documentación de Apple, explicando completamente este problema:

Doco de manzana

En su archivo .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".

Aquí está su archivo .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(); }

Tenga cuidado con el código de ejemplo fuera de fecha.

Con los sistemas modernos (2014+), haga lo que se muestra aquí. Es así de simple. Espero que ayude a alguien. ¡Feliz navidad 2013!


Para la posteridad / integridad ... Aquí hay dos ejemplos COMPLETOS de cómo implementar esta "manera de hacer las cosas" ridículamente versátil. La respuesta de @Robert es maravillosamente concisa y correcta, pero aquí también quiero mostrar formas de "definir" los bloques.

@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

¿Tonto? Sí. ¿Útil? Genial. Aquí hay una forma diferente, "más atómica" de establecer la propiedad ... y una clase que es ridículamente útil ...

@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

Esto ilustra la configuración de la propiedad de bloque a través del elemento de acceso (aunque dentro de init, una práctica incomparable) .. contra el mecanismo de "captador" "no atómico" del primer ejemplo. En cualquier caso ... las implementaciones "codificadas" siempre se pueden sobrescribir, por ejemplo .. a lá ..

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

Además ... si desea agregar una propiedad de bloque en una categoría ... digamos que quiere usar un Bloque en lugar de alguna acción / acción de la vieja escuela ... Puede usar valores asociados para, bueno ... asociar los bloques.

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

Ahora, cuando haces un botón, no tienes que configurar un drama de IBAction ... Solo asocia el trabajo que se realizará en la creación ...

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

Este patrón se puede aplicar OVER y OVER a las API de Cocoa. Utilice las propiedades para acercar las partes relevantes de su código, elimine los paradigmas de delegación complicados y aproveche el poder de los objetos más allá de simplemente actuar como "contenedores" tontos.


Por supuesto que podrías usar bloques como propiedades. Pero asegúrese de que estén declarados como @property (copia) . Por ejemplo:

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

En MRC, los bloques que capturan variables de contexto se asignan en la pila ; se liberarán cuando se destruya el marco de pila. Si se copian, se asignará un nuevo bloque en el montón , que se puede ejecutar más tarde después de que se abra el marco de la pila.


Puede seguir el formato a continuación y puede usar la propiedad testingObjectiveCBlock en la clase.

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

Para más información echar un vistazo here


Hola swift

Complementando lo que contestó @Francescu.

Añadiendo parámetros extra:

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 (nonatomic, copy) void (^simpleBlock)(void); @property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);

Si va a repetir el mismo bloque en varios lugares, use una definición de tipo

typedef void(^MyCompletionBlock)(BOOL success, NSError *error); @property (nonatomic) MyCompletionBlock completion;