本文共 11638 字,大约阅读时间需要 38 分钟。
在阅读本篇博客前,需要了解:
建议阅读博客:
Dirty memory:在进程运行时会发生更改的内存。
Clean memory:加载后不会发生更改的内存。
ro
属于clean memory
,在编辑时及确定的内存空间,只读,加载后不会发生改变的内存空间,包括类名称、方法、协议和实例变量的信息;rw
的数据空间属于dirty memory
,rw是运行时的结构,可读可写,由于其动态性,可以往类中添加属性、方法、协议。在运行时会发生变更的内存。dirty memory
要比clean memory
昂贵的多,只要进行运行它就必须一直存在,通过分离出那些不会被改变的数据,可以把大部分的类数据存储为clean memory。
SEL : 类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL只是方法编号。
IMP:一个函数指针,保存了方法的地址。
一开始的TWPerson的firstSubclass为nill的原因是因为TWTeacher一开始只有class_ro的clean内存,在第一次使用类的时后候,才会载入class_rw_t的dirty内存,此时有了firstSubclass指针和nextSiblingClass指针。类实际上是树状结构存储遍历。
使用Clang将源文件转换为C++文件。
属性有getter和setter方法。
Setter的本质是内存赋值的封装。
static void _I_TWPerson_setNickname_(TWPerson * self, SEL _cmd, NSString *nickname) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct TWPerson, _nickname), (id)nickname, 0, 1);}static void _I_TWPerson_setName_(TWPerson * self, SEL _cmd, NSString *name) {(*(NSString **)((char *)self + OBJC_IVAR_$_TWPerson$_name)) = name; }
但是setter方法为什么有的是内存平移赋值 有的使用objc_setProperty呢?
查看可执行文件函数表,发现getter和setter方法在编译期函数地址就已经确定,应该是LLVM编译时就确定了。
在getSetPropertyFn()函数中创建了objc_setProperty,那我们继续查找getSetPropertyFn。
GetPropertySetFunction()调用了 getSetPropertyFn()函数。
找它的父类。
下面有个函数:
/// Pick an implementation strategy for the given property synthesis.PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, const ObjCPropertyImplDecl *propImpl) { const ObjCPropertyDecl *prop = propImpl->getPropertyDecl(); ObjCPropertyDecl::SetterKind setterKind = prop->getSetterKind(); IsCopy = (setterKind == ObjCPropertyDecl::Copy); IsAtomic = prop->isAtomic(); HasStrong = false; // doesn't matter here. // Evaluate the ivar's size and alignment. ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl(); QualType ivarType = ivar->getType(); auto TInfo = CGM.getContext().getTypeInfoInChars(ivarType); IvarSize = TInfo.Width; IvarAlignment = TInfo.Align; // If we have a copy property, we always have to use getProperty/setProperty. if (IsCopy) { Kind = GetSetProperty; return; } if (setterKind == ObjCPropertyDecl::Retain) { if (CGM.getLangOpts().getGC() == LangOptions::GCOnly) { } else if (CGM.getLangOpts().ObjCAutoRefCount && !IsAtomic) { if (ivarType.getObjCLifetime() == Qualifiers::OCL_Strong) Kind = Expression; else Kind = SetPropertyAndExpressionGet; return; } else if (!IsAtomic) { Kind = SetPropertyAndExpressionGet; return; } else { Kind = GetSetProperty; return; } } if (!IsAtomic) { Kind = Expression; return; } if (ivar->isBitField()) { Kind = Expression; return; } if (ivarType.hasNonTrivialObjCLifetime() || (CGM.getLangOpts().getGC() && CGM.getContext().getObjCGCAttrKind(ivarType))) { Kind = Expression; return; } if (CGM.getLangOpts().getGC()) if (const RecordType *recordType = ivarType->getAs()) HasStrong = recordType->getDecl()->hasObjectMember(); if (HasStrong) { Kind = CopyStruct; return; } if (!IvarSize.isPowerOfTwo()) { Kind = CopyStruct; return; } llvm::Triple::ArchType arch = CGM.getTarget().getTriple().getArch(); if (IvarAlignment < IvarSize && !hasUnalignedAtomics(arch)) { Kind = CopyStruct; return; } if (IvarSize > getMaxAtomicAccessSize(CGM, arch)) { Kind = CopyStruct; return; } Kind = Native;}
发现使用copy修饰的属性使用objc_setProperty方法实现。
在可编译源代码中查看objc_setProperty
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy){ if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue);}
发现这就是一个深拷贝,重新开辟个内存,新值retain,旧值release。
Table 6-1 Objective-C type encodings | |
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) |
Table 6-2 Objective-C method encodings | |
Code | Meaning |
| |
|
|
|
|
|
|
|
|
|
|
|
|
eg.
types表示:
sayhi是TWPerson对象方法,saybye是TWPerson类方法。
void TWObjc_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)); NSLog(@"Method, name: %@", key); } free(methods);}
void TWInstanceMethod_classToMetaclass(Class pClass){ const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); Method method1 = class_getInstanceMethod(pClass, @selector(sayhi)); Method method2 = class_getInstanceMethod(metaClass, @selector(sayhi)); Method method3 = class_getInstanceMethod(pClass, @selector(saybye)); Method method4 = class_getInstanceMethod(metaClass, @selector(saybye)); NSLog(@"---%s---\n ---对象方法sayhi---\nTWPerson类实例方法:%p\nTWPerson元类类实例方法:%p\n---类方法saybye---\nTWPerson类实例方法:%p\nTWPerson元类类实例方法:%p\n",__func__,method1,method2,method3,method4);}
void TWClassMethod_classToMetaclass(Class pClass){ const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); Method method1 = class_getClassMethod(pClass, @selector(sayhi)); Method method2 = class_getClassMethod(metaClass, @selector(sayhi)); Method method3 = class_getClassMethod(pClass, @selector(saybye)); Method method4 = class_getClassMethod(metaClass, @selector(saybye)); NSLog(@"%s\n---对象方法sayhi---\nTWPerson类方法:%p\nTWPerson元类类方法:%p\n---类方法saybye---\nTWPerson类方法:%p\nTWPerson元类类方法:%p\n",__func__,method1,method2,method3,method4);}
为什么类中的类方法在元类中还是有类方法,不应该时以对象方法存储么。
/************************************************************************ class_getClassMethod. Return the class method for the specified* class and selector.**********************************************************************/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(); }
万物皆对象,类也是对象。拿的都是对象实例方法。在底层没有类方法,只有对象方法。
void TWIMP_classToMetaclass(Class pClass){ //有了IMP就有函数实现 const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); IMP imp1 = class_getMethodImplementation(pClass, @selector(sayhi)); IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayhi));// 应该是nil // sel -> imp 方法的查找流程 imp_Forward IMP imp3 = class_getMethodImplementation(pClass, @selector(saybye)); // 应该是nil IMP imp4 = class_getMethodImplementation(metaClass, @selector(saybye)); NSLog(@"%s\n---对象方法sayhi---\nTWPerson的IMP:%p\nTWPerson元类的IMP:%p\n---类方法saybye---\nTWPerson类的IMP:%p\nTWPerson元类的IMP:%p\n",__func__,imp1,imp2,imp3,imp4);}
为什么对象方法在元类中也找到了实现
__attribute__((flatten))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) { //imp不存在 返回objc_msgForward return _objc_msgForward; } return imp;}
当imp=nil时,会返回_objc_msgForward,所以imp2和ipm3才有相同的函数指针地址。
判断八个结果的真假。
void TWisKindofDemo(void){ BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; BOOL re3 = [(id)[TWPerson class] isKindOfClass:[TWPerson class]]; BOOL re4 = [(id)[TWPerson class] isMemberOfClass:[TWPerson class]]; NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4); BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; BOOL re7 = [(id)[TWPerson alloc] isKindOfClass:[TWPerson class]]; BOOL re8 = [(id)[TWPerson alloc] isMemberOfClass:[TWPerson class]]; NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);}
本质是理解isa走位与继承链图。
首先先看源码,先查看类方法的isKindOfClass和isMemberOfClass
//tcls=根元类 再一直找父类循环 一直到tcls为null+ (BOOL)isKindOfClass:(Class)cls { for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) { if (tcls == cls) return YES; } return NO;}+ (BOOL)isMemberOfClass:(Class)cls { return self->ISA() == cls;}
类方法的isKindOfClass是一个for循环,从当前类的isa开始找,顺着继承链,判断当前指向的类是不是调用类方法的类。
类方法的isMemberOfClass判断当前类的isa是不是调用类方法的类。
根据源码与继承链分析
NSObject的元类的父类还是NSObject,NSObject的isa指向NSObject指向元类。
TWPerson的isa和它的继承链都不会指向TWPerson。
故1-4应该是1-0-0-0
- (BOOL)isKindOfClass:(Class)cls { for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) { if (tcls == cls) return YES; } return NO;}- (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls;}
对象方法的isKindOfClass也是一个for循环,从当前对象的类开始,顺着继承链,判断当前指向的类是不是调用对象的类。
对方方法的isMemberOfClass判断当前对象的类是不是调用对象的类。
故5-8应该是1-1-1-1。
断点调试发现不会进入iskindOfClass里面。
但凡事不能只看源码,任何东西拿过来都要跑一跑,汇编看一看。
不能只看源码 ,因为LLVM会重定向,就像之前分析alloc做了什么一样,其实我第一篇文章没找到类加载,是因为一开始并不是就简单的先执行了alloc,这里的问题我以后在补,先看当前问题。遇到这里 我们打开编译器的汇编功能。
发现它走的是objc_opt_isKindOfClass
而 isMemberOfClass走的还是isMemberOfClass。那我们就再看看objc_opt_isKindOfClass
// Calls [obj isKindOfClass]BOOLobjc_opt_isKindOfClass(id obj, Class otherClass){#if __OBJC2__ if (slowpath(!obj)) return NO;//如果obj是对象,就获取类,如果obj是类,就获取元类 Class cls = obj->getIsa(); if (fastpath(!cls->hasCustomCore())) { for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) { if (tcls == otherClass) return YES; } return NO; }#endif//不是_OBJC2_就直接走之前看的源码 return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);}
如果obj是对象,就获取对象的类,如果obj是类,就获取类的元类。
然后还是一个for循环走继承链判断。
如有问题,欢迎大家留言,与我交流。
转载地址:http://bbqzi.baihongyu.com/