wake-up-neo.com

Kann ich Objective-C-Blöcke als Eigenschaften verwenden?

Ist es möglich, Blöcke als Eigenschaften zu verwenden, wenn die Standardsyntax für Eigenschaften verwendet wird?

Gibt es Änderungen für ARC ?

316
gurghet
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);

Wenn Sie denselben Block an mehreren Stellen wiederholen möchten, geben Sie def ein

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

Hier ist ein Beispiel, wie Sie eine solche Aufgabe ausführen würden:

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

Das einzige, was sich ändern müsste, wenn Sie den Vergleichstyp ändern müssten, wäre typedef int (^IntBlock)(). Wenn Sie zwei Objekte übergeben müssen, ändern Sie dies in: typedef int (^IntBlock)(id, id), und ändern Sie Ihren Block in:

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

Ich hoffe das hilft.

EDIT 12. März 2012:

Für ARC sind keine speziellen Änderungen erforderlich, da ARC die Blöcke für Sie verwaltet, solange sie als Kopie definiert sind. Sie müssen die Eigenschaft in Ihrem Destruktor auch nicht auf null setzen.

Weitere Informationen finden Sie in diesem Dokument: http://clang.llvm.org/docs/AutomaticReferenceCounting.html

210

Verwenden Sie für Swift einfach closures: Beispiel.


In Ziel-C,

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

So einfach ist das.

Apples Dokumentation, in der dieses Problem vollständig erläutert wird:

Apple doco.

In Ihrer .h-Datei:

// 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".

Hier ist Ihre .m-Datei:

 -(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();
    }

Achten Sie auf veralteten Beispielcode.

Machen Sie mit modernen (2014+) Systemen, was hier gezeigt wird. So einfach ist das. Hoffe es hilft jemandem. Frohe Weihnachten 2013!

156
Fattie

Der Nachwelt/Vollständigkeit halber ... Hier sind zwei VOLLSTÄNDIGE Beispiele, wie diese lächerlich vielseitige "Art, Dinge zu tun", implementiert werden kann. @ Roberts Antwort ist selig prägnant und richtig, aber hier möchte ich auch Wege aufzeigen, um die Blöcke tatsächlich "zu definieren".

@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

Dumm? Ja. Nützlich? Hells yeah. Hier ist eine andere, "atomarere" Methode zum Festlegen der Eigenschaft ... und eine Klasse, die lächerlich nützlich ist ...

@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

Dies zeigt, wie die Blockeigenschaft über den Accessor (wenn auch innerhalb von init, einer fragwürdigen Vorgehensweise) gegenüber dem "nichtatomaren" "Getter" -Mechanismus des ersten Beispiels eingestellt wird. In jedem Fall ... können die "hardcodierten" Implementierungen immer überschrieben werden, pro Instanz .. a lá ..

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

Auch .. wenn Sie eine Blockeigenschaft in eine Kategorie einfügen möchten ... sagen Sie, Sie möchten einen Block anstelle eines alten Ziels/einer Aktion "Aktion" verwenden ... Sie können einfach die zugehörigen Werte für ... verwenden. Ordnen Sie die Blöcke zu.

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

Wenn Sie eine Schaltfläche erstellen, müssen Sie kein IBAction -Drama einrichten. Verknüpfen Sie einfach die bei der Erstellung auszuführende Arbeit ...

_button.actionBlock = ^(NSControl*thisButton){ 

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

Dieses Muster kann angewendet werden OVER und OVER to Cocoa APIs. Verwenden Sie Eigenschaften, um die relevanten Teile Ihres Codes zusammenzuführen enger zusammenrücken, verschlungene Delegierungsparadigmen zu eliminieren und die Macht von Objekten zu nutzen, die über das bloße Handeln als "Container" hinausgeht.

20
Alex Gray

Natürlich können Sie Blöcke als Eigenschaften verwenden. Stellen Sie jedoch sicher, dass sie als @ property (copy) deklariert sind. Zum Beispiel:

typedef void(^TestBlock)(void);

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

In der MRC werden Blöcke, die Kontextvariablen erfassen, in Stapel; Sie werden freigegeben, wenn der Stapelrahmen zerstört wird. Wenn sie kopiert werden, wird ein neuer Block in Heap zugewiesen, der später ausgeführt werden kann, nachdem der Stack-Frame eingefügt wurde.

8
Mindy

Disclamer

Dies soll nicht "die gute Antwort" sein, da diese Frage ausdrücklich für ObjectiveC gestellt wird. Wie Apple eingeführt Swift auf der WWDC14), möchte ich die verschiedenen Arten der Verwendung von Blöcken (oder Closures) in Swift erläutern.

Hallo swift

Sie haben viele Möglichkeiten, einen Block zu übergeben, der der Funktion in Swift entspricht.

Ich habe drei gefunden.

Um dies zu verstehen, empfehle ich Ihnen, dieses kleine Stück Code auf dem Spielplatz zu testen.

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)

Schnell, optimiert für Verschlüsse

Da Swift für die asynchrone Entwicklung optimiert ist, hat Apple mehr an Closures gearbeitet. Erstens kann darauf geschlossen werden, dass die Funktionssignatur nicht neu geschrieben werden muss .

Zugriff auf Parameter über Nummern

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

Params schließen mit der Benennung

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

Trailing Closure

Dieser Sonderfall funktioniert nur, wenn der Block das letzte Argument ist und abschließender Schluss heißt

Hier ist ein Beispiel (zusammengeführt mit der abgeleiteten Signatur, um Swift power) zu zeigen.

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

Endlich:

Mit all dieser Kraft würde ich den abschließenden Schluss und die Typinferenz mischen (mit Namen für die Lesbarkeit)

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

Hallo, Swift

Ergänzt das, was @Francescu beantwortet hat.

Zusätzliche Parameter hinzufügen:

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)
0
Gil Beyruth