前言
書接上回鞋诗,經(jīng)過(guò)了小白理論篇穴店,相信大家對(duì)于runtime是什么能有一個(gè)大體的概念了撕捍,恭喜你裝逼神技已經(jīng)加了一點(diǎn)技能點(diǎn)了。同時(shí)也很榮幸泣洞,再次感謝簡(jiǎn)書的小編能給我拉到首頁(yè)忧风。然后這篇文章,介紹一下如何應(yīng)用球凰,加深一下理解狮腿,大家共同進(jìn)步该窗。
there is 正文
1.交換方法
- 使用場(chǎng)景:系統(tǒng)自帶的方法功能不夠,給系統(tǒng)自帶的方法擴(kuò)展一些功能蚤霞,并且保持原有的功能酗失。(可以和繼承系統(tǒng)類,重寫方法達(dá)到一樣效果)
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 需求:給imageNamed方法提供功能昧绣,每次加載圖片就判斷下圖片是否加載成功规肴。
// 步驟一:先搞個(gè)分類,定義一個(gè)能加載圖片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
// 步驟二:交換imageNamed和imageWithName的實(shí)現(xiàn)夜畴,就能調(diào)用imageWithName拖刃,間接調(diào)用imageWithName的實(shí)現(xiàn)。
UIImage *image = [UIImage imageNamed:@"123"];
}
擴(kuò)展
@implementation UIImage (Image)
// 加載分類到內(nèi)存的時(shí)候調(diào)用
+(void)load
{
// 交換方法
// 獲取imageWithName方法地址
Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
// 獲取imageWithName方法地址
Method imageName = class_getClassMethod(self, @selector(imageNamed:));
// 交換方法地址贪绘,相當(dāng)于交換實(shí)現(xiàn)方式
method_exchangeImplementations(imageWithName, imageName);
}
// 不能在分類中重寫系統(tǒng)方法imageNamed兑牡,因?yàn)闀?huì)把系統(tǒng)的功能給覆蓋掉,而且分類中不能調(diào)用super.
// 既能加載圖片又能打印
+(instancetype)imageWithName:(NSString *)name
{
// 這里調(diào)用imageWithName税灌,相當(dāng)于調(diào)用imageName
UIImage *image = [self imageWithName:name];
if (image == nil) {
NSLog(@"加載空的圖片");
}
return image;
}
2.動(dòng)態(tài)添加方法
- 開(kāi)發(fā)使用場(chǎng)景:加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源均函,需要給每個(gè)方法生成映射表,可以使用動(dòng)態(tài)給某個(gè)類菱涤,添加方法解決苞也。(經(jīng)典面試題:有沒(méi)有使用performSelector,其實(shí)主要想問(wèn)你有沒(méi)有動(dòng)態(tài)添加過(guò)方法粘秆。)
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *p = [[Person alloc] init];
// 默認(rèn)person如迟,沒(méi)有實(shí)現(xiàn)eat方法,可以通過(guò)performSelector調(diào)用攻走,但是會(huì)報(bào)錯(cuò)殷勘。
// 動(dòng)態(tài)添加方法就不會(huì)報(bào)錯(cuò)
[p performSelector:@selector(eat)];
}
@end
@implementation Person
// void(*)()
// 默認(rèn)方法都有兩個(gè)隱式參數(shù),
void eat(id self,SEL sel)
{
NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}
// 當(dāng)一個(gè)對(duì)象調(diào)用未實(shí)現(xiàn)的方法昔搂,會(huì)調(diào)用這個(gè)方法處理,并且會(huì)把對(duì)應(yīng)的方法列表傳過(guò)來(lái).
// 剛好可以用來(lái)判斷玲销,未實(shí)現(xiàn)的方法是不是我們想要?jiǎng)討B(tài)添加的方法
+(BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(eat)) {
// 動(dòng)態(tài)添加eat方法
// 第一個(gè)參數(shù):給哪個(gè)類添加方法
// 第二個(gè)參數(shù):添加方法的方法編號(hào)
// 第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址)
// 第四個(gè)參數(shù):函數(shù)的類型,(返回值+參數(shù)類型) v:void @:對(duì)象->self :表示SEL->_cmd
class_addMethod(self, @selector(eat), eat, "v@:");
}
return [super resolveInstanceMethod:sel];
}
@end
3.給分類添加屬性
- 原理:給一個(gè)類聲明屬性巩趁,其實(shí)本質(zhì)就是給這個(gè)類添加關(guān)聯(lián)痒玩,并不是直接把這個(gè)值的內(nèi)存空間添加到類存空間淳附。
@implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 給系統(tǒng)NSObject類動(dòng)態(tài)添加屬性name
NSObject *objc = [[NSObject alloc] init];
objc.name = @"小碼哥";
NSLog(@"%@",objc.name);
}
@end
// 定義關(guān)聯(lián)的key
static const char *key = "name";
@implementation NSObject (Property)
- (NSString *)name
{
// 根據(jù)關(guān)聯(lián)的key议慰,獲取關(guān)聯(lián)的值。
return objc_getAssociatedObject(self, key);
} - (void)setName:(NSString *)name
{
// 第一個(gè)參數(shù):給哪個(gè)對(duì)象添加關(guān)聯(lián)
// 第二個(gè)參數(shù):關(guān)聯(lián)的key奴曙,通過(guò)這個(gè)key獲取
// 第三個(gè)參數(shù):關(guān)聯(lián)的value
// 第四個(gè)參數(shù):關(guān)聯(lián)的策略
objc_setAssociatedObject(self,key,name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
4.字典轉(zhuǎn)模型
- 自動(dòng)根據(jù)一個(gè)字典别凹,生成對(duì)應(yīng)的屬性,和字典中的key一一對(duì)應(yīng)洽糟。
@implementation NSObject (Log)
// 自動(dòng)打印屬性字符串
+(void)resolveDict:(NSDictionary *)dict{
// 拼接屬性字符串代碼
NSMutableString *strM = [NSMutableString string];
// 1.遍歷字典炉菲,把字典中的所有key取出來(lái)堕战,生成對(duì)應(yīng)的屬性代碼
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//類型經(jīng)常變,抽出來(lái)
NSString *type;
if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) {
type = @"NSString";
}else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){
type = @"NSArray";
}else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){
type = @"int";
}else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){
type = @"NSDictionary";
}
// 屬性字符串
NSString *str;
if ([type containsString:@"NS"]) {
str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];
}else{
str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];
}
// 每生成屬性字符串拍霜,就自動(dòng)換行嘱丢。
[strM appendFormat:@"\n%@\n",str];
}];
// 把拼接好的字符串打印出來(lái),就好了祠饺。
NSLog(@"%@",strM);
}
@end