เป็นไปได้หรือไม่ที่จะมีบล็อกเป็นคุณสมบัติโดยใช้ไวยากรณ์คุณสมบัติมาตรฐาน
ARCมีการเปลี่ยนแปลงหรือไม่?
เป็นไปได้หรือไม่ที่จะมีบล็อกเป็นคุณสมบัติโดยใช้ไวยากรณ์คุณสมบัติมาตรฐาน
ARCมีการเปลี่ยนแปลงหรือไม่?
คำตอบ:
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
หากคุณกำลังจะทำซ้ำบล็อกเดียวกันในหลายแห่งให้ใช้การกำหนดประเภท
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
@synthesize myProp = _myProp
@synthesize
ค่าเริ่มต้นคือสิ่งที่คุณกำลังทำ@synthesize name = _name;
stackoverflow.com/a/12119360/1052616
นี่คือตัวอย่างของวิธีที่คุณจะทำงานให้สำเร็จ:
#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();
};
ฉันหวังว่านี่จะช่วยได้.
แก้ไข 12 มีนาคม 2555:
สำหรับ ARC ไม่จำเป็นต้องมีการเปลี่ยนแปลงใด ๆ เนื่องจาก ARC จะจัดการบล็อกให้กับคุณตราบใดที่มันถูกกำหนดเป็นสำเนา คุณไม่จำเป็นต้องตั้งค่าคุณสมบัติเป็นศูนย์ใน destructor ของคุณเช่นกัน
สำหรับการอ่านเพิ่มเติมโปรดตรวจสอบเอกสารนี้: http://clang.llvm.org/docs/AutomaticReferenceCounting.html
สำหรับ Swift เพียงใช้การปิด: ตัวอย่าง
ในวัตถุประสงค์ -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'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".
นี่คือไฟล์. 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();
}
ด้วยระบบที่ทันสมัย (2014+) ทำสิ่งที่แสดงไว้ที่นี่ มันง่ายมาก
strong
แทนcopy
?
nonatomic
แตกต่างจากแนวปฏิบัติที่ดีที่สุดสำหรับกรณีส่วนใหญ่ที่ใช้คุณสมบัติ
เพื่อประโยชน์ของลูกหลาน / ความสมบูรณ์ ... ต่อไปนี้เป็นสองตัวอย่างเต็มรูปแบบของวิธีการใช้ "วิธีการทำสิ่งต่าง ๆ " ที่น่าขันนี้ @ 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
โง่? ใช่. มีประโยชน์หรือไม่ นรกใช่ นี่คือวิธีที่แตกต่างกันคือ "more atomic" ในการตั้งค่าคุณสมบัติ .. และคลาสที่มีประโยชน์อย่างน่าขัน ...
@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
สิ่งนี้แสดงให้เห็นถึงการตั้งค่าคุณสมบัติบล็อกผ่าน accessor (แม้ว่าจะอยู่ภายใน init, เป็นการฝึกแบบ debatably .. ) เทียบกับกลไก "nonatomic" "getter" ตัวอย่างแรก ไม่ว่าในกรณีใด…การใช้งาน "hardcoded" สามารถเขียนทับได้ตลอดเวลาเช่น .. 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'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
ตอนนี้เมื่อคุณสร้างปุ่มคุณไม่ต้องสร้าง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
ในคณะกรรมาธิการแม่น้ำโขงบล็อกจับตัวแปรบริบทได้รับการจัดสรรในสแต็ค ; พวกเขาจะได้รับการปล่อยตัวเมื่อกองเฟรมถูกทำลาย หากพวกเขาถูกคัดลอกบล็อกใหม่จะถูกจัดสรรในฮีปซึ่งสามารถดำเนินการได้ในภายหลังหลังจากสแต็กเฟรมจะปรากฏขึ้น
นี่ไม่ใช่จุดประสงค์ที่จะเป็น "คำตอบที่ดี" เนื่องจากคำถามนี้ถามอย่างชัดเจนสำหรับ 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!")
}
}
สวัสดีสวิฟท์
การเสริมสิ่งที่ @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
สำหรับข้อมูลเพิ่มเติมดูได้ที่นี่