一.使用objc_msgSend()發(fā)消息
使用objc_msgSend()沒有參數(shù)提示且不可以傳入?yún)?shù)時需要進(jìn)行下列設(shè)置:
TARGETS->BuildSetting 搜索msg->Enable Strict Checking of objc_msgSend Calls 設(shè)置為NO
- 新建Person類
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
+ (void)eat;
- (void)eat;
- (void)run:(int)meter;
@end
Person.m
#import "Person.h"
@implementation Person
+ (void)eat {
NSLog(@"類方法-吃東西");
}
- (void)eat {
NSLog(@"對象方法-吃東西");
}
- (void)run:(int)meter {
NSLog(@"跑了%d米",meter);
}
@end
- 發(fā)送消息 調(diào)用對象方法:
一般調(diào)用對象方法的方式:
Person *p = [[Person alloc] init];
[p eat];
[p performSelector:@selector(eat)];
使用objc_msgSend發(fā)消息:
Person *p = [[Person alloc] init];
/*
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
self:消息的接收方
op :方法選擇器
... : 更多參數(shù)
*/
objc_msgSend(p, @selector(eat));//無參
objc_msgSend(p, @selector(run:),1000);//有參
- 發(fā)送消息 調(diào)用類方法:
一般調(diào)用類方法的方式:
[Person eat];
[[Person class] performSelector:@selector(eat)];
使用objc_msgSend發(fā)消息:
objc_msgSend([Person class], @selector(eat));
二.使用method_exchangeImplementations()交換方法
-
應(yīng)用場景
攔截系統(tǒng)方法 指定其的具體實(shí)現(xiàn)為自定義的方法 -
實(shí)例
攔截UIImage的imageNamed:方法 讓其判斷圖片是否存在
給UIImage新建一個Category
分類.h文件
#import <UIKit/UIKit.h>
@interface UIImage (UIImage)
+ (__kindof UIImage *)jsh_imageNamed:(NSString *)imageName;
@end
分類.m文件
#import "UIImage+UIImage.h"
#import <objc/message.h>
@implementation UIImage (UIImage)
+ (void)load {
//獲取需要交換的方法
Method imageNamedMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method jsh_imageNamedMethod = class_getClassMethod([UIImage class], @selector(jsh_imageNamed:));
/*
交換方法實(shí)現(xiàn)
交換以后:
調(diào)用imageNamedMethod 就是調(diào)用 jsh_imageNamedMethod
調(diào)用jsh_imageNamedMethod 就是調(diào)用imageNamedMethod
*/
method_exchangeImplementations(imageNamedMethod, jsh_imageNamedMethod);
}
+ (UIImage *)jsh_imageNamed:(NSString *)imageName {
//1.加載圖片
UIImage *image = [UIImage jsh_imageNamed:imageName];//此處本質(zhì)是調(diào)用imageNamed
//2.判斷圖片是否存在
if (!image) {
NSLog(@"%@圖片不存在",imageName);
}
return image;
}
@end
運(yùn)行下列代碼可以判斷圖片名為123的圖片是否存在:
UIImage *image = [UIImage imageNamed:@"123"];
三.使用class_addMethod()動態(tài)添加方法
新建一個Animation類:
Animation.h
#import <Foundation/Foundation.h>
@interface Animation : NSObject
@end
Animation.m
#import "Animation.h"
@implementation Animation
@end
運(yùn)行下列代碼:
Animation *animation = [[Animation alloc] init];
[animation performSelector:@selector(eat)];
控制臺報錯:
'-[Animation eat]: unrecognized selector sent to instance
-
解決方案
更改Animation.m文件
#import "Animation.h"
#import <objc/message.h>
/*
方法類型
void(id,SEL)
id SEL 是隱含參數(shù)
*/
//此處方法名可以隨意定義
void eat(id self,SEL _cmd) {
NSLog(@"動物在吃東西");
};
@implementation Animation
/*
當(dāng)我們調(diào)用了一個沒有實(shí)現(xiàn)的方法時,系統(tǒng)就會調(diào)用這個方法
如果我們調(diào)用的方法有實(shí)現(xiàn),系統(tǒng)是不會調(diào)用這個方法的
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat)) {
/*
class_addMethod()參數(shù)介紹:
cls:給哪個類添加方法
name:添加方法的方法編號
imp:方法實(shí)現(xiàn)/函數(shù)入口/函數(shù)名
types:方法類型/函數(shù)類型
*/
/*
types介紹:
v 代表void:函數(shù)bbb()的返回值是空
@ 代表對象:函數(shù)bbb()的第一個參數(shù)是id類型
: 代表SEL:函數(shù)bbb()的第二個參數(shù)是SEL
*/
class_addMethod(self,sel, (IMP)eat, "v@:");
return YES;
} else {
return [super resolveInstanceMethod:sel];
}
}
@end
此時便可正常運(yùn)行下列代碼:
Animation *animation = [[Animation alloc] init];
[animation performSelector:@selector(eat)];
四.使用objc_setAssociatedObject()和objc_getAssociatedObject()為分類添加屬性
- 方法介紹
/**
設(shè)置關(guān)聯(lián)對象
@param object 需要關(guān)聯(lián)的對象
@param key 需要關(guān)聯(lián)的屬性名稱
@param value 需要關(guān)聯(lián)的屬性的值
@param policy 關(guān)聯(lián)策略
*/
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
/**
獲取關(guān)聯(lián)對象
@param object 關(guān)聯(lián)的對象
@param key 關(guān)聯(lián)的屬性名稱
*/
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
- 示例:為NSObject 添加一個含有屬性的分類ShowTime
NSObject+ShowTime.h
#import <Foundation/Foundation.h>
@interface NSObject (ShowTime)
@property (nonatomic,strong) NSDate *currentTime;
@end
NSObject+ShowTime.m
#import "NSObject+ShowTime.h"
#import <objc/message.h>
@implementation NSObject (ShowTime)
- (void)setCurrentTime:(NSDate *)currentTime {
//設(shè)置關(guān)聯(lián)對象 添加屬性
/*
參數(shù)說明:
object:需要關(guān)聯(lián)的對象
key:需要關(guān)聯(lián)的屬性的名稱
value:需要關(guān)聯(lián)的屬性的值
policy:關(guān)聯(lián)策略
*/
objc_setAssociatedObject(self, @"currentTime", currentTime, OBJC_ASSOCIATION_RETAIN);
}
- (NSDate *)currentTime {
//獲取關(guān)聯(lián)對象 獲取屬性的值
return objc_getAssociatedObject(self, @"currentTime");
}
@end
- 關(guān)聯(lián)以后可正常運(yùn)行下列代碼
NSObject *objc = [[NSObject alloc] init];
objc.currentTime = [NSDate date];
NSLog(@"%@",objc.currentTime);