我知道代表们的工作方式,我知道如何使用它们.
但是我该如何创建呢?
Objective-C委托是已将delegate
另一个对象分配给属性的对象.要创建一个,只需定义一个实现您感兴趣的委托方法的类,并将该类标记为实现委托协议.
例如,假设你有一个UIWebView
.如果你想实现它的委托webViewDidStartLoad:
方法,你可以创建一个这样的类:
@interface MyClass// ... @end @implementation MyClass - (void)webViewDidStartLoad:(UIWebView *)webView { // ... } @end
然后,您可以创建MyClass的实例并将其指定为Web视图的委托:
MyClass *instanceOfMyClass = [[MyClass alloc] init]; myWebView.delegate = instanceOfMyClass;
另一方面UIWebView
,它可能具有与此类似的代码,以查看委托是否响应webViewDidStartLoad:
消息respondsToSelector:
并在适当时发送它.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { [self.delegate webViewDidStartLoad:self]; }
委托属性本身通常声明weak
(在ARC中)或assign
(预ARC)以避免保留循环,因为对象的委托通常持有对该对象的强引用.(例如,视图控制器通常是它包含的视图的委托.)
要定义自己的委托,您必须在某处声明其方法,如Apple Docs on protocols中所述.您通常会声明一个正式的协议.从UIWebView.h转述的声明如下所示:
@protocol UIWebViewDelegate@optional - (void)webViewDidStartLoad:(UIWebView *)webView; // ... other methods here @end
这类似于接口或抽象基类,因为它为您的委托创建了一种特殊类型,UIWebViewDelegate
在本例中.代表实施者必须采用该协议:
@interface MyClass// ... @end
然后实现协议中的方法.对于在协议中声明的方法@optional
(与大多数委托方法一样),您需要-respondsToSelector:
在调用特定方法之前进行检查.
委托方法通常以委托类名称开头命名,并将委托对象作为第一个参数.他们也经常使用意志,应该或形式.因此,webViewDidStartLoad:
(第一个参数是Web视图)而不是loadStarted
(不带参数).
每次我们想要给它发消息时,不是检查委托是否响应选择器,而是在设置委托时缓存该信息.一个非常干净的方法是使用位域,如下所示:
@protocol SomethingDelegate@optional - (void)something:(id)something didFinishLoadingItem:(id)item; - (void)something:(id)something didFailWithError:(NSError *)error; @end @interface Something : NSObject @property (nonatomic, weak) id delegate; @end @implementation Something { struct { unsigned int didFinishLoadingItem:1; unsigned int didFailWithError:1; } delegateRespondsTo; } @synthesize delegate; - (void)setDelegate:(id )aDelegate { if (delegate != aDelegate) { delegate = aDelegate; delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)]; delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)]; } } @end
然后,在正文中,我们可以检查我们的委托通过访问我们的delegateRespondsTo
结构来处理消息,而不是-respondsToSelector:
一遍又一遍地发送.
协议出现之前,它是共同使用类别上NSObject
宣布委托可以实现的方法.例如,CALayer
仍然这样做:
@interface NSObject(CALayerDelegate) - (void)displayLayer:(CALayer *)layer; // ... other methods here @end
这基本上告诉编译器任何对象都可以实现displayLayer:
.
然后,您将使用与上述相同的-respondsToSelector:
方法来调用此方法.代理只需实现此方法并分配delegate
属性,就是它(没有声明您符合协议).这种方法在Apple的库中很常见,但是新代码应该使用上面更现代的协议方法,因为这种方法会污染NSObject
(这使得自动完成功能不那么有用)并且使编译器很难警告你有关拼写错误和类似错误的信息.
批准的答案很棒,但如果您正在寻找1分钟的答案,请尝试以下方法:
MyClass.h文件应如下所示(添加带注释的委托行!)
#import@class MyClass; //define class, so protocol can see MyClass @protocol MyClassDelegate //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 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
要在另一个类中使用您的委托(在本例中称为MyVC的UIViewController)MyVC.h:
#import "MyClass.h" @interface MyVC:UIViewController{ //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!"); }
当使用正式的协议方法来创建委托支持时,我发现你可以通过添加类似的东西来确保正确的类型检查(尽管是运行时,而不是编译时):
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) { [NSException raise:@"MyDelegate Exception" format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__]; }
在您的委托访问器(setDelegate)代码中.这有助于减少错误.
也许这更像是你所缺少的:
如果你来自C++这样的观点,代表们会习惯一点 - 但基本上"他们只是工作".
它的工作方式是你设置一些你写为NSWindow的委托的对象,但是你的对象只有许多可能的委托方法中的一个或几个的实现(方法).所以发生了一些事情,并且NSWindow
想要调用你的对象 - 它只是使用Objective-c的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
方法中设置委托.
请!查看以下简单的分步教程,了解Delegates如何在iOS中工作.
在iOS中代表
我创建了两个ViewControllers(用于将数据从一个发送到另一个)
FirstViewController实现委托(提供数据).
SecondViewController声明委托(将接收数据).
作为Apple推荐的一种良好实践,代表(根据定义,这是一种协议)符合NSObject
协议是有益的.
@protocol MyDelegate... @end
&在您的委托中创建可选方法(即不一定需要实现的方法),您可以使用这样的@optional
注释:
@protocol MyDelegate... ... // 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等,所以这是我在C++范例中寻找类似类似物的2分钟.
如果我要向C++/Java程序员解释代表,我会说
代表们是什么?这些是指向另一个类中的类的静态指针.分配指针后,可以调用该类中的函数/方法.因此,您的类的某些函数被"委托"(在C++世界中 - 由类对象指针指向)到另一个类.
什么是协议?从概念上讲,它的作用与您指定为委托类的类的头文件类似.协议是一种明确的方法,用于定义需要在类中实现哪些方法的指针被设置为类中的委托.
我怎样才能在C++中做类似的事情?如果您尝试在C++中执行此操作,则可以通过在类定义中定义指向类(对象)的指针,然后将它们连接到其他类,这些类将提供其他函数作为基类的委托.但是这种布线需要在代码中保留,并且笨拙且容易出错.Objective C假设程序员不擅长维护这个decipline并提供编译器限制来强制执行干净的实现.
委托只是一个为另一个类做一些工作的类.阅读以下代码,了解一个有点愚蠢(但有希望启发)的Playground示例,该示例演示了如何在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!" }
在实际操作中,代表经常在以下情况下使用
当一个类需要将一些信息传递给另一个类时
当一个类想要允许另一个类来自定义它时
除了委托类符合所需的协议之外,这些类不需要事先了解彼此.
我强烈建议阅读以下两篇文章.他们帮助我了解代表甚至比文档更好.
什么是代表团? - Swift开发人员指南
代表团的工作方式 - Swift开发人员指南
好吧,这不是问题的真正答案,但是如果你正在查找如何制作自己的代表,那么更简单的东西可能是更好的答案.
我很难实现我的代表,因为我很少需要.我只能拥有一个委托对象的委托.因此,如果您希望您的代表以单向方式进行通信/传递数据,而不是通过通知更好.
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-(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
type(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@class YourViewController; @protocol YourViewController Delegate @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 ()@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- (void) serviceDidFailWithRequestType:(NSString*)error; - (void) serviceDidFinishedSucessfully:(NSString*)success; @end
这是应该完成某些任务的服务类.它显示了如何定义委托以及如何设置委托.在任务完成后的实现类中,委托调用方法.
@interface ServiceClass : NSObject { id_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{ ServiceClass* _service; } - (void) go; @end @implementation viewController // //some methods // - (void) go { _service = [[ServiceClass alloc] init]; [_service setDelegate:self]; [_service someTask]; }
就是这样,通过在这个类中实现委托方法,控制将在操作/任务完成后返回.