And*_*obs 735 cocoa delegates objective-c callback ios
我知道代表们的工作方式,我知道如何使用它们.
但是我该如何创建呢?
Jes*_*sak 882
Objective-C委托是已将delegate另一个对象分配给属性的对象.要创建一个,只需定义一个实现您感兴趣的委托方法的类,并将该类标记为实现委托协议.
例如,假设你有一个UIWebView.如果你想实现它的委托webViewDidStartLoad:方法,你可以创建一个这样的类:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Run Code Online (Sandbox Code Playgroud)
然后,您可以创建MyClass的实例并将其指定为Web视图的委托:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
Run Code Online (Sandbox Code Playgroud)
另一方面UIWebView,它可能具有与此类似的代码,以查看委托是否响应webViewDidStartLoad:消息respondsToSelector:并在适当时发送它.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
Run Code Online (Sandbox Code Playgroud)
委托属性本身通常声明weak(在ARC中)或assign(预ARC)以避免保留循环,因为对象的委托通常持有对该对象的强引用.(例如,视图控制器通常是它包含的视图的委托.)
要定义自己的委托,您必须在某处声明其方法,如Apple Docs on protocols中所述.您通常会声明一个正式的协议.从UIWebView.h转述的声明如下所示:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
Run Code Online (Sandbox Code Playgroud)
这类似于接口或抽象基类,因为它为您的委托创建了一种特殊类型,UIWebViewDelegate在本例中.代表实施者必须采用该协议:
@interface MyClass <UIWebViewDelegate>
// ...
@end
Run Code Online (Sandbox Code Playgroud)
然后实现协议中的方法.对于在协议中声明的方法@optional(与大多数委托方法一样),您需要-respondsToSelector:在调用特定方法之前进行检查.
委托方法通常以委托类名称开头命名,并将委托对象作为第一个参数.他们也经常使用意志,应该或形式.因此,webViewDidStartLoad:(第一个参数是Web视图)而不是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
Run Code Online (Sandbox Code Playgroud)
然后,在正文中,我们可以检查我们的委托通过访问我们的delegateRespondsTo结构来处理消息,而不是-respondsToSelector:一遍又一遍地发送.
协议出现之前,它是共同使用类别上NSObject宣布委托可以实现的方法.例如,CALayer仍然这样做:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
Run Code Online (Sandbox Code Playgroud)
这基本上告诉编译器任何对象都可以实现displayLayer:.
然后,您将使用与上述相同的-respondsToSelector:方法来调用此方法.代理只需实现此方法并分配delegate属性,就是它(没有声明您符合协议).这种方法在Apple的库中很常见,但是新代码应该使用上面更现代的协议方法,因为这种方法会污染NSObject(这使得自动完成功能不那么有用)并且使编译器很难警告你有关拼写错误和类似错误的信息.
Tib*_*abo 379
批准的答案很棒,但如果您正在寻找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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
要在另一个类中使用您的委托(在本例中称为MyVC的UIViewController)MyVC.h:
#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}
Run Code Online (Sandbox Code Playgroud)
MyVC.m:
myClass.delegate = self; //set its delegate to self somewhere
Run Code Online (Sandbox Code Playgroud)
实现委托方法
- (void) myClassDelegateMethod: (MyClass *) sender {
NSLog(@"Delegates are great!");
}
Run Code Online (Sandbox Code Playgroud)
umo*_*mop 18
当使用正式的协议方法来创建委托支持时,我发现你可以通过添加类似的东西来确保正确的类型检查(尽管是运行时,而不是编译时):
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
Run Code Online (Sandbox Code Playgroud)
在您的委托访问器(setDelegate)代码中.这有助于减少错误.
Tom*_*sen 17
也许这更像是你所缺少的:
如果你来自C++这样的观点,代表们会习惯一点 - 但基本上"他们只是工作".
它的工作方式是你设置一些你写为NSWindow的委托的对象,但是你的对象只有许多可能的委托方法中的一个或几个的实现(方法).所以发生了一些事情,并且NSWindow想要调用你的对象 - 它只是使用Objective-c的respondsToSelector方法来确定你的对象是否想要调用该方法,然后调用它.这就是Objective-c的工作原理 - 根据需要查找方法.
用你自己的对象来做这件事是完全无关紧要的,没有什么特别的事情,例如你可以拥有NSArray27个对象,所有不同类型的对象,其中只有18个有方法-(void)setToBue;,其他9个没有.因此,要求setToBlue所有18个需要它完成的东西,如下所示:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
Run Code Online (Sandbox Code Playgroud)
关于委托的另一件事是它们没有被保留,所以你总是必须nil在你的MyClass dealloc方法中设置委托.
Jea*_*ean 15
作为Apple推荐的一种良好实践,代表(根据定义,这是一种协议)符合NSObject协议是有益的.
@protocol MyDelegate <NSObject>
...
@end
Run Code Online (Sandbox Code Playgroud)
&在您的委托中创建可选方法(即不一定需要实现的方法),您可以使用这样的@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
Run Code Online (Sandbox Code Playgroud)
因此,当使用您指定为可选的方法时,您需要(在您的类中)检查respondsToSelector视图(符合您的委托)是否实际实现了可选方法.
DrB*_*Bug 10
我认为,一旦你理解了代表,所有这些答案都很有意义.就个人而言,我来自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!"
}
Run Code Online (Sandbox Code Playgroud)
在实际操作中,代表经常在以下情况下使用
除了委托类符合所需的协议之外,这些类不需要事先了解彼此.
我强烈建议阅读以下两篇文章.他们帮助我了解代表甚至比文档更好.
好吧,这不是问题的真正答案,但是如果你正在查找如何制作自己的代表,那么更简单的东西可能是更好的答案.
我很难实现我的代表,因为我很少需要.我只能拥有一个委托对象的委托.因此,如果您希望您的代表以单向方式进行通信/传递数据,而不是通过通知更好.
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
Run Code Online (Sandbox Code Playgroud)
要在另一个类中使用您的通知:将类添加为观察者:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];
Run Code Online (Sandbox Code Playgroud)
实现选择器:
- (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
}
Run Code Online (Sandbox Code Playgroud)
如果是,请不要忘记以观察员的身份删除您的班级
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Run Code Online (Sandbox Code Playgroud)
假设你有一个你开发的类,并且想要声明一个委托属性,以便在发生某些事件时能够通知它:
@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
Run Code Online (Sandbox Code Playgroud)
所以你在MyClass头文件(或一个单独的头文件)中声明一个协议,并声明你的委托必须/应该实现的必需/可选事件处理程序,然后声明一个MyClasstype(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];
}
Run Code Online (Sandbox Code Playgroud)
首先检查您的委托是否响应您要调用的协议方法,以防委托没有实现它,然后应用程序将崩溃(即使需要协议方法).
这是一个创建委托的简单方法
在.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
Run Code Online (Sandbox Code Playgroud)
协议行为中定义的方法可以使用@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
Run Code Online (Sandbox Code Playgroud)
//在调用之前测试方法是否已定义
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
Run Code Online (Sandbox Code Playgroud)
要创建自己的委托,首先需要创建协议并声明必要的方法,而不实现.然后将此协议实现到要在其中实现委托或委托方法的头类中.
协议必须声明如下:
@protocol ServiceResponceDelegate <NSObject>
- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;
@end
Run Code Online (Sandbox Code Playgroud)
这是应该完成某些任务的服务类.它显示了如何定义委托以及如何设置委托.在任务完成后的实现类中,委托调用方法.
@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
Run Code Online (Sandbox Code Playgroud)
这是通过将委托设置为自身来调用服务类的主视图类.协议也在头类中实现.
@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}
- (void) go;
@end
@implementation viewController
//
//some methods
//
- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}
Run Code Online (Sandbox Code Playgroud)
就是这样,通过在这个类中实现委托方法,控制将在操作/任务完成后返回.
免责声明:这是Swift如何创建delegate.
那么,什么是代表?…在软件开发中,有一些通用的可重用解决方案架构可以帮助解决给定上下文中常见的问题,这些“模板”可以说是最广为人知的设计模式。委托是一种设计模式,它允许一个对象在特定事件发生时向另一个对象发送消息。想象一个对象 A 调用一个对象 B 来执行一个动作。一旦动作完成,对象A应该知道B已经完成任务并采取必要的动作,这可以在委托的帮助下实现!
为了更好地解释,我将向您展示如何创建一个在类之间传递数据的自定义委托,在一个简单的应用程序中使用 Swift,首先下载或克隆这个启动项目并运行它!
您可以看到一个包含两个类的应用程序,ViewController A以及ViewController B. B 有两个视图,点击时会改变 的背景颜色ViewController,没什么太复杂的吧?好吧,现在让我们以一种简单的方式思考,当点击 B 类的视图时,也可以更改 A 类的背景颜色。
问题是这个视图是 B 类的一部分,不知道 A 类,所以我们需要找到一种方法来在这两个类之间进行通信,这就是委托的魅力所在。我将实现分为 6 个步骤,因此您可以在需要时将其用作备忘单。
步骤 1:在 ClassBVC 文件中查找 pragma mark step 1 并添加此
//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
Run Code Online (Sandbox Code Playgroud)
第一步是创建一个protocol,在这种情况下,我们将在类 B 中创建协议,在协议中,您可以根据实现的要求创建任意数量的函数。在这种情况下,我们只有一个简单的函数,它接受一个可选项UIColor作为参数。delegate在类名末尾添加单词来命名您的协议是一个好习惯,在本例中为ClassBVCDelegate.
第 2 步:在第 2 步中查找 pragma 标记ClassVBC并添加它
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
Run Code Online (Sandbox Code Playgroud)
这里我们只是为类创建了一个委托属性,这个属性必须采用protocol类型,并且应该是可选的。此外,您应该在属性之前添加weak 关键字以避免保留循环和潜在的内存泄漏,如果您不知道这意味着什么,现在不要担心,只需记住添加此关键字即可。
step 3: 在handle里面寻找pragma mark step 3Tap methodin ClassBVCand add this
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
Run Code Online (Sandbox Code Playgroud)
您应该知道的一件事是,运行应用程序并点击任何视图,您将看不到任何新行为,这是正确的,但我想指出的是,调用委托时应用程序不会崩溃,并且这是因为我们将它创建为一个可选值,这就是为什么即使委托尚不存在它也不会崩溃。现在让我们去ClassAVC归档并制作它,委托。
第 4 步:在 handleTap 方法中查找第 4 步中的 pragma 标记,ClassAVC并将其添加到您的类类型旁边,如下所示。
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
Run Code Online (Sandbox Code Playgroud)
现在 ClassAVC 采用了ClassBVCDelegate协议,你可以看到你的编译器给你一个错误,说“类型 'ClassAVC 不符合协议 'ClassBVCDelegate' 这只是意味着你还没有使用协议的方法,想象一下当 A 类采用该协议时,就像与 B 类签订了一份合同,而这份合同上写着“任何采用我的类都必须使用我的功能!”
快速说明:如果您来自Objective-C背景,您可能会认为您也可以关闭该错误使该方法可选,但令我惊讶的是,可能是您的,Swift语言不支持 optional protocols,如果你想这样做你可以创建您的扩展protocol或在您的protocol实现中使用 @objc 关键字。
就个人而言,如果我必须使用不同的可选方法创建协议,我更愿意将其分解为不同的protocols,这样我将遵循为我的对象赋予单一责任的概念,但它可以根据具体实现而有所不同。
这是一篇关于可选方法的好文章。
第 5 步:在prepare for segue 方法中寻找pragma mark step 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
}
Run Code Online (Sandbox Code Playgroud)
这里我们只是创建了一个实例ClassBVC并将它的委托分配给了 self,但是这里的 self 是什么?好吧, self 是ClassAVC已委托的!
第 6 步:最后,查找第 6 步中的编译指示ClassAVC,让我们使用 的功能protocol,开始键入 func changeBackgroundColor,您将看到它为您自动完成它。您可以在其中添加任何实现,在此示例中,我们将仅更改背景颜色,添加此。
//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
Run Code Online (Sandbox Code Playgroud)
现在运行应用程序!
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
Run Code Online (Sandbox Code Playgroud)