类的方法属性探究下
2021-06-25 本文已影响0人
Kates
类的属性
@interface HFStudent : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) NSString *nikeName;
@property (nonatomic, assign) NSInteger age;
@end
@implementation HFStudent
@end
int main(int argc, char * argv[]) {
HFStudent *stu = [HFStudent alloc];
}
上面是我们创建的类,接下来我们把他编程的cpp文件
#ifndef _REWRITER_typedef_HFStudent
#define _REWRITER_typedef_HFStudent
typedef struct objc_object HFStudent;
typedef struct {} _objc_exc_HFStudent;
#endif
extern "C" unsigned long OBJC_IVAR_$_HFStudent$_name;
extern "C" unsigned long OBJC_IVAR_$_HFStudent$_nikeName;
extern "C" unsigned long OBJC_IVAR_$_HFStudent$_age;
struct HFStudent_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
NSString *_nikeName;
NSInteger _age;
};
// @property (nonatomic, strong) NSString *name;
// @property (nonatomic, copy) NSString *nikeName;
// @property (nonatomic, assign) NSInteger age;
/* @end */
// @implementation HFStudent
static NSString * _I_HFStudent_name(HFStudent * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_name)); }
static void _I_HFStudent_setName_(HFStudent * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_name)) = name; }
static NSString * _I_HFStudent_nikeName(HFStudent * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_nikeName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_HFStudent_setNikeName_(HFStudent * self, SEL _cmd, NSString *nikeName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct HFStudent, _nikeName), (id)nikeName, 0, 1); }
static NSInteger _I_HFStudent_age(HFStudent * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_HFStudent$_age)); }
static void _I_HFStudent_setAge_(HFStudent * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_HFStudent$_age)) = age; }
// @end
int main(int argc, char * argv[]) {
HFStudent *stu = ((HFStudent *(*)(id, SEL))(void *)objc_msgSend((id)objc_getClass("HFStudent"), sel_registerName("alloc"));
}
从上面我们可以看到类定义在底层会被编译成结构体,而定义的属性会被定义成成员变量,我们在开发的时候,属性会自动生成get和set方法,而这边我看到底层的方法如下
static NSString * _I_HFStudent_name(HFStudent * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_name)); }
static void _I_HFStudent_setName_(HFStudent * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_name)) = name; }
static NSString * _I_HFStudent_nikeName(HFStudent * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_HFStudent$_nikeName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_HFStudent_setNikeName_(HFStudent * self, SEL _cmd, NSString *nikeName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct HFStudent, _nikeName), (id)nikeName, 0, 1); }
static NSInteger _I_HFStudent_age(HFStudent * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_HFStudent$_age)); }
static void _I_HFStudent_setAge_(HFStudent * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_HFStudent$_age)) = age; }
首先我们看到get方法都是通过指针平移的方式获取到对应的值,而set方法有些细微的差异,可以看到setNikeName和其他的不一样,其他都是指针平移然后设置对象,而setNikeName里多了一个objc_setProperty
我们通过llvm代码找到objc_setProperty的
llvm::FunctionCallee getSetPropertyFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
// void objc_setProperty (id, SEL, ptrdiff_t, id, bool, bool)
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
CanQualType Params[] = {
IdType,
SelType,
Ctx.getPointerDiffType()->getCanonicalTypeUnqualified(),
IdType,
Ctx.BoolTy,
Ctx.BoolTy};
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_setProperty");
}
这边创建了objc_setProperty函数,追根溯源我们找到了如下方法
void
CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
const ObjCPropertyImplDecl *propImpl,
llvm::Constant *AtomicHelperFn)
其中里面有段代码如下
PropertyImplStrategy strategy(CGM, propImpl);
switch (strategy.getKind()){...}
这边根据strategy的kind来跳转
case PropertyImplStrategy::GetSetProperty:
case PropertyImplStrategy::SetPropertyAndExpressionGet:
这边我就需要知道strategy的定义,看看里面的kind有做了什么事情, 方法很长,但是我们主要去找一下有没有copy,
PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM,
const ObjCPropertyImplDecl *propImpl)
在此函数中我们找到如下代码
IsCopy = (setterKind == ObjCPropertyDecl::Copy);
if (IsCopy) {
Kind = GetSetProperty;
return;
}
通过分析llvm代码,我们终于知道objc_setProperty函数的由来,当我们的属性由copy修饰时,llvm编译的时候会创建objc_setProperty来处理我们的set方法。
类方法归属分析
这边通过runtime提供的api来打印类和实例对象的方法
@interface HFPerson : NSObject
{
NSObject *objc;
NSString *nickName;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSObject *obj;
- (void)sayHello;
+ (void)sayHappy;
- (void)sayHello{
NSObject *obj;
NSLog(@"HFPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"HFPerson say : Happy!!!");
}
以上是类的定义
通过class_copyMethodList我们可以打印出类里面的方法
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[I];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
lgObjc_copyMethodList(metaClass);
结果:
Method, name: sayHello
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:
Method, name: obj
Method, name: setObj:
***********************************
Method, name: sayHappy
从结果中我们看到并没有sayHappy方法,因为sayHappy方法存放在元类里面
打印类里面是否有该方法
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
结果:lgInstanceMethod_classToMetaclass - 0x100004508-0x0-0x0-0x1000044a0
class_getInstanceMethod:获取实例方法,
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
结果输出:lgClassMethod_classToMetaclass-0x0-0x0-0x1000044a0-0x1000044a0
class_getClassMethod:获取类的方法,这边就有一个疑问了,为什么Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); 这边method4有值,而且跟method3一样。这边我们就要去看class_getClassMethod的方法实现了
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClassMaybeUnrealized())
return (Class)this;
else
return this->ISA();
}
这边cls调用了getMeta方法,看这个方法名似乎是获取元类的,但是跟进去发现 NOT identical to this->ISA when this is a metaclass 意思是如果是元类,返回的是元类本身,所以这边的cls->getMeta() 还是HFPerson元类,返回的结果跟method3一样
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
// sel -> imp 方法的查找流程 imp_farw
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
HFPerson *person = [HFPerson alloc];
Class pClass = object_getClass(person);
lgIMP_classToMetaclass(pClass);
class_getMethodImplementation 获取方法实现
结果:
0x1000010e0-0x7fff6f9de300-0x7fff6f9de300-0x100001120
这边可以看到imp2和imp3是一样的,据我们刚刚class_getInstanceMethod的了解元类里面并不会有实例方法,而类里面也不会有类方法,这时候又需要去看源码了
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
lockdebug_assert_no_locks_locked_except({ &loadMethodLock });
imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
从源码中我们看到如果lookUpImpOrNilTryCache 返回的为null,则结果会返回_objc_msgForward,这也我们就清楚为什么会返回一样的地址了
补充 类编码
void lgTypes(void){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
}Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}
image.png