Czy Mogę używać bloków Objective-C jako właściwości?
Czy możliwe jest posiadanie bloków jako właściwości przy użyciu standardowej składni właściwości?
Czy są jakieś zmiany dla ARC ?
8 answers
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
Jeśli zamierzasz powtarzać ten sam blok w kilku miejscach użyj type def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 10:47:10
Oto przykład jak wykonałbyś takie zadanie:
#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;
}
Teraz, jedyną rzeczą, która musiałaby się zmienić, jeśli trzeba zmienić rodzaj porównania, byłoby typedef int (^IntBlock)()
. Jeśli chcesz przekazać do niego dwa obiekty, zmień to na to: typedef int (^IntBlock)(id, id)
, a następnie zmień swój blok na:
^ (id obj1, id obj2)
{
return rand();
};
Mam nadzieję, że to pomoże.
Edycja 12 marca 2012:
W przypadku ARC nie są wymagane żadne konkretne zmiany, ponieważ ARC będzie zarządzać blokami tak długo, jak są one zdefiniowane jako kopia. Ty tak nie trzeba też ustawiać właściwości na zero w destruktorze.
Aby dowiedzieć się więcej, zapoznaj się z tym dokumentem: http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-28 12:23:56
Dla Swift, wystarczy użyć zamknięcia: przykład.
W Objective-C:
@ property (copy)void
@property (copy)void (^doStuff)(void);
To takie proste.
[10]}Oto prawdziwa dokumentacja Apple, która dokładnie określa, czego używać:
W Twoim .plik 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".
Oto Twoja .plik 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();
}
Uważaj na Nieaktualny przykładowy kod.
Z nowoczesnymi (2014+) systemami, rób to, co pokazano tutaj. To takie proste.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-25 17:42:53
Ze względu na potomność/ kompletność ... oto dwa pełne przykłady, jak wdrożyć ten absurdalnie wszechstronny "sposób robienia rzeczy". @ Robert odpowiedź jest banalnie zwięzła i poprawna, ale tutaj chcę również pokazać sposoby na faktycznie "zdefiniowanie" bloków.
@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
Głupie? Tak. przydatne? jasne. oto inny, "bardziej atomowy" sposób ustawienia właściwości.. i klasa, która jest śmiesznie przydatna ...
@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
To ilustruje ustawienie właściwość blokowa za pośrednictwem accesora (choć wewnątrz init, dyskusyjna ryzykowna praktyka..) kontra pierwszy przykładowy "nieatomowy" mechanizm "getter". W obu przypadkach... "zakodowane na twardo" implementacje mogą być zawsze nadpisane, na instancję.. a lá..
CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
[layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Również.. jeśli chcesz dodać właściwość block do kategorii... powiedzmy, że chcesz użyć bloku zamiast jakiegoś starego celu / akcji "Akcja"... Możesz po prostu użyć powiązanych wartości, dobrze.. / align = "left" / bloki.
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
Teraz, kiedy robisz guzik, nie musisz urządzać jakiegoś dramatu.. Wystarczy powiązać dzieło do wykonania przy stworzeniu...
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
Ten wzorzec może być stosowany w kółko do API Cocoa. użyj właściwości, aby zbliżyć odpowiednie części kodu do siebie , wyeliminować zawiłe paradygmaty delegacji i wykorzystać moc obiektów wykraczającą poza działanie jako głupie "kontenery".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-07 13:20:44
Oczywiście można użyć bloków jako właściwości. Ale upewnij się, że są zadeklarowane jako @ property (copy) . Na przykład:
typedef void(^TestBlock)(void);
@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
W MRC bloki przechwytywające zmienne kontekstowe są przydzielane w stosie ; zostaną one zwolnione, gdy ramka stosu zostanie zniszczona. Jeśli zostaną skopiowane, nowy blok zostanie przydzielony w stercie , która może być wykonana Później Po wypełnieniu ramki stosu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-16 09:08:50
[7]} nie ma to być "dobra odpowiedź", ponieważ to pytanie zadaje się wprost dla ObjectiveC. Ponieważ Apple wprowadziło Swift na WWDC14, chciałbym podzielić się różnymi sposobami korzystania z bloków (lub zamknięć) w Swift.
Hello, Swift
Masz wiele sposobów na przekazanie bloku odpowiadającego funkcji w języku Swift.
Znalazłem trzy.Aby to zrozumieć proponuję przetestować na placu zabaw ten mały kawałek kodu.
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, Optymalizacja dla zamknięć
Ponieważ Swift jest zoptymalizowany pod kątem asynchronicznego rozwoju, Apple więcej pracowało nad zamknięciami. Po pierwsze, można wywnioskować podpis funkcji, dzięki czemu nie trzeba jej przepisywać.
Access params by numbers
let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
Params wnioskowanie z nazewnictwa
let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Zamknięcie Końcowe
Ten specjalny przypadek działa tylko wtedy, gdy blok jest ostatnim argumentem, nazywa się zamykaniem końcowym
Oto przykład (połączony z wywnioskowanym podpis, aby pokazać moc Swift)
let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Wreszcie:
Używając całej tej mocy, to co bym zrobił, to mieszanie zamknięcia końcowego i wnioskowania typu (z nazwami dla czytelności)
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!")
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-17 13:46:08
Witaj, Swift
Completing what @Francescu answer.
Dodanie dodatkowych parametrów:
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-21 17:56:53
Możesz postępować zgodnie z poniższym formatem i używać właściwości testingObjectiveCBlock
w klasie.
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end
Po Więcej informacji zajrzyj tutaj
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-24 06:41:20