Objective-C中的@dynamic

來源:互聯網
上載者:User

標籤:

轉自 http://blog.csdn.net/haishu_zheng/article/details/12873151

Objective-C中的@dynamic

 

一、@dynamic與@synthesize的區別

@property有兩個對應的詞,一個是@synthesize,一個是@dynamic。如果@synthesize和@dynamic都沒寫,那麼預設的就是@syntheszie var = _var;

 

@synthesize的語義是如果你沒有手動實現setter方法和getter方法,那麼編譯器會自動為你加上這兩個方法。

 

@dynamic告訴編譯器,屬性的setter與getter方法由使用者自己實現,不自動產生。(當然對於readonly的屬性只需提供getter即可)。假如一個屬性被聲明為@dynamic var,然後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當程式運行到instance.var =someVar,由於缺setter方法會導致程式崩潰;或者當運行到 someVar = var時,由於缺getter方法同樣會導致崩潰。編譯時間沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。

 

二、通過私人變數來實現@dynamic的存取方法

1)Book.h

#import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

 

@interface Book :NSObject

{

 @private

    __strong NSString *_name;

    __strong NSString *_author;

}

 

@property(nonatomic, copy) NSString *name;

@property(nonatomic, copy) NSString *author;

@property(nonatomic, copy) NSString*version;

 

@end

 

2)Book.m

#import "Book.h"

 

@implementation Book

 

@dynamic name;

@dynamicauthor;

@synthesizeversion = _version;

 

- (id)init

{

    self = [super init];

    if(self)

    {

    }

    return self;

}

 

- (NSString *)name

{

    if(nil == _name)

    {

        _name = @"you forgot inputbook name";

    }

    return _name;

}

 

- (void)setName:(NSString *)name

{

    _name = name;

    NSLog(@"_name address:%p", _name);

}

 

- (NSString *)author

{

    if(nil == _author)

    {

        _author = @"you forgot inputbook author";

    }

    return _author;

}

 

- (void)setAuthor:(NSString *)author

{

    _author = author;

}

 

@end

 

從上面的代碼可以看出,用@dynamic後,可以在存取方法中訪問一個私人變數來賦值或取值。而@synthesize則直接用@synthesize var = _var;來讓屬性和私人變數直接等同起來。這就是二者在書寫形式上的差別。

 

三、通過訊息轉寄來實現@dynamic的存取方法

若對於一個屬性使用了@dynamic var = _var,則編譯器立馬報錯。這樣你就無法像@synthesize那樣在var的setter方法和getter方法中使用_var,當然你更不能編寫如下的setter方法和getter方法

- (void)setVar:(id)newVar

{

    self.var =newVar;

}

 

- (void)var

{

    return self.var;

}

這兩個方法都是自己調用自己,會導致無限迴圈直接導致程式崩潰。

 

這裡提供一種利用訊息轉寄機制來實現@dynamic的setter和getter方法。

先上代碼:

1)Book.h

#import <Foundation/Foundation.h>

 

@interface Book :NSObject

{

 @private

    NSMutableDictionary *_propertiesDict;

}

 

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString*author;

@property (nonatomic, copy) NSString*version;

 

@end

2)Book.m

#import "Book.h"

 

@implementation Book

@dynamic    name; // 不能寫成name = _name;否則編譯器馬上報錯

@dynamic    author;

@synthesizeversion;

 

- (id)init

{

    self = [super init];

    if(self)

    {

        _propertiesDict = [[NSMutableDictionary alloc] init];

    }

    

    return self;

}

 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector

{

    NSString *sel = NSStringFromSelector(selector);

    if ([sel rangeOfString:@"set"].location == 0)

    {

        return [NSMethodSignature signatureWithObjCTypes:"[email protected]:@"];

    }

    else

    {

        return [NSMethodSignature signatureWithObjCTypes:"@@:"];

    }

}

 

- (void)forwardInvocation:(NSInvocation *)invocation

{

    NSString *key = NSStringFromSelector([invocation selector]);

    if ([key rangeOfString:@"set"].location == 0)

    {

        key= [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];

        NSString *obj;

       [invocation getArgument:&objatIndex:2];

        [_propertiesDict setObject:obj forKey:key];

    }

    else

    {

        NSString *obj = [_propertiesDict objectForKey:key];

       [invocation setReturnValue:&obj];

    }

}

 

@end

 

3)main.m

#import <Foundation/Foundation.h>

#import "Book.h"

 

int main(int argc, const char * argv[])

{

 

    @autoreleasepool

    {

        Book *book = [[Book alloc] init];

       book.name = @"c++ primer";

       book.author = @"Stanley B.Lippman";

        book.version = @"5.0";

        NSLog(@"%@", book.name);

        NSLog(@"%@", book.author);

        NSLog(@"%@", book.version);

    }

    return 0;

}

 

程式分析:

1)在給程式添加訊息轉寄功能以前,必須覆蓋兩個方法,即methodSignatureForSelector:和forwardInvocation:。methodSignatureForSelector:的作用在於為另一個類實現的訊息建立一個有效方法簽名。forwardInvocation:將選取器轉寄給一個真正實現了該訊息的對象。

 

2)Objective-C中的方法預設被隱藏了兩個參數:self和_cmd。self指向對象本身,_cmd指向方法本身。舉兩個例子來說明:

    例一:- (NSString *)name

這個方法實際上有兩個參數:self和_cmd。

    例二:- (void)setValue:(int)val

這個方法實際上有三個參數:self, _cmd和val。

    被指定為動態實現的方法的參數類型有如下的要求:

    A.第一個參數類型必須是id(就是self的類型)

    B.第二個參數類型必須是SEL(就是_cmd的類型)

    C.從第三個參數起,可以按照原方法的參數類型定義。舉兩個例子來說明:

    例一:setHeight:(CGFloat)height中的參數height是浮點型的,所以第三個參數類型就是f。

    例二:再比如setName:(NSString *)name中的參數name是字串類型的,所以第三個參數類型就是@

 

3)在main.m中有一句代碼是book.name = @"c++ primer";程式運行到這裡時,會去Book.m中尋找setName:這個賦值方法。但是Book.m裡並沒有這個方法,於是程式進入methodSignatureForSelector:中進行訊息轉寄。執行完之後,以"[email protected]:@"作為方法簽名類型返回。

    這裡[email protected]:@是什麼東西呢?實際上,這裡的第一個字元v代表函數的傳回型別是void,後面三個字元參考上面2)中的解釋就可以知道,分別是self, _cmd, name這三個參數的類型id, SEL, NSString。

    接著程式進入forwardInvocation方法。得到的key為方法名稱setName:,然後利用[invocationgetArgument:&objatIndex:2];擷取到參數值,這裡是“c++ primer”。這裡的index為什麼要取2呢?如前面分析,第0個參數是self,第1個參數是_cmd,第2個參數才是方法後面帶的那個參數。

    最後利用一個可變字典來賦值。這樣就完成了整個setter過程。

 

4)在main.m中有一句代碼是 NSLog(@"%@", book.name);,程式運行到這裡時,會去Book.m中尋找name這個取值方法 。但是Book.m裡並沒有這個取值方法,於是程式進入methodSignatureForSelector:中進行訊息轉寄。執行完之後,以"@@:"作為方法簽名類型返回。這裡第一字元@代表函數傳回型別NSString,第二個字元@代表self的類型id,第三個字元:代表_cmd的類型SEL。

    接著程式進入forwardInvocation方法。得到的key為方法名稱name。最後根據這個key從字典裡擷取相應的值,這樣就完成了整個getter過程。

 

5)注意,調試代碼的過程,我們發現只有name和author的賦值和取值進入methodSignatureForSelector:和forwardInvocation:這兩個方法。還有一個屬性version的賦值和取值,並沒有進入methodSignatureForSelector:和forwardInvocation:這兩個方法。這是因為,version屬性被標識為@synthesize,編譯器自動會加上setVersion和version兩個方法,所以就不用訊息轉寄了。

 

四、@dynamic在NSManagedObject的子類中的使用

    @dynamic最常用的使用是在NSManagedObject中,此時不需要顯示編程setter和getter方法。原因是:@dynamic告訴編譯器不做處理,使編譯通過,其getter和setter方法會在運行時動態建立,由Core Data架構為此類屬性產生存取方法。

Objective-C中的@dynamic(轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.