类的原理分析(下)

180 阅读2分钟

setter方法底层实现

声明一个类LKPerson,并添加属性

@interface LKPerson : NSObject

@property (nonatomic, copy) NSString *nickName_nocopy;
@property (atomic, copy) NSString *nickName_copy;
@property (nonatomic) NSString *nickName;
@property (atomic) NSString *nickName_no;
@property (nonatomic, strong) NSString *nickName_nostr;
@property (atomic, strong) NSString *nickName_str;

@end


@implementation LKPerson

@end

通过clang编译成c++文件,查看底层实现

clang -rewrite-objc main.m -o main.cpp

可以看到setget方法

static NSString * _I_LKPerson_nickName_nocopy(LKPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_nocopy)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_LKPerson_setNickName_nocopy_(LKPerson * self, SEL _cmd, NSString *nickName_nocopy) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LKPerson, _nickName_nocopy), (id)nickName_nocopy, 0, 1); }

extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);

static NSString * _I_LKPerson_nickName_copy(LKPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct LKPerson, _nickName_copy), 1); }
static void _I_LKPerson_setNickName_copy_(LKPerson * self, SEL _cmd, NSString *nickName_copy) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LKPerson, _nickName_copy), (id)nickName_copy, 1, 1); }

static NSString * _I_LKPerson_nickName(LKPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName)); }
static void _I_LKPerson_setNickName_(LKPerson * self, SEL _cmd, NSString *nickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName)) = nickName; }

static NSString * _I_LKPerson_nickName_no(LKPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_no)); }
static void _I_LKPerson_setNickName_no_(LKPerson * self, SEL _cmd, NSString *nickName_no) { (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_no)) = nickName_no; }

static NSString * _I_LKPerson_nickName_nostr(LKPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_nostr)); }
static void _I_LKPerson_setNickName_nostr_(LKPerson * self, SEL _cmd, NSString *nickName_nostr) { (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_nostr)) = nickName_nostr; }

static NSString * _I_LKPerson_nickName_str(LKPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_str)); }
static void _I_LKPerson_setNickName_str_(LKPerson * self, SEL _cmd, NSString *nickName_str) { (*(NSString **)((char *)self + OBJC_IVAR_$_LKPerson$_nickName_str)) = nickName_str; }

可以看出,含有copy关键字的实现方式为objc_setProperty,而不包含copy关键字实现方式为内存偏移。

isKindOfClassisMemberOfClass

查看isKindOfClassisMemberOfClass底层实现源码

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

BOOL objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

可以看出,类方法的isKindOfClassisMemberOfClass,都是查找的元类,然后进行比较。实例方法都是查找类来进行比较。isKindOfClassisMemberOfClass的区别在于,isKindOfClass会查找父类,而isMemberOfClass不会。

验证

BOOL reg1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL reg2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL reg3 = [(id)[LKPerson class] isKindOfClass:[LKPerson class]];
BOOL reg4 = [(id)[LKPerson class] isMemberOfClass:[LKPerson class]];
NSLog(@" reg1 :%hhd reg2 :%hhd reg3 :%hhd reg4 :%hhd",reg1,reg2,reg3,reg4);

BOOL reg5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL reg6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL reg7 = [(id)[LKPerson alloc] isKindOfClass:[LKPerson class]];
BOOL reg8 = [(id)[LKPerson alloc] isMemberOfClass:[LKPerson class]];
NSLog(@" reg5 :%hhd reg6 :%hhd reg7 :%hhd reg8 :%hhd",reg5,reg6,reg7,reg8);

reg1是类方法isKindOfClass,应该查找元类,很明显NSObject的元类不是NSObject,继续查找父类,NSObject的元类的父类为NSObject,返回true
reg2是类方法isMemberOfClass,查找元类,NSObject的元类不是NSObject,返回false。
reg3查找LKPerson的元类,然后继续往上找,找到根元类,然后找到NSObject,找到nil,返回false。
reg3查找LKPerson的元类,返回false。

reg5是实例方法isKindOfClass,应该查找类,查找NSObject类比对,返回truereg6是实例方法isMemberOfClass,应该查找类,查找NSObject类比对,返回truereg7查找LKPerson类比对,返回truereg8查找LKPerson类比对,返回true。 查看输出

reg1 :1 reg2 :0 reg3 :0 reg4 :0
reg5 :1 reg6 :1 reg7 :1 reg8 :1