2010-11-24 99 views
13

在iOS 4之前,我曾經爲添加到地圖視圖中的每個MKAnnotationView添加了一個觀察者,監聽它的選定方法,因此我知道用戶何時點擊了某個圖釘。如何測試方法的協議?

這工作正常到iOS 4.2。我注意到,在發佈的註釋視圖實際上被重用,並以某種方式與觀察者混淆。

因此,我想我可以使用MKMapViewDelegate中的-mapview:didSelectAnnotationView:方法來滿足我的需求,但這只是添加到iOS 4.0 SDK中。

因此,爲了保持兼容性,我想在我的代理上實現此方法,並有條件地檢查MKMapViewDelegate協議中是否存在此方法,以便如果它不存在,我將添加我的觀察者到註釋視圖。

我該如何做一個協議方法,類似於我們如何檢查一個類是不是零?

UPDATE

正如丹尼爾Dickison指出的那樣,我不能使用respondsToSelector:,因爲我的委託已實施了4.0+設備-mapview:didSelectAnnotationView:。我需要的是檢查該設備上的協議是否有可選-mapview:didSelectAnnotationView:方法如果MKMapView將在其代理上查找該方法。

我結束了對當前運行的iOS版本的測試。如果它高於4.0,MKMapView將查找該方法並調用它。

if ([[[UIDevice currentDevice] systemVersion] doubleValue] < 4.0) 
    [self setupObserver]; 

這解決了原來的問題,但它仍然將是有趣的檢查實際協議的方法,不知何故。

回答

5

這是一個棘手的一個。因此,如果我正確理解您的問題,您想在運行時查明地圖視圖是否將mapView:didSelectAnnotationView:消息發送給其代表。但是您不能使用conformsToProtocol:respondsToSelector:,因爲您正在實施委託,所以顯然您正在採用該協議並實施該方法。

我唯一能想到的就是檢查在iOS 4中添加到MKMapView(不是代表)的其他方法,如:mapRectThatFits:

另一種可能性是使用Objective-C runtime library來查詢協議對象。但是這可能是過度殺毒,我也不認爲它會起作用,因爲在您構建應用程序時Protocol object is created by the compiler,並且有可能您將獲得UIKit SDK定義的MKMapViewDelegate協議對象,而不是運行時編譯的任何內容。

2

我會用respondsToSelector:方法,因爲它允許你檢查的具體方法(這聽起來像你正在做的,否則,如果你」重新尋找一個特定的協議,@埃裏克的答案是一個很好的答案)。這個SO post談到使用這種方式。

基本上,你會使用它的方式是

SEL methodName = @selector(mymethod:); 
BOOL test = [object respondsToSelector:methodName]; 
+1

克里斯 - 你可能不想使用保留關鍵字選擇SEL作爲變量名稱。 – DHamrick 2010-11-24 21:34:48

+0

@DHamrick,你是絕對正確的......很愚蠢......我很快就寫下了它,並有一個臉掌... – 2010-11-24 23:02:47

+1

你不能使用「respondsToSelector」,因爲你會問它的唯一對象是你自己!你需要知道MKMapView是否會從委託中尋找這種方法... – 2010-11-24 23:14:39

0

我採取了稍微不同的方法。

我只需使用#ifdef (__iPHONE_OS_VERSION_MIN_REQUIRED...,並根據需要添加觀察者,並使用-mapview:didSelectAnnotationView:委託方法。

26

由於沒有對象實例,您可以詢問它是否響應消息選擇器,並且您已經知道該協議受支持但您只是在尋找一種方法 - 您需要使用protocol_getMethodDescription,就像這樣(方法是類實例和可選的)在那裏你檢查一個零返回值:

#import <objc/runtime.h> 

struct objc_method_description hasMethod = protocol_getMethodDescription(@protocol(MKMapViewDelegate), @selector(mapView:didSelectAnnotationView:), NO, YES); 

if (hasMethod.name != NULL) 
{ 
... 
}