类的方法属性探究下

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
上一篇下一篇

猜你喜欢

热点阅读