ฉันรู้ว่าผู้ได้รับมอบหมายทำงานอย่างไรและฉันรู้ว่าฉันจะใช้มันอย่างไร
แต่ฉันจะสร้างพวกเขาได้อย่างไร
ฉันรู้ว่าผู้ได้รับมอบหมายทำงานอย่างไรและฉันรู้ว่าฉันจะใช้มันอย่างไร
แต่ฉันจะสร้างพวกเขาได้อย่างไร
คำตอบ:
ผู้รับมอบอำนาจ -C เป็นวัตถุที่ได้รับมอบหมายให้delegate
ทรัพย์สินวัตถุอื่น ในการสร้างคลาสคุณกำหนดคลาสที่ใช้วิธีการมอบสิทธิ์ที่คุณสนใจและทำเครื่องหมายคลาสนั้นว่าเป็นการใช้โปรโตคอลตัวแทน
UIWebView
ตัวอย่างเช่นสมมติว่าคุณมี หากคุณต้องการใช้webViewDidStartLoad:
วิธีการของผู้รับมอบสิทธิ์คุณสามารถสร้างคลาสแบบนี้ได้:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
จากนั้นคุณสามารถสร้างอินสแตนซ์ของ MyClass และกำหนดเป็นผู้รับมอบสิทธิ์ของมุมมองเว็บ:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
ทางUIWebView
ด้านข้างอาจมีรหัสคล้ายกันนี้เพื่อดูว่าผู้ร่วมประชุมตอบกลับwebViewDidStartLoad:
ข้อความโดยใช้respondsToSelector:
และส่งตามความเหมาะสมหรือไม่
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
โดยทั่วไปแล้วคุณสมบัติผู้รับมอบสิทธิ์จะประกาศweak
(ใน ARC) หรือassign
(pre-ARC) เพื่อหลีกเลี่ยงการวนซ้ำเนื่องจากผู้รับมอบสิทธิ์ของวัตถุมักจะมีการอ้างอิงที่แข็งแกร่งกับวัตถุนั้น (ตัวอย่างเช่นตัวควบคุมมุมมองมักจะเป็นตัวแทนของมุมมองที่มีอยู่)
เพื่อกำหนดผู้ได้รับมอบหมายของคุณเองคุณจะต้องประกาศวิธีการของพวกเขาอยู่ที่ไหนสักแห่งตามที่กล่าวไว้ในแอปเปิ้ลเอกสารบนโปรโตคอล คุณมักจะประกาศโปรโตคอลอย่างเป็นทางการ การประกาศถอดความจาก UIWebView.h จะมีลักษณะเช่นนี้:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
นี่คล้ายกับอินเทอร์เฟซหรือคลาสพื้นฐานที่เป็นนามธรรมเนื่องจากมันสร้างชนิดพิเศษสำหรับผู้รับมอบสิทธิ์ของคุณUIWebViewDelegate
ในกรณีนี้ ผู้ดำเนินการตัวแทนจะต้องนำโปรโตคอลนี้ไปใช้:
@interface MyClass <UIWebViewDelegate>
// ...
@end
จากนั้นใช้วิธีการในโปรโตคอล สำหรับวิธีการที่ประกาศในโปรโตคอลเป็น@optional
(เช่นวิธีการมอบสิทธิ์ส่วนใหญ่) คุณจะต้องตรวจสอบ-respondsToSelector:
ก่อนเรียกวิธีการเฉพาะ
โดยทั่วไปวิธีการมอบหมายจะตั้งชื่อโดยเริ่มต้นด้วยชื่อคลาสที่มอบหมายและนำวัตถุมอบหมายเป็นพารามิเตอร์แรก พวกเขายังมักจะใช้รูปแบบที่ควรหรือควรทำ ดังนั้นwebViewDidStartLoad:
(พารามิเตอร์แรกคือมุมมองเว็บ) แทนที่จะเป็นloadStarted
(ไม่มีพารามิเตอร์) ตัวอย่างเช่น
แทนที่จะตรวจสอบว่าผู้รับมอบสิทธิ์ตอบสนองต่อตัวเลือกทุกครั้งที่เราต้องการส่งข้อความคุณสามารถแคชข้อมูลนั้นเมื่อตั้งค่าผู้ได้รับมอบหมาย วิธีหนึ่งที่สะอาดมากในการทำเช่นนี้คือการใช้บิตฟิลด์ดังนี้:
@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
จากนั้นในร่างกายเราสามารถตรวจสอบว่าผู้ได้รับมอบหมายของเราจัดการข้อความโดยเข้าถึงdelegateRespondsTo
struct ของเราแทนที่จะส่ง-respondsToSelector:
ซ้ำแล้วซ้ำอีก
ก่อนโปรโตคอลที่มีอยู่มันเป็นเรื่องธรรมดาที่จะใช้หมวดหมู่ในNSObject
การประกาศวิธีการที่เป็นตัวแทนสามารถใช้ ตัวอย่างเช่นCALayer
ยังคงทำสิ่งนี้:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
นี้จะบอกคอมไพเลอร์ที่วัตถุใด ๆ displayLayer:
ที่อาจใช้
จากนั้นคุณจะใช้-respondsToSelector:
วิธีการเดียวกับที่อธิบายไว้ข้างต้นเพื่อเรียกวิธีนี้ ผู้รับมอบสิทธิ์ใช้วิธีนี้และกำหนดdelegate
คุณสมบัติและนั่นคือ (ไม่มีการประกาศว่าคุณเป็นไปตามโปรโตคอล) วิธีนี้เป็นเรื่องปกติในห้องสมุดของ Apple แต่รหัสใหม่ควรใช้วิธีการโปรโตคอลที่ทันสมัยกว่าด้านบนเนื่องจากวิธีการนี้ทำให้เกิดมลพิษNSObject
(ซึ่งทำให้การเติมข้อความอัตโนมัติมีประโยชน์น้อยกว่า) และทำให้ยากสำหรับคอมไพเลอร์ที่จะเตือนคุณเกี่ยวกับ
unsigned int
ประเภทเพื่อBOOL
เป็นค่าตอบแทนในการเป็นประเภทdelegate respondsToSelector
BOOL
คำตอบที่ได้รับการอนุมัตินั้นดีมาก แต่ถ้าคุณกำลังมองหาคำตอบ 1 นาทีลองทำดังนี้:
ไฟล์ MyClass.h ควรมีลักษณะเช่นนี้ (เพิ่มบรรทัดผู้รับมอบสิทธิ์พร้อมความคิดเห็น!)
#import <BlaClass/BlaClass.h>
@class MyClass; //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject> //define delegate protocol
- (void) myClassDelegateMethod: (MyClass *) sender; //define delegate method to be implemented within another class
@end //end protocol
@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate
@end
ไฟล์ MyClass.m ควรมีลักษณะเช่นนี้
#import "MyClass.h"
@implementation MyClass
@synthesize delegate; //synthesise MyClassDelegate delegate
- (void) myMethodToDoStuff {
[self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class
}
@end
ในการใช้ผู้รับมอบสิทธิ์ของคุณในคลาสอื่น (UIViewController ชื่อ MyVC ในกรณีนี้) MyVC.h
#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}
MyVC.m:
myClass.delegate = self; //set its delegate to self somewhere
ใช้วิธีการมอบหมาย
- (void) myClassDelegateMethod: (MyClass *) sender {
NSLog(@"Delegates are great!");
}
myClass
instantiated ภายใน MyVC.m?
เมื่อใช้วิธีโปรโตคอลอย่างเป็นทางการสำหรับการสร้างการสนับสนุนผู้แทนฉันพบว่าคุณสามารถตรวจสอบประเภทที่เหมาะสม (แม้ว่ารันไทม์ไม่ใช่เวลารวบรวม) โดยการเพิ่มสิ่งที่ชอบ:
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
ในรหัสผู้รับมอบสิทธิ์ accessor (setDelegate) ของคุณ สิ่งนี้จะช่วยลดข้อผิดพลาด
โปรด! ตรวจสอบด้านล่างนี้ทีละขั้นตอนการสอนเพื่อทำความเข้าใจว่าผู้ได้รับมอบหมายทำงานอย่างไรใน iOS
ฉันสร้าง ViewControllers สองรายการ (สำหรับการส่งข้อมูลจากที่หนึ่งไปยังอีก)
บางทีนี่อาจเป็นไปตามแนวทางของสิ่งที่คุณขาดไป:
หากคุณมาจากมุมมอง C ++ ผู้ได้รับมอบหมายจะคุ้นเคยเล็กน้อย แต่โดยทั่วไปแล้วพวกเขาแค่ทำงาน
วิธีการทำงานคือคุณตั้งค่าวัตถุบางอย่างที่คุณเขียนเป็นผู้รับมอบสิทธิ์ไปยัง NSWindow แต่วัตถุของคุณมีการใช้งาน (วิธีการ) สำหรับวิธีการมอบหมายอย่างใดอย่างหนึ่งหรือไม่กี่วิธี มีบางสิ่งเกิดขึ้นและNSWindow
ต้องการเรียกวัตถุของคุณ - เพียงแค่ใช้respondsToSelector
วิธีการของ Objective-c เพื่อตรวจสอบว่าวัตถุของคุณต้องการวิธีการที่เรียกว่าหรือไม่แล้วเรียกมันว่า นี่คือวิธีการทำงานของวัตถุประสงค์ - วิธีการค้นหาตามความต้องการ
การทำสิ่งนี้กับวัตถุของคุณเองไม่มีอะไรพิเศษเกิดขึ้นคุณสามารถมีNSArray
วัตถุ 27 ชนิดวัตถุต่าง ๆ ทั้งหมดมีเพียง 18 ชิ้นเท่านั้นที่มีวิธี-(void)setToBue;
ส่วนอีก 9 ไม่มี ดังนั้นเมื่อต้องการเรียกsetToBlue
ทั้งหมด 18 ที่จำเป็นต้องทำบางสิ่งเช่นนี้:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
สิ่งอื่น ๆ เกี่ยวกับผู้ได้รับมอบหมายคือพวกเขาจะไม่ถูกเก็บไว้ดังนั้นคุณต้องตั้งผู้รับมอบสิทธิ์nil
ในMyClass dealloc
วิธีการของคุณ
ตามแนวทางปฏิบัติที่ดีที่ Apple แนะนำให้ใช้สำหรับผู้รับมอบสิทธิ์ (ซึ่งเป็นโปรโตคอลตามข้อกำหนด) เพื่อให้สอดคล้องกับNSObject
โปรโตคอล
@protocol MyDelegate <NSObject>
...
@end
& เพื่อสร้างวิธีเลือกเพิ่มเติมภายในผู้รับมอบสิทธิ์ของคุณ (เช่นวิธีที่ไม่จำเป็นต้องนำมาใช้) คุณสามารถใช้@optional
คำอธิบายประกอบดังนี้:
@protocol MyDelegate <NSObject>
...
...
// Declaration for Methods that 'must' be implemented'
...
...
@optional
...
// Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
...
@end
ดังนั้นเมื่อใช้วิธีการที่คุณระบุว่าเป็นตัวเลือกคุณจำเป็นต้องตรวจสอบ (ในชั้นเรียนของคุณ) respondsToSelector
ว่ามุมมอง (ที่สอดคล้องกับผู้รับมอบสิทธิ์ของคุณ) ได้ใช้วิธีการที่เป็นตัวเลือกของคุณจริงหรือไม่
ฉันคิดว่าคำตอบทั้งหมดนี้สมเหตุสมผลมากเมื่อคุณเข้าใจผู้ได้รับมอบหมาย โดยส่วนตัวฉันมาจากดินแดนแห่ง C / C ++ และก่อนหน้าภาษาขั้นตอนเช่น Fortran ฯลฯ ดังนั้นนี่คือ 2 นาทีของฉันในการหา analogues ที่คล้ายกันในกระบวนทัศน์ C ++
ถ้าฉันจะอธิบายผู้ได้รับมอบหมายให้โปรแกรมเมอร์ C ++ / Java ฉันจะบอกว่า
ผู้ได้รับมอบหมายคืออะไร? เหล่านี้เป็นตัวชี้คงที่ไปยังคลาสภายในคลาสอื่น เมื่อคุณกำหนดพอยน์เตอร์คุณสามารถเรียกใช้ฟังก์ชัน / เมธอดในคลาสนั้นได้ ดังนั้นฟังก์ชั่นบางอย่างของคลาสของคุณคือ "มอบหมาย" (ใน C ++ world - ตัวชี้ไปยังโดยตัวชี้วัตถุคลาส) ไปยังคลาสอื่น
โปรโตคอลคืออะไร โดยทั่วไปแล้วมันมีจุดประสงค์คล้ายกับไฟล์ส่วนหัวของคลาสที่คุณกำหนดให้เป็นคลาสตัวแทน โปรโตคอลเป็นวิธีที่ชัดเจนในการกำหนดว่าจะต้องใช้วิธีใดในคลาสที่ตัวชี้ถูกตั้งเป็นผู้รับมอบสิทธิ์ภายในคลาส
ฉันจะทำสิ่งที่คล้ายกันใน C ++ ได้อย่างไร หากคุณพยายามทำสิ่งนี้ใน C ++ คุณจะทำได้โดยการกำหนดพอยน์เตอร์ไปยังคลาส (วัตถุ) ในการกำหนดคลาสและจากนั้นเชื่อมโยงพวกเขาไปยังคลาสอื่น ๆ ที่จะให้ฟังก์ชันเพิ่มเติมในฐานะตัวแทนของคลาสพื้นฐาน แต่การเดินสายนี้จะต้องมีการจัดการภายในรหัสและจะเงอะงะและข้อผิดพลาดได้ง่าย วัตถุประสงค์ C เพิ่งสันนิษฐานว่าโปรแกรมเมอร์ไม่ดีที่สุดในการรักษา decipline นี้และให้ข้อ จำกัด ของคอมไพเลอร์เพื่อบังคับใช้การใช้งานที่สะอาด
ผู้รับมอบสิทธิ์เป็นเพียงคลาสที่ใช้งานได้กับคลาสอื่น อ่านโค้ดต่อไปนี้เพื่อดูตัวอย่างสนามเด็กเล่นที่โง่เขลา (แต่หวังว่าจะรู้แจ้ง) ที่แสดงวิธีการทำสิ่งนี้ใน Swift
// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
// This protocol only defines one required method
func getYourNiceOlderSiblingAGlassOfWater() -> String
}
class BossyBigBrother {
// The delegate is the BossyBigBrother's slave. This position can
// be assigned later to whoever is available (and conforms to the
// protocol).
weak var delegate: OlderSiblingDelegate?
func tellSomebodyToGetMeSomeWater() -> String? {
// The delegate is optional because there might not be anyone
// nearby to boss around.
return delegate?.getYourNiceOlderSiblingAGlassOfWater()
}
}
// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {
// This method is repquired by the protocol, but the protocol said
// nothing about how it needs to be implemented.
func getYourNiceOlderSiblingAGlassOfWater() -> String {
return "Go get it yourself!"
}
}
// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()
// Set the delegate
// bigBro could boss around anyone who conforms to the
// OlderSiblingDelegate protocol, but since lilSis is here,
// she is the unlucky choice.
bigBro.delegate = lilSis
// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
print(replyFromLilSis) // "Go get it yourself!"
}
ในการปฏิบัติจริงผู้ได้รับมอบหมายมักจะใช้ในสถานการณ์ต่อไปนี้
ชั้นเรียนไม่จำเป็นต้องรู้อะไรเกี่ยวกับกันและกันก่อนยกเว้นชั้นผู้รับมอบสิทธิ์จะสอดคล้องกับโปรโตคอลที่จำเป็น
ฉันขอแนะนำให้อ่านบทความสองบทความต่อไปนี้ พวกเขาช่วยให้ฉันเข้าใจผู้ได้รับมอบหมายให้ดียิ่งขึ้นกว่าเอกสารได้
ตกลงนี่ไม่ใช่คำตอบสำหรับคำถาม แต่ถ้าคุณกำลังมองหาวิธีสร้างตัวแทนของคุณเองบางทีสิ่งที่ง่ายกว่าอาจเป็นคำตอบที่ดีกว่าสำหรับคุณ
ฉันแทบไม่ได้ใช้ผู้รับมอบสิทธิ์เพราะไม่ค่อยต้องการ ฉันสามารถมีผู้รับมอบสิทธิ์เพียงคนเดียวสำหรับวัตถุผู้ได้รับมอบหมาย ดังนั้นหากคุณต้องการตัวแทนของคุณสำหรับการสื่อสารทางเดียว / การส่งผ่านข้อมูลมากกว่าที่คุณทำได้ดีกว่าด้วยการแจ้งเตือน
NSNotification สามารถส่งวัตถุไปยังผู้รับมากกว่าหนึ่งคนและใช้งานง่ายมาก มันได้ผลเช่นนี้:
ไฟล์ MyClass.m ควรมีลักษณะเช่นนี้
#import "MyClass.h"
@implementation MyClass
- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case) in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
object:self
userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end
วิธีใช้การแจ้งเตือนของคุณในคลาสอื่น: เพิ่มคลาสเป็นผู้สังเกตการณ์:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];
ใช้เครื่องมือเลือก:
- (void) otherClassUpdatedItsData:(NSNotification *)note {
NSLog(@"*** Other class updated its data ***");
MyClass *otherClass = [note object]; //the object itself, you can call back any selector if you want
NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}
อย่าลืมลบชั้นเรียนของคุณในฐานะผู้สังเกตการณ์ด้วย
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
สมมติว่าคุณมีคลาสที่คุณพัฒนาขึ้นและต้องการประกาศคุณสมบัติของผู้รับมอบสิทธิ์เพื่อให้สามารถแจ้งให้ทราบเมื่อเกิดเหตุการณ์บางอย่าง:
@class myClass;
@protocol myClassDelegate <NSObject>
-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;
@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;
@end
@interface MyClass : NSObject
@property(nonatomic,weak)id< MyClassDelegate> delegate;
@end
ดังนั้นคุณจึงประกาศโปรโตคอลในMyClass
ไฟล์ส่วนหัว (หรือไฟล์ส่วนหัวที่แยกต่างหาก) และประกาศตัวจัดการเหตุการณ์ที่จำเป็น / ตัวเลือกที่ผู้รับมอบสิทธิ์ของคุณต้อง / ควรดำเนินการจากนั้นประกาศคุณสมบัติในMyClass
ประเภท ( id< MyClassDelegate>
) ซึ่งหมายถึงคลาส c วัตถุประสงค์ใด ๆ โพรโทคอลMyClassDelegate
คุณจะสังเกตเห็นว่าคุณสมบัติของผู้รับมอบสิทธิ์ถูกประกาศว่าอ่อนแอนี่เป็นสิ่งสำคัญมากในการป้องกันวงจรการเก็บรักษาMyClass
อินสแตนซ์ดังนั้นหากคุณประกาศให้ผู้รับมอบสิทธิ์เป็นผู้รักษา ของพวกเขาจะถูกปล่อยออกมา)
คุณจะสังเกตเห็นได้ว่าวิธีการโปรโตคอลผ่านMyClass
อินสแตนซ์ที่จะเป็นผู้รับมอบสิทธิ์เป็นพารามิเตอร์นี้เป็นวิธีที่ดีที่สุดในกรณีที่ต้องการตัวแทนไปเรียกวิธีการบางอย่างเกี่ยวกับMyClass
อินสแตนซ์และยังช่วยให้เมื่อผู้ร่วมประชุมประกาศตัวเองเป็นMyClassDelegate
หลายMyClass
กรณีเช่นเมื่อคุณมีหลายUITableView's
อินสแตนซ์ในของคุณViewController
และประกาศว่าตนเองเป็นUITableViewDelegate
ของพวกเขาทั้งหมด
และภายในของMyClass
คุณคุณแจ้งผู้ได้รับมอบหมายด้วยเหตุการณ์ที่ประกาศดังต่อไปนี้:
if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
[_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}
คุณตรวจสอบก่อนว่าผู้รับมอบสิทธิ์ของคุณตอบสนองต่อวิธีการโพรโทคอลที่คุณกำลังจะโทรหาหรือไม่ในกรณีที่ผู้รับมอบสิทธิ์ไม่ได้ใช้งานและแอพจะหยุดทำงานแล้ว (แม้ว่าจำเป็นต้องใช้วิธีโพรโทคอล)
นี่คือวิธีการง่ายๆในการสร้างผู้รับมอบสิทธิ์
สร้างโปรโตคอลในไฟล์. h ตรวจสอบให้แน่ใจว่ามีการกำหนดไว้ก่อนหน้าโปรโตคอลที่ใช้ @class ตามด้วยชื่อของ UIViewController< As the protocol I am going to use is UIViewController class>.
ขั้นตอนที่ 1:สร้างโพรโทคอลคลาสใหม่ชื่อ "YourViewController" ซึ่งจะเป็นคลาสย่อยของคลาส UIViewController และกำหนดคลาสนี้ให้กับ ViewController ที่สอง
ขั้นตอนที่ 2:ไปที่ไฟล์ "YourViewController" และแก้ไขดังต่อไปนี้:
#import <UIKit/UIkit.h>
@class YourViewController;
@protocol YourViewController Delegate <NSObject>
@optional
-(void)defineDelegateMethodName: (YourViewController *) controller;
@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;
@end
@interface YourViewController : UIViewController
//Since the property for the protocol could be of any class, then it will be marked as a type of id.
@property (nonatomic, weak) id< YourViewController Delegate> delegate;
@end
วิธีการที่กำหนดไว้ในลักษณะการทำงานของโปรโตคอลสามารถควบคุมได้ด้วย @optional และ @required เป็นส่วนหนึ่งของข้อกำหนดของโปรโตคอล
ขั้นตอนที่ 3: การนำผู้แทนไปปฏิบัติ
#import "delegate.h"
@interface YourDelegateUser ()
<YourViewControllerDelegate>
@end
@implementation YourDelegateUser
- (void) variousFoo {
YourViewController *controller = [[YourViewController alloc] init];
controller.delegate = self;
}
-(void)defineDelegateMethodName: (YourViewController *) controller {
// handle the delegate being called here
}
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
// handle the delegate being called here
return YES;
}
@end
// ทดสอบว่ามีการกำหนดวิธีการก่อนที่จะเรียกมันหรือไม่
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
ในการสร้างผู้รับมอบสิทธิ์ของคุณเองก่อนอื่นคุณต้องสร้างโปรโตคอลและประกาศวิธีการที่จำเป็นโดยไม่ต้องดำเนินการ จากนั้นใช้โปรโตคอลนี้ในคลาสส่วนหัวของคุณที่คุณต้องการใช้วิธีการมอบหมายหรือผู้รับมอบสิทธิ์
ต้องประกาศโปรโตคอลดังต่อไปนี้:
@protocol ServiceResponceDelegate <NSObject>
- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;
@end
นี่คือคลาสบริการที่ควรทำภารกิจบางอย่าง มันแสดงวิธีการกำหนดผู้รับมอบสิทธิ์และวิธีการตั้งค่าผู้รับมอบสิทธิ์ ในชั้นเรียนการใช้งานหลังจากงานเสร็จสมบูรณ์ตัวแทนของวิธีการที่เรียกว่า
@interface ServiceClass : NSObject
{
id <ServiceResponceDelegate> _delegate;
}
- (void) setDelegate:(id)delegate;
- (void) someTask;
@end
@implementation ServiceClass
- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}
- (void) someTask
{
/*
perform task
*/
if (!success)
{
[_delegate serviceDidFailWithRequestType:@”task failed”];
}
else
{
[_delegate serviceDidFinishedSucessfully:@”task success”];
}
}
@end
นี่คือคลาสมุมมองหลักจากที่เรียกคลาสเซอร์วิสโดยการตั้งค่าผู้รับมอบสิทธิ์ให้กับตัวเอง และยังมีการใช้โปรโตคอลในคลาสส่วนหัว
@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}
- (void) go;
@end
@implementation viewController
//
//some methods
//
- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}
เพียงเท่านี้และโดยการใช้วิธีผู้ร่วมประชุมในคลาสนี้การควบคุมจะกลับมาอีกครั้งเมื่องาน / งานเสร็จสิ้น
คำเตือน: นี้เป็นรุ่นของวิธีการสร้างSwift
delegate
ดังนั้นผู้ได้รับมอบหมายคืออะไร? …ในการพัฒนาซอฟต์แวร์มีสถาปัตยกรรมโซลูชันที่ใช้ซ้ำได้ทั่วไปที่ช่วยในการแก้ปัญหาที่เกิดขึ้นโดยทั่วไปในบริบทที่กำหนด“ เทมเพลต” เหล่านี้ดังนั้นการพูดจึงเป็นที่รู้จักกันดีในฐานะรูปแบบการออกแบบ ผู้รับมอบสิทธิ์เป็นรูปแบบการออกแบบที่อนุญาตให้วัตถุหนึ่งส่งข้อความไปยังวัตถุอื่นเมื่อมีเหตุการณ์เฉพาะเกิดขึ้น ลองนึกภาพวัตถุ A เรียกวัตถุ B เพื่อดำเนินการ เมื่อการดำเนินการเสร็จสิ้นแล้ววัตถุ A ควรรู้ว่า B ดำเนินงานและดำเนินการที่จำเป็นแล้วสิ่งนี้สามารถทำได้ด้วยความช่วยเหลือของผู้ได้รับมอบหมาย!
สำหรับคำอธิบายที่ดีกว่าฉันจะแสดงวิธีสร้างตัวแทนที่กำหนดเองที่ส่งผ่านข้อมูลระหว่างคลาสด้วย Swift ในแอปพลิเคชันง่ายเริ่มต้นด้วยการดาวน์โหลดหรือโคลนโครงการเริ่มต้นและเรียกใช้!
คุณสามารถเห็นแอปที่มีสองชั้นและViewController A
ViewController B
B มีสองมุมมองว่าการแตะเปลี่ยนสีพื้นหลังของViewController
ไม่มีอะไรซับซ้อนเกินไปใช่มั้ย ทีนี้ลองคิดวิธีง่ายๆในการเปลี่ยนสีพื้นหลังของคลาส A เมื่อมุมมองของคลาส B ถูกแตะ
ปัญหาคือว่ามุมมองนี้เป็นส่วนหนึ่งของคลาส B และไม่มีความคิดเกี่ยวกับคลาส A ดังนั้นเราจำเป็นต้องหาวิธีในการสื่อสารระหว่างสองคลาสนี้และนั่นคือสิ่งที่คณะผู้แทนเปล่งประกาย ฉันแบ่งการใช้งานเป็น 6 ขั้นตอนเพื่อให้คุณสามารถใช้สิ่งนี้เป็นแผ่นสูตรโกงเมื่อคุณต้องการ
ขั้นตอนที่ 1: ค้นหาเครื่องหมาย pragma ขั้นตอนที่ 1 ในไฟล์ ClassBVC และเพิ่มสิ่งนี้
//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
ขั้นตอนแรกคือการสร้าง a protocol
ในกรณีนี้เราจะสร้างโปรโตคอลในคลาส B ภายในโปรโตคอลที่คุณสามารถสร้างฟังก์ชั่นได้มากเท่าที่คุณต้องการตามความต้องการของการใช้งานของคุณ ในกรณีนี้เรามีเพียงฟังก์ชั่นง่าย ๆ ที่ยอมรับทางเลือกUIColor
เป็นอาร์กิวเมนต์ เป็นวิธีปฏิบัติที่ดีในการตั้งชื่อโปรโตคอลของคุณโดยเพิ่มคำdelegate
ที่ท้ายชื่อคลาสในกรณีนี้ClassBVCDelegate
นี้
ขั้นตอนที่ 2: ค้นหาเครื่องหมาย pragma ขั้นตอนที่ 2 ClassVBC
และเพิ่มสิ่งนี้
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
ที่นี่เราเพิ่งสร้างคุณสมบัติตัวแทนสำหรับชั้นเรียนคุณสมบัตินี้จะต้องนำมาใช้protocol
ประเภทและมันควรจะเป็นตัวเลือก นอกจากนี้คุณควรเพิ่มคำหลักที่อ่อนแอก่อนที่จะพร็อพเพอร์ตี้เพื่อหลีกเลี่ยงการเก็บรอบและการรั่วไหลของหน่วยความจำที่อาจเกิดขึ้นหากคุณไม่ทราบว่านั่นหมายความว่าไม่ต้องกังวลตอนนี้
ขั้นตอนที่ 3: ค้นหาเครื่องหมาย pragma ขั้นตอนที่ 3 ภายใน handleTap method
ในClassBVC
และเพิ่มสิ่งนี้
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
สิ่งหนึ่งที่คุณควรรู้เรียกใช้แอพและแตะที่มุมมองใด ๆ คุณจะไม่เห็นพฤติกรรมใหม่และถูกต้อง แต่สิ่งที่ฉันต้องการชี้ให้เห็นก็คือแอปนั้นจะไม่ทำงานล้มเหลวเมื่อผู้ร่วมประชุมเรียกและ มันเป็นเพราะเราสร้างมันขึ้นมาเพื่อเป็นค่าเผื่อเลือกและนั่นเป็นเหตุผลว่าทำไมมันจึงไม่ผิดพลาดแม้จะยังไม่มีตัวแทน ตอนนี้เราไปที่ClassAVC
ไฟล์และทำให้มันได้รับมอบหมาย
ขั้นตอนที่ 4: มองหาเครื่องหมาย pragma ขั้นตอนที่ 4 ภายในเมธอด handleTap ClassAVC
และเพิ่มสิ่งนี้ถัดจากประเภทคลาสของคุณเช่นนี้
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
ขณะนี้ ClassAVC นำClassBVCDelegate
โปรโตคอลมาใช้คุณจะเห็นว่าคอมไพเลอร์ของคุณกำลังให้ข้อผิดพลาดที่ระบุว่า“ ประเภท 'ClassAVC ไม่สอดคล้องกับโปรโตคอล' ClassBVCDelegate 'และนี่หมายความว่าคุณไม่ได้ใช้วิธีการของโปรโตคอลเท่านั้นลองจินตนาการว่า เมื่อคลาส A ใช้โพรโทคอลก็เหมือนกับการเซ็นสัญญากับคลาส B และสัญญานี้บอกว่า“ คลาสใดที่รับฉันต้องใช้ฟังก์ชั่นของฉัน!”
บันทึกย่อ: หากคุณมาจากObjective-C
พื้นหลังคุณอาจคิดว่าคุณสามารถปิดข้อผิดพลาดที่ทำให้วิธีการนี้เป็นตัวเลือก แต่สำหรับความประหลาดใจของฉันและอาจเป็นของคุณSwift
ภาษาไม่สนับสนุนตัวเลือกprotocols
ถ้าคุณต้องการที่จะทำมันคุณสามารถสร้าง ส่วนขยายสำหรับคุณprotocol
หรือใช้คำหลัก @objc ในprotocol
การนำไปใช้ของคุณ
โดยส่วนตัวถ้าฉันต้องสร้างโปรโตคอลด้วยวิธีการทางเลือกที่แตกต่างกันฉันต้องการที่จะแบ่งมันเป็นprotocols
วิธีที่ฉันจะทำตามแนวคิดของการให้ความรับผิดชอบหนึ่งเดียวกับวัตถุของฉัน แต่มันอาจแตกต่างกันไปขึ้นอยู่กับการใช้งานเฉพาะ
นี่เป็นบทความที่ดีเกี่ยวกับวิธีการที่เป็นทางเลือก
ขั้นตอนที่ 5: มองหาเครื่องหมาย pragma ขั้นตอนที่ 5 ภายในวิธีการเตรียมการแยกส่วนและเพิ่มสิ่งนี้
//MARK: step 5 create a reference of Class B and bind them through the `prepareforsegue` method.
if let nav = segue.destination as? UINavigationController, let classBVC = nav.topViewController as? ClassBVC {
classBVC.delegate = self
}
ที่นี่เราเพิ่งสร้างตัวอย่างของClassBVC
และมอบหมายผู้แทนให้ตัวเอง แต่ตัวเองที่นี่คืออะไร? ดีตัวเองเป็นClassAVC
ที่ได้รับมอบหมาย!
ขั้นตอนที่ 6: สุดท้ายมองหา pragma ขั้นตอนที่ 6 ClassAVC
และลองใช้ฟังก์ชั่นของprotocol
เริ่มพิมพ์ func changeBackgroundColorแล้วคุณจะเห็นว่ามันกำลังเติมให้คุณโดยอัตโนมัติ คุณสามารถเพิ่มการใช้งานใด ๆ ที่อยู่ภายในตัวอย่างนี้เราจะเปลี่ยนสีพื้นหลังเพิ่มสิ่งนี้
//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
ตอนนี้รันแอพ!
Delegates
มีอยู่ทุกหนทุกแห่งและคุณอาจใช้มันโดยไม่แจ้งให้ทราบล่วงหน้าหากคุณสร้างสิ่งtableview
ก่อนหน้านี้คุณใช้การมอบหมายUIKIT
งานที่มีอยู่รอบตัวพวกเขาและอื่น ๆ อีกมากมายframeworks
พวกเขาแก้ปัญหาหลักเหล่านี้ได้
ขอแสดงความยินดีคุณเพิ่งใช้ผู้รับมอบสิทธิ์ที่กำหนดเองฉันรู้ว่าคุณอาจกำลังคิดว่าจะเกิดปัญหามากขึ้นสำหรับเรื่องนี้? การมอบหมายเป็นรูปแบบการออกแบบที่สำคัญมากที่จะเข้าใจหากคุณต้องการเป็นiOS
นักพัฒนาและจำไว้เสมอว่าพวกเขามีความสัมพันธ์แบบหนึ่งต่อหนึ่งระหว่างวัตถุ
คุณสามารถดูบทช่วยสอนดั้งเดิมได้ที่นี่
คำตอบคือคำตอบจริง แต่ฉันอยากจะให้ "สูตรชีท" แก่คุณในการสร้างผู้แทน:
DELEGATE SCRIPT
CLASS A - Where delegate is calling function
@protocol <#Protocol Name#> <NSObject>
-(void)delegateMethod;
@end
@interface <#Some ViewController#> : <#UIViewController#>
@property (nonatomic, assign) id <<#Protocol Name#>> delegate;
@end
@implementation <#Some ViewController#>
-(void)someMethod {
[self.delegate methodName];
}
@end
CLASS B - Where delegate is called
@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end
@implementation <#Other ViewController#>
-(void)otherMethod {
CLASSA *classA = [[CLASSA alloc] init];
[classA setDelegate:self];
}
-delegateMethod() {
}
@end
ViewController.h
@protocol NameDelegate <NSObject>
-(void)delegateMEthod: (ArgType) arg;
@end
@property id <NameDelegate> delegate;
ViewController.m
[self.delegate delegateMEthod: argument];
MainViewController.m
ViewController viewController = [ViewController new];
viewController.delegate = self;
วิธี:
-(void)delegateMEthod: (ArgType) arg{
}
ในมุมมองของฉันสร้างคลาสที่แยกต่างหากสำหรับวิธีการมอบหมายและคุณสามารถใช้ที่คุณต้องการ
ใน Custom DropDownClass.h ของฉัน
typedef enum
{
DDSTATE,
DDCITY
}DropDownType;
@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;
หลังจากนั้นไฟล์ in.m จะสร้างอาร์เรย์พร้อมวัตถุ
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if (self.delegate) {
if (self.dropDownType == DDCITY) {
cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
}
else if (self.dropDownType == DDSTATE) {
cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissViewControllerAnimated:YES completion:^{
if(self.delegate){
if(self.dropDownType == DDCITY){
[self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
}
else if (self.dropDownType == DDSTATE) {
[self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
}
}
}];
}
ที่นี่ทั้งหมดถูกตั้งค่าสำหรับคลาสผู้แทนกำหนดเองหลังจากนั้นคุณสามารถใช้วิธีการมอบหมายนี้ที่คุณต้องการตัวอย่างเช่น ...
ในการนำเข้า viewcontroller อื่นของฉันหลังจากนั้น
สร้างการดำเนินการสำหรับการเรียกวิธีมอบสิทธิ์แบบนี้
- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}
หลังจากวิธีการมอบหมายการโทรแบบนี้
- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
case DDCITY:{
if(itemString.length > 0){
//Here i am printing the selected row
[self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
}
}
break;
case DDSTATE: {
//Here i am printing the selected row
[self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
}
default:
break;
}
}
มอบหมาย: - สร้าง
@protocol addToCartDelegate <NSObject>
-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;
@end
ส่งและโปรดมอบหมายผู้แทนเพื่อดูว่าคุณกำลังส่งข้อมูล
[self.delegate addToCartAction:itemsModel isAdded:YES];
//1.
//Custom delegate
@protocol TB_RemovedUserCellTag <NSObject>
-(void)didRemoveCellWithTag:(NSInteger)tag;
@end
//2.
//Create a weak reference in a class where you declared the delegate
@property(weak,nonatomic)id <TB_RemovedUserCellTag> removedCellTagDelegate;
//3.
// use it in the class
[self.removedCellTagDelegate didRemoveCellWithTag:self.tag];
//4. import the header file in the class where you want to conform to the protocol
@interface MyClassUsesDelegate ()<TB_RemovedUserCellTag>
@end
// 5 ใช้วิธีการในคลาส. m - (void) didRemoveCellWithTag: แท็ก (NSInteger) {NSLog @ ("แท็ก% d", แท็ก);
}
เริ่มจากตัวอย่างถ้าเราซื้อผลิตภัณฑ์ทางออนไลน์มันต้องผ่านกระบวนการเช่นการจัดส่ง / จัดการโดยทีมที่แตกต่างกันดังนั้นถ้าการจัดส่งเสร็จสมบูรณ์ทีมจัดส่งควรแจ้งทีมการจัดส่ง & ควรเป็นการสื่อสารแบบหนึ่งต่อหนึ่ง จะเป็นค่าใช้จ่ายสำหรับคนอื่น / ผู้ขายอาจต้องการส่งผ่านข้อมูลนี้ไปยังผู้ที่ต้องการเท่านั้น
ดังนั้นหากเราคิดว่าในแง่ของแอพของเราเหตุการณ์อาจเป็นการสั่งซื้อออนไลน์ & ทีมที่แตกต่างกันอาจเป็นหลายมุมมอง
นี่คือรหัสพิจารณา ShippingView เป็นทีมขนส่งและ DeliveryView เป็นทีมจัดส่ง:
//Declare the protocol with functions having info which needs to be communicated
protocol ShippingDelegate : class {
func productShipped(productID : String)
}
//shippingView which shows shipping status of products
class ShippingView : UIView
{
weak var delegate:ShippingDelegate?
var productID : String
@IBAction func checkShippingStatus(sender: UIButton)
{
// if product is shipped
delegate?.productShipped(productID: productID)
}
}
//Delivery view which shows delivery status & tracking info
class DeliveryView: UIView,ShippingDelegate
{
func productShipped(productID : String)
{
// update status on view & perform delivery
}
}
//Main page on app which has both views & shows updated info on product whole status
class ProductViewController : UIViewController
{
var shippingView : ShippingView
var deliveryView : DeliveryView
override func viewDidLoad() {
super.viewDidLoad()
// as we want to update shipping info on delivery view, so assign delegate to delivery object
// whenever shipping status gets updated it will call productShipped method in DeliveryView & update UI.
shippingView.delegate = deliveryView
//
}
}