328 votes

Puis-je utiliser des blocs de Objective-C sous forme de propriétés ?

Est il possible d’avoir des blocs en tant que propriétés à l’aide de la syntaxe de la propriété standard ?

Y a-t-il des changements pour l’ARC ?

315voto

Robert Points 10822

Réponse de Richard est grand, voici une version concise.

En supposant que :

  • Vous utilisez xCode > 4.4 et ai synthétiser par défaut activé (sinon juste `` )
  • Vous utilisez ARC (sinon `` dans dealloc).

210voto

Richard J. Ross III Points 33152

Voici un exemple de comment vous feriez pour accomplir une telle tâche:

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

Maintenant, la seule chose qui aurait besoin de changer si vous avez besoin de changer le type de comparer serait l' typedef int (^IntBlock)(). Si vous avez besoin de passer deux objets, changer ceci: typedef int (^IntBlock)(id, id), et de changer votre bloc:

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

J'espère que cette aide.

EDIT du 12 Mars 2012:

Pour l'ARC, il n'y a pas de changements spécifiques nécessaires, comme l'ARC de gérer les blocs pour vous aussi longtemps qu'ils sont définis en tant que copie. Vous n'avez pas besoin de définir la propriété à néant dans votre destructeur.

Pour en savoir plus, veuillez consulter ce document: http://clang.llvm.org/docs/AutomaticReferenceCounting.html

161voto

Joe Blow Points 3618

REMARQUE: ce est pour Objective-c seulement.

Si vous travaillez avec Swift, découvrez "Fermetures".

(Exemple assurance de la qualité. En effet, voici pertinentes Swift de référence.)


Pour l'enregistrement...

Voici exactement comment le faire à partir de 2014...

à l'ARC, Xcode5, iOS7.

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

Ne pas utiliser autre chose que simplement "copier". Ne pas synthétiser.

C'est aussi simple que cela.

Voici une complète et détaillée de l'exemple, avec de longues notes explicatives:

Dans votre .h fichier:

// Here is a block as a property:
//
// So for example:
//
// If someone passes you a block, you can "hold on to it",
// while you do other stuff. Later, you can use the block.
//
// So: Here is the property which will hold the "incoming block".
// We will name the property 'doStuff':

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

// So, here's some handy 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.
// The method refers to the incoming block of code as 'pleaseDoMeLater':

-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater;

// Just as it says above, we will "hold on to" that block of code
// in our astounding block-property called "doStuff".

Plein doco sur les raisons d'utiliser précisément "copier": WorkingwithBlocks.html d'Apple

Dans votre .m fichier:

Ne pas en faire la synthèse.

 -(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater
    {
    self.doStuff = pleaseDoMeLater;
    // Here you would do other long, complicated processes, perhaps
    // following one of many different paths and using many routines.
    // In our example let's say that, when "everything is finally done"
    // we will end up at the routine "_teste".
    // So, "_teste" needs the block that was passed in to us, and that
    // is exactly why you use a block property in this situation.
    [self _teste];
    }
-(void)_teste
    {
    NSLog(@"I am in _teste and it's 2014.");

    // And here's how to run the block:
    // (it is best to check that it is not nil)

    if ( self.doStuff != nil )
       self.doStuff();
    }

Méfiez-vous de l'out-of-date code d'exemple pour ce sujet.

Sur l'internet il ya de nombreux hors de la date d'exemples de propriétés d'un bloc. VEUILLEZ ÊTRE PRUDENT.

Avec moderne (2014+) des systèmes, il vous suffit de faire exactement ce qu'il montre ici. Heureusement, c'est aussi simple que cela.

Espérons que cela aide quelqu'un. Joyeux Noël 2013!

21voto

alex gray Points 5089

Pour la postérité / l'exhaustivité de l'amour... " Voici deux exemples de la façon de mettre en œuvre cette ridiculement polyvalent "façon de faire les choses". @Robert réponse est béatement concise et correcte, mais ici, je veux aussi montrer des façons de fait de "définir" les blocs.

@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

Idiot? Oui. Utile? Hells ouais. Voici un autre, plus "de atomique en" voie de la définition de la propriété.. et une classe qui est ridiculement utile...

@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

Ceci illustre la définition de la propriété de bloc via l'accesseur (quoique à l'intérieur de l'init, un discutable de dicey pratique..) vs le premier exemple de "nonatomic" "getter" mécanisme. Dans les deux cas... "codé en dur" les implémentations peuvent toujours être remplacé, par exemple.. un lá..

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

Aussi.. si vous souhaitez ajouter une propriété de bloc dans une catégorie... dites que vous voulez utiliser un Bloc à la place de vieux-école cible / action "action"... Vous pouvez juste utiliser les valeurs associées, bien.. associer les blocs.

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

Maintenant, lorsque vous effectuez un bouton, vous n'avez pas à configurer certains IBAction drame.. Juste associer le travail à faire à la création...

_button.actionBlock = ^(NSControl*thisButton){ 

     [doc open]; [thisButton setEnabled:NO]; 
};

Ce modèle peut être appliqué à des API Cocoa. Utiliser les propriétés d'amener les parties pertinentes de votre code de rapprocher, d'éliminer alambiqué délégation paradigmes, et de tirer parti de la puissance des objets au-delà de tout simplement agir comme des idiots "conteneurs".

6voto

Francescu Points 3720

Clause de non-responsabilité

Ce n'est pas prévu pour être "la bonne réponse", que cette question, demandez explicitement pour ObjectiveC. Comme Apple a introduit Swift à la WWDC14, j'aimerais partager avec vous les différentes façons d'utiliser le bloc (ou fermeture) de Swift.

Bonjour, Swift

Vous avez de nombreux moyens proposés pour passer un bloc équivalent à la fonction de Swift.

J'ai trouvé trois.

Pour comprendre cela, je vous propose de tester en terrain de jeu, ce petit morceau de 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)

Rapide, optimisé pour les fermetures

Comme Swift est optimisé pour asynchrones développement, Apple a travaillé plus sur les fermetures. La première est que la fonction de signature peut être déduit de sorte que vous n'avez pas à le réécrire.

L'accès params par numéros

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

Params inférence avec de nommage

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

Fuite De Fermeture

Ce cas particulier ne fonctionne que si le bloc est le dernier argument, il est appelé de fuite de fermeture

Voici un exemple (fusionné avec déduite de la signature de montrer Swift alimentation)

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

Enfin:

À l'aide de toute cette puissance ce que je fais est un mélange de fuite de fermeture et de l'inférence de type (avec naming pour des raisons de lisibilité)

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X