Можно ли иметь блоки как свойства с использованием стандартного синтаксиса свойств?
Существуют ли какие-либо изменения для ARC?
Можно ли иметь блоки как свойства с использованием стандартного синтаксиса свойств?
Существуют ли какие-либо изменения для ARC?
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
Если вы собираетесь повторять один и тот же блок в нескольких местах, используйте тип def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
Вот пример того, как вы выполнили бы такую задачу:
#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;
}
Теперь единственное, что нужно изменить, если вам нужно изменить тип сравнения, будет typedef int (^IntBlock)()
. Если вам нужно передать два объекта, измените их на: typedef int (^IntBlock)(id, id)
и измените свой блок на:
^ (id obj1, id obj2)
{
return rand();
};
Надеюсь, это поможет.
EDIT 12 марта 2012 года:
Для ARC никаких конкретных изменений не требуется, поскольку ARC будет управлять блоками для вас, пока они определены как копия. Вам также не нужно устанавливать свойство nil в свой деструктор.
Подробнее читайте в этом документе: http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Для Swift просто используйте замыкания: пример.
В Objective-C ключом является:
@property (copy)void (^doStuff)(void);
Это так просто.
В вашем .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 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".
Вот ваш файл .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 how to run the block:
if ( self.doStuff != nil )
self.doStuff();
}
С современными (2014+) системами делайте то, что показано здесь. Это так просто.
Для потомства/полноты... Вот два ПОЛНЫХ примера того, как реализовать этот смехотворно универсальный "способ делать вещи". Ответ @Robert блаженно кратким и правильным, но здесь я хочу также показать способы фактического "определения" блоков.
@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
Глупый? Да. Полезно? Hells yeah. Вот другой, более атомный способ установки свойства.. и класс, который смехотворно полезен...
@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
Это иллюстрирует настройку свойства блока через аксессуар (хотя и внутри init, дискуссивно рискованная практика...) по сравнению с первым примером "неатомического" механизма "getter". В любом случае... "жестко закодированные" реализации всегда могут быть перезаписаны на один экземпляр.. a lá..
CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
[layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Также.. если вы хотите добавить свойство блока в категорию... скажите, что вы хотите использовать блок вместо какого-либо действия "целевое/действие" старой школы... Вы можете просто использовать связанные значения, ну.. свяжите блоки.
typedef void(^NSControlActionBlock)(NSControl*);
@interface NSControl (ActionBlocks)
@property (copy) NSControlActionBlock actionBlock; @end
@implementation NSControl (ActionBlocks)
- (NSControlActionBlock) actionBlock {
// use the "getter" method 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 called.
}
- (void) doItYourself {
if (self.actionBlock && self.target == self) self.actionBlock(self);
}
@end
Теперь, когда вы делаете кнопку, вам не нужно настраивать какую-либо драму IBAction
.. Просто сопоставьте работу, которую нужно выполнить при создании...
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
Этот шаблон может применяться OVER и OVER до Cocoa API. Используйте свойства, чтобы сблизить соответствующие части вашего кода, устранить запутанные парадигмы делегирования и использовать силу объектов, помимо того, что они действуют как немые "контейнеры".
Конечно, вы можете использовать блоки как свойства. Но убедитесь, что они объявлены как @property (копия). Например:
typedef void(^TestBlock)(void);
@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
В MRC блоки захвата переменных контекста выделяются в стек; они будут выпущены, когда рама стека будет уничтожена. Если они скопированы, новый блок будет выделен в куче, который может быть выполнен позже после того, как будет вставлен фрейм стека.
Это не предназначено для "хорошего ответа", так как этот вопрос задается явно для ObjectiveC. Поскольку Apple представила Swift на WWDC14, я хотел бы поделиться различными способами использования блока (или закрытия) в Swift.
У вас есть много способов предложить блок, эквивалентный функции в Swift.
Я нашел три.
Чтобы понять это, я предлагаю вам испытать на детской площадке этот маленький кусок кода.
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 оптимизирован для асинхронной разработки, Apple больше работала над закрытием. Первая заключается в том, что сигнатура функции может быть выведена, поэтому вам не нужно ее переписывать.
let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Этот специальный случай работает, только если блок является последним аргументом, он называется закрывающим закрытием
Вот пример (объединенный с выведенной сигнатурой для отображения мощности Swift)
let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Наконец:
Используя всю эту силу, я сделаю смешение закрывающего закрытия и ввода типа (с именованием для удобочитаемости)
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!")
}
}
Привет, Swift
В дополнение к тому, что ответил @Francescu.
Добавление дополнительных параметров:
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)
Вы можете следить за форматом ниже и можете использовать свойство testingObjectiveCBlock
в классе.
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end
Для получения дополнительной информации смотрите здесь