Ist es möglich, Blöcke als Eigenschaften mit der Standardeigenschaftssyntax zu haben?
Gibt es Änderungen für ARC ?
Ist es möglich, Blöcke als Eigenschaften mit der Standardeigenschaftssyntax zu haben?
Gibt es Änderungen für ARC ?
Antworten:
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
Wenn Sie denselben Block an mehreren Stellen wiederholen möchten, verwenden Sie den Typ def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
@synthesize myProp = _myProp
@synthesize
die Standardeinstellung zu setzen, tun Sie @synthesize name = _name;
stackoverflow.com/a/12119360/1052616
Hier ist ein Beispiel, wie Sie eine solche Aufgabe erfüllen 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 die Art des Vergleichs ändern müssten, wäre das 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.
BEARBEITEN 12. März 2012:
Für ARC sind keine spezifischen Ä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
Verwenden Sie für Swift einfach Verschlüsse: Beispiel.
In Ziel-C:
@property (copy)void (^doStuff)(void);
So einfach ist das.
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();
}
Machen Sie mit modernen Systemen (2014+) das, was hier gezeigt wird. So einfach ist das.
strong
statt zu verwenden copy
?
nonatomic
meisten anderen Fällen, in denen Eigenschaften verwendet werden, nicht anders sein sollte als Best Practices?
Der Nachwelt / Vollständigkeit halber ... Hier sind zwei vollständige Beispiele, wie man diese lächerlich vielseitige "Art, Dinge zu tun" umsetzt. @ Roberts Antwort ist selig präzise und korrekt, aber hier möchte ich auch Wege zeigen, wie man die Blöcke tatsächlich "definiert".
@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? Höllen ja. Hier ist eine andere, "atomarere" Art, die Eigenschaft festzulegen ... 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, eine umstrittene heikle Praxis) gegenüber dem "nichtatomaren" "Getter" -Mechanismus des ersten Beispiels festgelegt wird. In beiden Fällen… können die "fest codierten" 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;
Außerdem ... wenn Sie eine Blockeigenschaft zu einer Kategorie hinzufügen möchten ... sagen Sie, Sie möchten einen Block anstelle einer alten Aktion / Aktion "Aktion" verwenden ... Sie können einfach zugehörige Werte verwenden, um ... 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 jetzt einen Knopf drücken, müssen Sie kein IBAction
Drama einrichten . Verknüpfen Sie einfach die Arbeit, die bei der Erstellung zu erledigen ist ...
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
Dieses Muster kann OVER und OVER auf Cocoa-APIs angewendet werden . Verwenden Sie Eigenschaften, um die relevanten Teile Ihres Codes näher zusammenzubringen , verschlungene Delegierungsparadigmen zu beseitigen und die Leistung von Objekten zu nutzen, die über die bloße Funktion als dumme "Container" hinausgeht.
Natürlich können Sie Blöcke als Eigenschaften verwenden. Stellen Sie jedoch sicher, dass sie als @property (Kopie) deklariert sind . Zum Beispiel:
typedef void(^TestBlock)(void);
@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
In MRC werden Blöcke, die Kontextvariablen erfassen, im Stapel zugewiesen . Sie werden freigegeben, wenn der Stapelrahmen zerstört wird. Wenn sie kopiert werden, wird ein neuer Block im Heap zugewiesen , der später ausgeführt werden kann, nachdem der Stapelrahmen eingefügt wurde.
Dies soll nicht "die gute Antwort" sein, da diese Frage ausdrücklich nach ObjectiveC fragt. Als Apple Swift auf der WWDC14 vorstellte, möchte ich Ihnen die verschiedenen Möglichkeiten zur Verwendung von Blöcken (oder Verschlüssen) in Swift vorstellen.
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)
Da Swift für die asynchrone Entwicklung optimiert ist, hat Apple mehr an Schließungen gearbeitet. Die erste ist, dass die Funktionssignatur abgeleitet werden kann, sodass Sie sie nicht neu schreiben müssen.
let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Dieser Sonderfall funktioniert nur, wenn der Block das letzte Argument ist. Er wird als abschließender Abschluss bezeichnet
Hier ist ein Beispiel (zusammengeführt mit der abgeleiteten Signatur, um die Swift-Leistung zu zeigen)
let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Schließlich:
Mit all dieser Kraft würde ich Trailing Closure und Typinferenz mischen (mit Benennung zur besseren 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!")
}
}
Hallo, Swift
Ergänzt die Antwort von @Francescu.
Hinzufügen zusätzlicher Parameter:
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)
Sie können dem folgenden Format folgen und die testingObjectiveCBlock
Eigenschaft in der Klasse verwenden.
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end
Weitere Informationen finden Sie hier