簡體   English   中英

的NSDictionary <FBGraphUser> *用戶語法說明

[英]NSDictionary<FBGraphUser> *user syntax explanation

在Facebook iOS SDK中,使用以下處理程序返回請求:

 ^(FBRequestConnection *connection, 
       NSDictionary<FBGraphUser> *user, 
       NSError *error) { }

然后可以使用這些調用來訪問用戶變量......

   self.userNameLabel.text = user.name;
   self.userProfileImage.profileID = user.id;

此語法有點類似於語法id <protocolDelegate> object語法,它是一個公共屬性聲明,除了NSDictionary是明確的id對象,並且該字典符合協議? 但點語法來自何處以及如何聲明任意NSFoundation對象與協議相對應而沒有對對象本身進行子類化並使其符合?

我做了一些關於點符號和NSDictionary的額外研究,看起來如果沒有為NSDictionary添加類別,就不可能在字典上使用點符號。 但是,我沒有在Apple文檔中看到任何<>語法的引用,表明NSDictionary的這個特定實例符合該表示法。

關於這種包裝的工作方式,Facebook文檔有點稀疏:

FBGraphUser協議表示Facebook用戶對象最常用的屬性。 它可用於訪問已使用FBGraphObject外觀包裝的NSDictionary對象。

如果跟隨這個引導到FBGraphObject文檔,那么有一些方法返回符合這個“外觀......”的字典,但沒有關於如何包裝字典的進一步解釋。

所以我想我的問題有幾點:

  1. 底層代碼看起來會使這種語法有效嗎?
  2. 它為什么存在?
  3. 為什么facebook會以這種方式實現它,而不是只創建一個可以將數據轉換成對象的對象?

任何解釋或見解將非常感謝!

基本上, NSDictionary<FBGraphUser> *user表示一個繼承自NSDictionary的對象,添加了FBGraphUser協議聲明的功能(特別是類型化訪問)。

這種方法背后的原因在FBGraphObject文檔中有相當詳細的描述FBGraphUser協議擴展了FBGraphObject協議)。 你可能會感到困惑的是FBGraphObject是一個協議( 在這里描述)和一個類( 在這里描述),它繼承自NSMutableDictionary

在內部實現方面,它是一些非常先進的Objective-C動態魔法,你可能不想擔心。 您需要知道的是,如果您願意,可以將對象視為字典,或者使用協議中的其他方法。 如果您真的想知道詳細信息,可以查看FBGraphObject源代碼 ,特別是這些方法:

#pragma mark -
#pragma mark NSObject overrides

// make the respondsToSelector method do the right thing for the selectors we handle
- (BOOL)respondsToSelector:(SEL)sel
{
    return  [super respondsToSelector:sel] ||
    ([FBGraphObject inferredImplTypeForSelector:sel] != SelectorInferredImplTypeNone);
}

- (BOOL)conformsToProtocol:(Protocol *)protocol {
    return  [super conformsToProtocol:protocol] ||
    ([FBGraphObject isProtocolImplementationInferable:protocol 
                           checkFBGraphObjectAdoption:YES]);
}

// returns the signature for the method that we will actually invoke
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    SEL alternateSelector = sel;

    // if we should forward, to where?
    switch ([FBGraphObject inferredImplTypeForSelector:sel]) {
        case SelectorInferredImplTypeGet:
            alternateSelector = @selector(objectForKey:);
            break;
        case SelectorInferredImplTypeSet:
            alternateSelector = @selector(setObject:forKey:);
            break;
        case SelectorInferredImplTypeNone:
        default:
            break;
    }

    return [super methodSignatureForSelector:alternateSelector];
}

// forwards otherwise missing selectors that match the FBGraphObject convention
- (void)forwardInvocation:(NSInvocation *)invocation {
    // if we should forward, to where?
    switch ([FBGraphObject inferredImplTypeForSelector:[invocation selector]]) {
        case SelectorInferredImplTypeGet: {
            // property getter impl uses the selector name as an argument...
            NSString *propertyName = NSStringFromSelector([invocation selector]);
            [invocation setArgument:&propertyName atIndex:2];
            //... to the replacement method objectForKey:
            invocation.selector = @selector(objectForKey:);
            [invocation invokeWithTarget:self];
            break;
        }
        case SelectorInferredImplTypeSet: {
            // property setter impl uses the selector name as an argument...
            NSMutableString *propertyName = [NSMutableString stringWithString:NSStringFromSelector([invocation selector])];
            // remove 'set' and trailing ':', and lowercase the new first character
            [propertyName deleteCharactersInRange:NSMakeRange(0, 3)];                       // "set"
            [propertyName deleteCharactersInRange:NSMakeRange(propertyName.length - 1, 1)]; // ":"

            NSString *firstChar = [[propertyName substringWithRange:NSMakeRange(0,1)] lowercaseString];
            [propertyName replaceCharactersInRange:NSMakeRange(0, 1) withString:firstChar];
            // the object argument is already in the right place (2), but we need to set the key argument
            [invocation setArgument:&propertyName atIndex:3];
            // and replace the missing method with setObject:forKey:
            invocation.selector = @selector(setObject:forKey:);
            [invocation invokeWithTarget:self]; 
            break;
        } 
        case SelectorInferredImplTypeNone:
        default: 
            [super forwardInvocation:invocation];
            return;
    }
}

此語法有點類似於語法id對象語法

“有點類似”? 怎么“相同”?

那個字典符合協議

Nah,聲明說你必須傳入一個類是NSDictionary的對象,它同時符合FBGraphUser協議。

但點語法來自何處

我不明白這一點。 它來自編寫這段代碼的程序員。 這是可能的,因為FBGraphUser協議聲明了一些屬性,然后可以通過點表示法訪問它們。

如何說明任意NSFoundation對象對應的協議沒有對對象本身進行子類化並使其符合?

它不被稱為“NSFoundation”,只是基金會。 並且它不是與協議“對應”(因為它相當“符合”)的對象,而是它的類。 你只是自己展示了它的語法。

它是如何實現的? 簡單:一個類別。

#import <Foundation/Foundation.h>

@protocol Foo
@property (readonly, assign) int answer;
@end

@interface NSDictionary (MyCategory) <Foo>
@end

@implementation NSDictionary (MyCategory)

- (int)answer
{
    return 42;
}

@end

int main()
{
    NSDictionary *d = [NSDictionary dictionary];
    NSLog(@"%d", d.answer);
    return 0;
}

這是一個SSCCE,即它按原樣編譯和運行,試試吧!

底層代碼看起來會使這種語法有效嗎?

以上回答。

它為什么存在?

因為語言是這樣定義的。

為什么facebook會以這種方式實現它,而不是只創建一個可以將數據轉換成對象的對象?

我不知道,問問Facebook的人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM