@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
#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
要在另一个类中使用您的委托(在本例中称为MyVC的UIViewController)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
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
// 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!"
}
#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
- (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
}
#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作为协议定义的一部分来控制 .
Step : 3 :Implementation of Delegate
#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
//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
}
//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
//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
//
}
}
19 回答
作为Apple推荐的一种好的做法,代表(根据定义,这是一个协议)符合
NSObject
协议是有益的 .&在您的委托中创建可选方法(即不一定需要实现的方法),您可以使用
@optional
注释,如下所示:因此,当您使用已指定为可选的方法时,如果视图(符合您的委托)实际上已实现您的可选方法,则需要(在您的类中)检查
respondsToSelector
.Objective-C委托是一个已分配给
delegate
属性另一个对象的对象 . 要创建一个,只需定义一个实现您感兴趣的委托方法的类,并将该类标记为实现委托协议 .例如,假设您有一个
UIWebView
. 如果你'd like to implement its delegate' s webViewDidStartLoad:方法,你可以创建一个这样的类:然后,您可以创建MyClass的实例并将其指定为Web视图的委托:
在
UIWebView
方面,它可能具有与此类似的代码,以查看委托是否使用respondsToSelector:响应webViewDidStartLoad:
消息并在适当时发送它 .委托属性本身通常声明为
weak
(在ARC中)或assign
(在ARC之前)以避免保留循环,因为对象的委托通常持有对该对象的强引用 . (例如,视图控制器通常是它包含的视图的委托 . )为您的类创建代理
要定义自己的委托,您必须在某处声明其方法,如Apple Docs on protocols中所述 . 您通常会声明一个正式的协议 . 从UIWebView.h转述的声明如下所示:
这类似于接口或抽象基类,因为它为您的委托创建了一个特殊类型,在这种情况下为
UIWebViewDelegate
. 代表实施者必须采用该协议:然后实现协议中的方法 . 对于在协议中声明为
@optional
的方法(与大多数委托方法一样),在调用特定方法之前,需要使用-respondsToSelector:
进行检查 .命名
委托方法通常以委托类名称开头命名,并将委托对象作为第一个参数 . 他们也经常使用遗嘱,遗嘱或遗嘱 . 因此,
webViewDidStartLoad:
(第一个参数是Web视图)而不是loadStarted
(不带参数) .速度优化
每次我们想要发送消息时,您都可以缓存该信息,而不是检查委托是否响应选择器 . 一个非常干净的方法是使用位域,如下所示:
然后,在正文中,我们可以检查我们的委托通过访问我们的
delegateRespondsTo
结构来处理消息,而不是一遍又一遍地发送-respondsToSelector:
.非正式代表
在协议存在之前,通常在
NSObject
上使用category来声明委托可以实现的方法 . 例如,CALayer
仍然这样做:这基本上告诉编译器任何对象都可能实现
displayLayer:
.然后,您将使用与上述相同的
-respondsToSelector:
方法来调用此方法 . 代理只需实现此方法并分配delegate
属性,并且's it (there'不声明您符合协议) . 这种方法在Apple的库中很常见,但是新代码应该使用上面更现代的协议方法,因为这种方法污染了NSObject
(这使得自动完成功能不那么有用)并且使编译器很难警告你有关拼写错误和类似错误的信息 .批准的答案很棒,但如果您正在寻找1分钟的答案,请尝试以下方法:
MyClass.h文件应如下所示(添加带注释的委托行!)
MyClass.m文件应如下所示
要在另一个类中使用您的委托(在本例中称为MyVC的UIViewController)MyVC.h:
MyVC.m:
实现委托方法
当使用正式协议方法创建委托支持时,我发现您可以通过添加以下内容来确保正确的类型检查(尽管是运行时,而不是编译时):
在您的委托访问器(setDelegate)代码中 . 这有助于减少错误 .
也许这更像是你所缺少的:
如果你来自一个类似C的观点,代表们需要一点点习惯 - 但基本上'他们只是工作' .
它的工作方式是你设置一些你写为NSWindow的委托的对象,但是你的对象只有许多可能的委托方法中的一个或几个的实现(方法) . 所以发生了一些事情,并且
NSWindow
想要调用你的对象 - 它只是使用Objective-c的respondsToSelector
方法来确定你的对象是否想要调用该方法,然后调用它 . 这就是Objective-c的工作方式 - 根据需要查找方法 .用你自己的对象来做这件事是完全无足轻重的,没有什么特别的事情,例如你可以有27个对象,所有不同类型的对象,只有18个有一些方法
-(void)setToBue;
其他9个不要吨 . 所以要在18个需要它的所有18个上调用setToBlue
,这样的事情:关于代表的另一件事是他们没有被保留,所以你总是必须设置您的
MyClass dealloc
方法中的代表nil
.请!查看以下简单的分步教程,了解Delegates如何在iOS中工作 .
我创建了两个ViewControllers(用于将数据从一个发送到另一个)
FirstViewController实现委托(提供数据) .
SecondViewController声明委托(将接收数据) .
我认为,一旦你理解了代表,所有这些答案都很有意义 . 就个人而言,我来自C / C之前和Fortran之类的程序性语言,所以这里是我在C范式中找到类似类比的2分钟 .
如果我要向C / Java程序员解释代表,我会说
代表们是什么?这些是指向另一个类中的类的静态指针 . 分配指针后,可以调用该类中的函数/方法 . 因此,您的类的某些函数被“委托”(在C世界中 - 由类对象指针指向)到另一个类 .
什么是协议?从概念上讲,它的作用与您指定为委托类的类的头文件类似 . 协议是一种明确的方法,用于定义需要在类中实现哪些方法的指针被设置为类中的委托 .
我怎样才能在C中做类似的事情?如果您尝试在C中执行此操作,则可以通过在类定义中定义指向类(对象)的指针,然后将它们连接到其他类,这些类将提供其他函数作为基类的委托 . 但是这种布线需要在代码中保留,并且笨拙且容易出错 . Objective C假设程序员不擅长维护这个decipline并提供编译器限制来强制执行干净的实现 .
Swift版本
委托只是一个为另一个类做一些工作的类 . 阅读以下代码,了解一个有点愚蠢(但希望很有启发性)的Playground示例,该示例演示了如何在Swift中完成此操作 .
在实际操作中,代表经常在以下情况下使用
当一个类需要将一些信息传递给另一个类时
当一个类想要允许另一个类来自定义它时
除了委托类符合所需的协议之外,这些类不需要事先了解彼此 .
我强烈建议阅读以下两篇文章 . 他们帮助我了解代表甚至比documentation更好 .
What is Delegation? – A Swift Developer’s Guide
How Delegation Works – A Swift Developer’s Guide
好吧,这不是问题的真正答案,但是如果你正在查找如何制作自己的代表,那么更简单的东西可能是更好的答案 .
我很难实现我的代表,因为我很少需要 . 我只能拥有一个委托对象的委托 . 因此,如果您希望您的代表以单向方式进行通信/传递数据,那么您通过通知会更好 .
NSNotification可以将对象传递给多个收件人,并且它非常易于使用 . 它的工作原理如下:
MyClass.m文件应如下所示
要在其他类中使用通知:将类添加为观察者:
实现选择器:
如果是,请不要忘记以观察员的身份删除您的 class
假设你有一个你开发的类,并且想要声明一个委托属性,以便在发生某些事件时能够通知它:
所以你在
MyClass
头文件(或一个单独的头文件)中声明一个协议,并声明你的委托必须/应该实现的必需/可选事件处理程序,然后在MyClass
中声明一个类型(id< MyClassDelegate>
)的属性,这意味着任何目标c类符合协议MyClassDelegate
,您会注意到委托属性被声明为弱,这对于防止保留周期非常重要(通常委托保留MyClass
实例,因此如果您将委托声明为保留,则两者都将保持彼此,他们都不会被释放) .您还会注意到协议方法将
MyClass
实例作为参数传递给委托,这是最好的做法,以防委托想要调用MyClass
实例上的某些方法,并且当委托将自身声明为MyClassDelegate
到多个MyClass
实例时也是有帮助的,例如当ViewController
中有多个UITableView's
实例并将其自身声明为UITableViewDelegate
时他们都是 .在
MyClass
内部,您通过声明的事件通知委托,如下所示:首先检查您的委托是否响应您要调用的协议方法,以防委托没有实现它,然后应用程序将崩溃(即使需要协议方法) .
这是创建委托的简单方法
在.h文件中创建协议 . 确保在协议之前使用@class定义后跟UIViewController的名称
< As the protocol I am going to use is UIViewController class>.
Step : 1 : 创建一个名为"YourViewController"的新类协议,它将是UIViewController类的子类,并将此类分配给第二个ViewController .
Step : 2 : 转到"YourViewController"文件并按如下所示进行修改:
协议行为中定义的方法可以使用@optional和@required作为协议定义的一部分来控制 .
Step : 3 : Implementation of Delegate
//在调用之前测试方法是否已定义
要创建自己的委托,首先需要创建协议并声明必要的方法,而不实现 . 然后将此协议实现到要在其中实现委托或委托方法的头类中 .
协议必须声明如下:
这是应该完成某些任务的服务类 . 它显示了如何定义委托以及如何设置委托 . 在任务完成后的实现类中,调用委托的方法 .
这是通过将委托设置为自身来调用服务类的主视图类 . 并且协议也在头类中实现 .
就是这样,通过在这个类中实现委托方法,控制将在操作/任务完成后返回 .
免责声明:这是如何创建
delegate
的Swift
版本 .那么,代表们是什么? ...在软件开发中,有一般的可重用解决方案体系结构有助于解决给定上下文中常见的问题,可以说这些“模板”最为人所知的是设计模式 . 委托是一种设计模式,允许一个对象在特定事件发生时将消息发送到另一个对象 . 想象一下,对象A调用对象B来执行操作 . 一旦动作完成,对象A应该知道B已完成任务并采取必要的行动,这可以在代表的帮助下实现!
为了更好的解释,我将向您展示如何创建一个在类之间传递数据的自定义委托,在一个简单的应用程序中使用Swift,start by downloading or cloning this starter project and run it!
您可以看到一个包含两个类的应用程序,
ViewController A
和ViewController B
. B有两个视图,点按即可更改ViewController
的背景颜色,没什么太复杂的权利吗?现在让我们以一种简单的方式思考,当点击B类视图时,也可以改变A类的背景颜色 .问题是这些视图是B类的一部分,并且不知道A类,所以我们需要找到一种在这两个类之间进行通信的方法,这就是委托发光的地方 . 我将实现分为6个步骤,因此您可以在需要时将其用作备忘单 .
步骤1:在ClassBVC文件中查找pragma标记步骤1并添加它
第一步是创建
protocol
,在这种情况下,我们将在B类中创建协议,在协议内部,您可以根据实现的要求创建任意数量的函数 . 在这种情况下,我们只有一个简单的函数接受一个可选的UIColor
作为参数 . 在类名末尾添加单词delegate
,在这种情况下,ClassBVCDelegate
是一个很好的做法 .步骤2:在
ClassVBC
中查找pragma mark步骤2并添加它这里我们只为类创建一个委托属性,该属性必须采用
protocol
类型,并且它应该是可选的 . 此外,您应该在属性之前添加weak关键字以避免保留周期和潜在的内存泄漏,如果您现在不知道这意味着什么,请记住添加此关键字 .第3步:在
ClassBVC
中的handleTapmethod
中查找pragma mark步骤3并添加此你应该知道的一件事,运行应用程序并点击任何视图,你将看不到任何新行为,这是正确的,但我要指出的是,当调用委托时,应用程序不会崩溃,并且这是因为我们将它创建为可选值,这就是为什么即使委托不存在也不会崩溃的原因 . 现在让我们转到
ClassAVC
文件并制作它,委托 .步骤4:在
ClassAVC
中的handleTap方法中查找pragma mark步骤4,然后将此类型添加到您的类类型旁边 .现在ClassAVC采用了
ClassBVCDelegate
协议,你可以看到你的编译器给你一个错误,上面写着“Type'ClassAVC不符合协议'ClassBVCDelegate',这只意味着你没有使用这些方法对于协议而言,想象一下,当A级采用协议就像签订B级 Contract 一样,这个 Contract 说“任何采用我的 class 都必须使用我的功能!”快速注意:如果你来自
Objective-C
背景,你可能会认为你也可以关闭那个错误,使该方法可选,但令我惊讶的是,可能是你的,Swift
语言不支持可选的protocols
,如果你想这样做您可以为protocol
创建扩展名,或在protocol
实现中使用@objc关键字 .就个人而言,如果我必须创建一个具有不同可选方法的协议,我宁愿将其分解为不同的
protocols
,这样我将遵循对我的对象赋予一个单一责任的概念,但它可以根据具体实现而变化 .here is关于可选方法的好文章 .
步骤5:在prepare for segue方法中查找pragma mark步骤5并添加它
这里我们只是创建一个
ClassBVC
的实例并将其委托分配给self,但这里的self是什么?好吧,自我是ClassAVC
已被委派!步骤6:最后,在
ClassAVC
中查找pragma步骤6,让我们使用protocol
的函数,开始键入func changeBackgroundColor ,您将看到它为您自动完成它 . 你可以在其中添加任何实现,在这个例子中,我们只需更改背景颜色,添加它 .现在运行应用程序!
Delegates
无处不在,你甚至可能在没有通知的情况下使用它们,如果你在过去使用委托创建了一个tableview
,许多类的UIKIT
也在它们周围工作,而且还有很多其他的frameworks
,它们解决了这些主要问题 .避免物体紧密耦合 .
修改行为和外观,无需子类化对象 .
允许将任务处理到任意对象 .
恭喜你,你只是实现了一个自定义委托,我知道你可能正在考虑,为此这么多麻烦?好吧,委托是一个非常重要的设计模式,可以理解你是否想成为一名开发人员,并始终牢记他们在对象之间有一对一的关系 .
你可以看到原始的教程here
ViewController.h
ViewController.m
MainViewController.m
方法:
在我看来,为该委托方法创建单独的类,您可以使用您想要的位置 .
在我的自定义DropDownClass.h中
之后in.m文件创建包含对象的数组,
这里所有都设置为自定义委托类 . 之后你可以在你想要的地方使用这个委托方法 . 例如......
在我的另一个viewcontroller导入之后
像这样创建调用委托方法的动作
之后调用这样的委托方法
答案实际上已经回答,但我想给你一个创建代表的“备忘单”:
代表: - 创建
发送并请指派代表查看您正在发送数据
// 5 . 在类.m中实现方法 - (void)didRemoveCellWithTag:(NSInteger)标记{NSLog @(“Tag%d”,tag);
}
让我们从一个例子开始,如果我们在线购买产品,它会经历由不同团队处理的运输/交付等过程 . 因此,如果运输完成,运输团队应该通知交付团队并且它应该是一对一的通信,因为广播此信息对于其他人/供应商可能希望仅将此信息传递给所需人员 .
因此,如果我们根据我们的应用程序考虑,事件可以是在线订单,不同的团队可以像多个视图一样 .
以下代码将ShippingView视为Shipping team&DeliveryView作为交付团队: