@interface TestObj : NSObject
@property (nonatomic, assign) NSInteger userId;
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, assign) BOOL gender;
+ (NSInteger)test;
+ (NSString *)testWithArg:(NSInteger)arg1 arg2:(NSString *)arg2;
- (BOOL)isChnage;
@end
@implementation TestObj
+ (NSInteger)test
{
NSLog(@"这是测试方法");
return 6;
}
+ (NSString *)testWithArg:(NSInteger)arg1 arg2:(NSString *)arg2
{
NSLog(@"测试方法 : %ld -- %@", arg1, arg2);
return @"test";
}
+ (void)test2
{
NSLog(@"内部方法");
}
- (BOOL)isChnage
{
NSLog(@“有没有改变");
return NO;
}
- (NSString *)changeUserName:(NSString *)userName
{
return @"修改后的名字";
}
@end
// 直接调用类方法,返回值需要强转,也可以不处理返回值
NSInteger test1 = (NSInteger)objc_msgSend(objc_getClass("TestObj"), sel_registerName("test"));
NSLog(@"测试1:%ld", test1);
// 调用有参数的类方法
NSString *test2 = (NSString *) objc_msgSend(objc_getClass("TestObj"), @selector(testWithArg:arg2:), 6, @"消息");
NSLog(@"测试2:%@", test2);
// 直接调用TestObj里面的类方法,尽管它没有暴露出来, 这就是运行时的厉害之处
objc_msgSend(objc_getClass("TestObj"), sel_registerName("test2"));
// 调用方法
TestObj *testObj = [TestObj new];
objc_msgSend(testObj, sel_registerName("isChnage"));
unsigned int methodCount;
Method *methods = class_copyMethodList(objc_getClass("TestObj"), &methodCount);
for (int i = 0; i < methodCount; i++) {
Method tempMethod = methods[i];
NSLog(@“方法名称:%s", sel_getName(method_getName(tempMethod)));
NSLog(@“入参个数:%d", method_getNumberOfArguments(tempMethod));
NSLog(@“%s”, method_getTypeEncoding(tempMethod));
NSLog(@“返回值类型:%s”, method_copyReturnType(tempMethod));
IMP imp = method_getImplementation(tempMethod);
NSLog(@"IMP: %@", imp_getBlock(imp));
NSLog(@"---入参----\n");
unsigned int argCount = method_getNumberOfArguments(tempMethod);
for (int j = 0; j < argCount; j++) {
NSLog(@"%s", method_copyArgumentType(tempMethod, j));
}
}
// 手动释放,避免内存泄露
free(methods);
Code | Meaning |
---|---|
| A |
| An |
| A |
| A |
| A |
| An |
| An |
| An |
| An |
| An |
| A |
| A |
| A C++ |
| A |
| A character string ( |
| An object (whether statically typed or typed |
| A class object ( |
| A method selector ( |
[array type] | An array |
{name=type...} | A structure |
(name=type...) | A union |
| A bit field of num bits |
| A pointer to type |
| An unknown type (among other things, this code is used for function pointers) |
Code | Meaning |
---|---|
| |
| |
| |
| |
| |
| |
|
unsigned int propertyCount;
objc_property_t *propertys = class_copyPropertyList(objc_getClass("TestObj"), &propertyCount);
for (int i = 0; i < propertyCount; i++) {
objc_property_t tempProperty = propertys[i];
NSLog(@“成员变量名称:%s", property_getAttributes(tempProperty));
NSLog(@"成员变量描述:%s”, property_getAttributes(tempProperty));
}
// 释放
free(propertys);
- objc_setAssociatedObject(idobject, const void *key, id value, objc_AssociationPolicypolicy) // 设置绑定对象
- objc_getAssociatedObject(idobject, const void *key) // 获得绑定对象
- objc_removeAssociatedObjects(idobject) // 移除绑定对象
- OBJC_ASSOCIATION_ASSIGN =0, /**< Specifies a weak reference to the associated object. */
- OBJC_ASSOCIATION_RETAIN_NONATOMIC =1,/**< Specifies a strong reference to the associated object.
- OBJC_ASSOCIATION_COPY_NONATOMIC =3, /**< Specifies that the associated object is copied.
- OBJC_ASSOCIATION_RETAIN =01401, /**< Specifies a strong reference to the associated object.
- OBJC_ASSOCIATION_COPY =01403 /**< Specifies that the associated object is copied.
// 绑定对象
TestObj2 *a = [TestObj2 new];
a.userID = 1001;
a.userName = @"AbooJan";
a.books = @[@"Harry", @"永无止境", @"编程思想"];
_test = [[TestObj alloc] init];
objc_setAssociatedObject(_test, "testObj", a, OBJC_ASSOCIATION_RETAIN);
objc_setAssociatedObject(_test, "testNum", @(666), OBJC_ASSOCIATION_ASSIGN);
// 获得绑定对象
TestObj2 *a = objc_getAssociatedObject(_test, "testObj”);
NSNumber *testNum = objc_getAssociatedObject(_test, "testNum");
NSLog(@"%@", [a description]);
NSLog(@"%ld", [testNum integerValue]);
/*
* 1. 如果是一个子类,一般在 initialize 方法中替换方法
* 2. 如果是一个分类,一般在 load 方法中实行替换
*
* 在以上两个方法中替换,是保证它在一启动的时候就实行替换
*
*
* load和initialize有很多共同特点,下面简单列一下:
* 1.在不考虑开发者主动使用的情况下,系统最多会调用一次
* 2.如果父类和子类都被调用,父类的调用一定在子类之前
* 3.都是为了应用运行提前创建合适的运行环境
* 4.在使用时都不要过重地依赖于这两个方法,除非真正必要
*
*
* +load 能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。
* +initialize 在其所属类的方法被调用或类初始化时会被调用,否则它可能永远不会被调用
*
*/
+ (void)load
{
NSLog(@"load");
}
+ (void)initialize
{
// dispatch_once是GCD中的一个方法,它保证了代码块只执行一次,并让其为一个原子操作,
// 保证线程的安 全,避免并发引发问题,认为它是Method Swizzling 的最佳实现
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 第一种初始化方法
Class target = [self class];
Method originalMethod = class_getInstanceMethod(target, sel_registerName("description"));
Method swizzMethod = class_getInstanceMethod(target, sel_registerName("swizzle_description"));
// 第二种初始化方法
// Class aClass = object_getClass((id)self);
//
// Method originalMethod = class_getClassMethod(aClass, @selector(description));
// Method swizzledMethod = class_getClassMethod(aClass, @selector(swizzle_description));
/*
* object_getClass((id)self) 与 [self class] 返回的结果类型都是 Class,
* 但前者为元类,后者为其本身,因为此时 self 为 Class 而不是实例。
*/
/*
* 如果类中不存在要替换的方法,那就先用class_addMethod和class_replaceMethod函数添加
* 和替换两个方法的实现;如果类中已经有了想要替换的方法,那么就直接调
* 用 method_exchangeImplementations函数交换了两个方法的 IMP。
* 个人建议将要替换的方法在当前类先实现出来.
*/
// BOOL didAddMethod = class_addMethod(target, method_getName(originalMethod), method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
//
// if (didAddMethod) {
//
// class_replaceMethod(target, method_getName(swizzMethod), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
//
// }else{
// method_exchangeImplementations(originalMethod, swizzMethod);
// }
// 如果确定已添加替换的方法,直接执行替换就可以了
method_exchangeImplementations(originalMethod, swizzMethod);
});
}
- (NSString *)description
{
NSLog(@"原来的方法");
return [super description];
}
- (void)swizzle_description
{
NSLog(@"这是替换的方法");
// 本身替换的方法只会执行一次,当执行以下方法的时候,它就会调用该类原来的方法了
[self aboo_description];
// 如果调用以下方法,就会进入死循环
// [self description];
}