在 Objective-C 中系宫,我們偶爾會看到一個屬性被聲明為 @dynamic, 其實這個是在向編譯器保證,雖然現(xiàn)在這個屬性找不到 setter 和 getter 方法建车,但是在運行時會有可用的實現(xiàn)扩借,你編譯器不要自動幫我合成 ivar 了。我們可以使用 resolveinstancemethod: 方法 和 resolveClassMethod: 方法來動態(tài)提供方法實現(xiàn)缤至,那么 @dynamic 合成屬性的方法是怎么樣的呢潮罪? 我們這里使用一個 Demo 來說明這個過程。
這個 Demo 使用動態(tài)實現(xiàn)來為 NSMutableDictionary 中存儲的屬性動態(tài)創(chuàng)建獲取方法和設(shè)置方法领斥。
創(chuàng)建一個 Person 對象嫉到,該對象有 firstName 和 lastName 這兩個屬性。
// Person.h
@interface Person : NSObject
@property (nonatomic,copy) NSString *firstName;
@property (nonatomic,copy) NSString *lastName;
@end
接下來看 Person.m 文件
#import "Person.h"
#import <objc/runtime.h>
@interface Person ()
@property (nonatomic,strong) NSMutableDictionary *properties;
@end
@implementation Person
// @dynamic, 其實這個是在向編譯器保證月洛,雖然現(xiàn)在這個屬性找不到 setter 和 getter 方法何恶,
// 但是在運行時會有可用的實現(xiàn),你編譯器不要自動幫我合成 ivar 了
@dynamic firstName,lastName;
- (id)init{
if (self = [super init]) {
_properties = [[NSMutableDictionary alloc] init];
}
return self;
}
// getter 方法
static id propertyIMP(id self, SEL _cmd){
return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}
// setter 方法
static void setPropertyIMP(id self,SEL _cmd, id aValue){
id value = [aValue copy];
// 使用 mutableCopy 而不是 copy
NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
// 刪除 “set”
[key deleteCharactersInRange:NSMakeRange(0, 3)];
// 刪除 “:”
[key deleteCharactersInRange:NSMakeRange([key length] -1, 1)];
// 將第一個字母變?yōu)樾? NSString *firstC = [key substringToIndex:1];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstC lowercaseString]];
// 保存對應(yīng)屬性的值
[[self properties] setValue:value forKey:key];
}
// 這里假設(shè)所有不能識別的方法都是 setter 方法或者 getter 方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if ([NSStringFromSelector(sel) hasPrefix:@"set"]) {
// 第一個字符 v 表明返回值是一個 void嚼黔。
// 接下來的二個字符 @: 表明該方法接受一個 id 和一個 SEL
// 最后一個字符是方法的顯式參數(shù) @ 表示是一個 id
class_addMethod([self class], sel, (IMP)setPropertyIMP, "v@:@");
}else{
// 第一個字符 @ 表明返回值是一個 id细层。
// 對于消息傳遞系統(tǒng)來說惜辑,所以的 Objective-C 對象都是 id 類型。
// 接下來的二個字符 @: 表明該方法接受一個 id 和一個 SEL
class_addMethod([self class], sel, (IMP)propertyIMP, "@:@");
}
return YES;
}
@end
從代碼中可以看到疫赎,我們將 firstName,lastName 這2個屬性設(shè)置成 @dynamic 韵丑。然后提供了 static void setPropertyIMP(id self,SEL _cmd, id aValue) 這個 setter 方法,也提供了 static id propertyIMP(id self, SEL _cmd) 這個 getter 方法虚缎。在 setter 和 getter 中使用了 NSMutableDictionary 來存儲和讀取屬性。
當(dāng) Person 無法識別一些方法的時候钓株,Objective-C 的消息分派就會調(diào)用 Person 類的 + (BOOL)resolveInstanceMethod:(SEL)sel 方法实牡。那么我們在這個方法里面 為 Person 這個類動態(tài)的添加屬性的 setter 和 getter 方法。這樣 Person 就可以正確的響應(yīng)屬性的 setter 和 getter创坞。這個就是 Objective-C 的動態(tài)實現(xiàn),在運行過程中提供方法實現(xiàn)题涨。
參考
本文是《iOS 編程實戰(zhàn)》的讀書筆記,對閱讀的內(nèi)容進(jìn)行總結(jié)纲堵。當(dāng)我們看懂了之后,不一定懂闰渔;我們跟著書上代碼敲了一遍之后席函,還是不一定懂;只有我們能夠把自己理解的內(nèi)容寫下來或者通過其它方式表達(dá)出來的時候冈涧,這個才是真的懂了;