我知道委托如何工作,我也知道怎么使用。但是我该怎么创建呢?
原文:
How do I create delegates in Objective-C? I know how delegates work, and I know how I can use them. But how do I create them?
I know how delegates work, and I know how I can use them. But how do I create them?
这是问题原文链接
欢迎选择我的课程,让我们一起见证您的进步~~
Objective-C 委託就是已被指定為另一個委託的對象,沒有特殊的創建過程,你只要定義一個實現你感興趣的委託方法的類別就可以。 (雖然委託使用正式協議,但你必須得聲明委託來執行該協議,如下所示) 例如,假設你有個NSWindow。如果你想要實作他的委託的 windowDidMove: 方法,你可以建立一個這樣的類別:
@implementation MyClass - (void)windowDidMove:(NSNotification*)notification { // ... } @end
然後建立一個MyClass的實例並制定為window的委託:
MyClass *myDelegate = [[MyClass alloc] init]; [window setDelegate: myDelegate];
在NSWindow方面, 可能有類似此的程式碼使用 respondsToSelector: 來看委託是否回應windowDidMove:訊息,如何合適就發送。
if([[self delegate] respondsToSelector:@selector(windowDidMove:)]) { [[self delegate] windowDidMove:notification]; }
委託資源本身是典型聲明的weak(ARC)或assign(預ARC)來避免循環,因為物件委託經常持有強引用該物件(例如,一個視圖控制器通常包含視圖委託) 要定義自己的委託,你需要在某個地方聲明方法。有兩個基本的方法,蘋果的文檔協議有討論過.
這個就跟NSWindow差不多,在NSObject的類別實作。例如,繼續上面的例子,這是從NSWindow.h:轉述的:
@interface NSObject(NSWindowNotifications) - (void)windowDidMove:(NSNotification *)notification; // ... 其他方法 @end
就像上面描述的那樣,當呼叫這個函數的時候,你會使用-respondsToSelector:,委託簡單的實作此方法,就完成了。這個方法在蘋果的庫裡是直接常見的,但是新的程式碼應該是用下面的更現代的方法。
新的選擇是聲明一個正式的協議。聲明應該像這個樣子:
@protocol NSWindowNotifications <NSObject> @optional - (void)windowDidMove:(NSNotification *)notification; // ...其他方法 @end
這類似於一個藉口或抽象基底類,因為這為委託建立了一個特殊的類型,這種情況下是NSWindowNotifications。委託執行者應該採用這個協議:
@interface MyDelegate <NSWindowNotifications> // ... @end
然後再協定中執行方法。對於諸如@optional(就和大多數委託方法一樣)在協定中聲明的方法,你仍然需要在呼叫特殊方法之前檢查-respondsToSelector:。 蘋果建議這種方法,因為這個更精確,不會和NSObject弄混,並且提供更好的工具支援。
取代檢查委託是否回應選擇器,你可以在設定委託時儲存相關資訊。使用bitfield是一個非常清楚的方法,如下:
@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 <JSSomethingDelegate>)aDelegate { if (delegate != aDelegate) { delegate = aDelegate; delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)]; delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)]; } } @end
然後,在正文裡,可以透過存取delegateRespondsTo來檢查委託處理郵件,而不是一遍又一遍的發送-respondsToSelector:。
上面的方法很厲害!但如果你想在1分鐘內解決問題可以試試看這個: MyClass.h檔案應該像這個樣子(用註解加上委託行)
#import <BlaClass/BlaClass.h> @class MyClass; //定义类,这样协议可以看到MyClass @protocol MyClassDelegate //定义委托协议 - (void) myClassDelegateMethod: (MyClass *) sender; //定义在另一个类里实现的委托方法 @end //结束协议 @interface MyClass : NSObject { } @property (nonatomic, weak) id <MyClassDelegate> delegate; //定义 MyClassDelegate为委托 @end
MyClass.m 檔案應該像這樣:
#import "MyClass.h" @implementation MyClass @synthesize delegate; //综合MyClassDelegate 委托 - (void) myMethodToDoStuff { [self.delegate myClassDelegateMethod:self]; //这个会调用在其他类里实现的方法 } @end
為了在其他的類別使用委託(本情況是UIViewController呼叫MyVC)MyVC.h:
#import "MyClass.h" @interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate }
MyVC.m:
myClass.delegate = self; //设置委托至自身的某个地方
執行委託方法:
- (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的實作方式-根據需求尋找方法。 用你自己的對象實現這一點是非常瑣碎的,沒有什麼特別的事情,你甚至可以為讓一個實例有27個對象的NSArray,完全不同類型的對象,其中只有18個有-(void)setToBue;方法,其他的9個沒有。所以在18個需要完成的呼叫setToBlue,就像這樣:
for (id anObject in myArray) { if ([anObject respondsToSelector:@selector(@"setToBlue")]) [anObject setToBlue]; }
另外,委託是不保留的,所以需要在MyClass dealloc方法中將委託設定為nil。
請看下面的教學是如何一步一步介紹iOS中的委託的。
我建立了兩個 ViewControllers (從一個傳送給另一個) 1. FirstViewController 執行委託(提供資料). 2. SecondViewController聲明委託(接收資料).
1. 來自@Jesse Rusak 的回答:
Objective-C 委託就是已被指定為另一個委託的對象,沒有特殊的創建過程,你只要定義一個實現你感興趣的委託方法的類別就可以。 (雖然委託使用正式協議,但你必須得聲明委託來執行該協議,如下所示)
例如,假設你有個NSWindow。如果你想要實作他的委託的 windowDidMove: 方法,你可以建立一個這樣的類別:
然後建立一個MyClass的實例並制定為window的委託:
在NSWindow方面, 可能有類似此的程式碼使用 respondsToSelector: 來看委託是否回應windowDidMove:訊息,如何合適就發送。
委託資源本身是典型聲明的weak(ARC)或assign(預ARC)來避免循環,因為物件委託經常持有強引用該物件(例如,一個視圖控制器通常包含視圖委託)
要定義自己的委託,你需要在某個地方聲明方法。有兩個基本的方法,蘋果的文檔協議有討論過.
1) 一個非正式的協議
這個就跟NSWindow差不多,在NSObject的類別實作。例如,繼續上面的例子,這是從NSWindow.h:轉述的:
就像上面描述的那樣,當呼叫這個函數的時候,你會使用-respondsToSelector:,委託簡單的實作此方法,就完成了。這個方法在蘋果的庫裡是直接常見的,但是新的程式碼應該是用下面的更現代的方法。
2)一個正式的協議
新的選擇是聲明一個正式的協議。聲明應該像這個樣子:
這類似於一個藉口或抽象基底類,因為這為委託建立了一個特殊的類型,這種情況下是NSWindowNotifications。委託執行者應該採用這個協議:
然後再協定中執行方法。對於諸如@optional(就和大多數委託方法一樣)在協定中聲明的方法,你仍然需要在呼叫特殊方法之前檢查-respondsToSelector:。
蘋果建議這種方法,因為這個更精確,不會和NSObject弄混,並且提供更好的工具支援。
最佳化速度
取代檢查委託是否回應選擇器,你可以在設定委託時儲存相關資訊。使用bitfield是一個非常清楚的方法,如下:
然後,在正文裡,可以透過存取delegateRespondsTo來檢查委託處理郵件,而不是一遍又一遍的發送-respondsToSelector:。
2. 來自@Tibidabo
上面的方法很厲害!但如果你想在1分鐘內解決問題可以試試看這個:
MyClass.h檔案應該像這個樣子(用註解加上委託行)
MyClass.m 檔案應該像這樣:
為了在其他的類別使用委託(本情況是UIViewController呼叫MyVC)MyVC.h:
MyVC.m:
執行委託方法:
3. 來自@umop
當用正式的協議方法創建委託支援時,我發現可以確保正確的類型檢查(雖然是運行時間,不是編譯時間),透過添加以下程式碼:
在你的委託存取(setDelegate)程式碼,這個能將錯誤最小化。
4. 來自@Tom Andersen的回答
或許更多的是在於你所缺少的行。
如果從C++的角度來看,委託需要一點時間適應,但基本上「他們只是工作」。
委託實現的方式是:設定NSWindow的委託對象,但是對像只為一個或幾個可能的委託方法執行。所以會發生一些事,NSWindow想要調用對象,它只使用Objective-c的respondsToSelector方法來決定對像是否被調用,然後再調用。這就是objective-c的實作方式-根據需求尋找方法。
用你自己的對象實現這一點是非常瑣碎的,沒有什麼特別的事情,你甚至可以為讓一個實例有27個對象的NSArray,完全不同類型的對象,其中只有18個有-(void)setToBue;方法,其他的9個沒有。所以在18個需要完成的呼叫setToBlue,就像這樣:
另外,委託是不保留的,所以需要在MyClass dealloc方法中將委託設定為nil。
5. 來自@RDC 的回答:
請看下面的教學是如何一步一步介紹iOS中的委託的。
我建立了兩個 ViewControllers (從一個傳送給另一個)
1. FirstViewController 執行委託(提供資料).
2. SecondViewController聲明委託(接收資料).