1 NSObject源码实现分析
Objective-C NSObject的实现分析(2014-10-23更新)
http://blog.csdn.net/uxyheaven/article/details/38120335
1.1 属性
1.1.1 isa
是一个指向Class的指针,具体请看这篇文章Objective-C objc_class介绍
1.2 方法
1.2.1 class
实例方法返回的是isa指针, 类方法返回的是本身
代码实现如下:
- class
{
return (id)isa;
}
+ class
{
return self;
}
1.2.2 superclass
返回父类
代码实现如下:
+ superclass
{
return class_getSuperclass((Class)self);
}
- superclass
{
return class_getSuperclass(isa);
}
+ superclass
{
return class_getSuperclass((Class)self);
}
- superclass
{
return class_getSuperclass(isa);
}
调用的是runtime中的class_getSuperclass方法,跟踪到最后实例方法返回的是isa->superclass,类方法返回的是self->superclass
static class_t * getSuperclass(class_t *cls)
{
if (!cls) return NULL;
return cls->superclass;
}
1.2.3 isEqual
就是直接比较
- (BOOL)isEqual:anObject
{
return anObject ==self;
}
- (BOOL)isEqual:anObject
{
return anObject == self;
}
1.2.4 isMemberOf:
- (BOOL)isMemberOf:aClass
{
return isa == (Class)aClass;
}
看代码可以得知是通过比较实例对象的isa是否和 传过来的[类 Class] 一样来判断的.而实例对象的isa确实就是指着实例对象的类的.
- (BOOL)isMemberOf:aClass
{
return isa ==(Class)aClass;
}
1.2.5 isKindOf:
- (BOOL)isKindOf:aClass
{
register Class cls;
for (cls = isa; cls; cls = class_getSuperclass(cls))
if (cls == (Class)aClass)
return YES;
return NO;
}
// class_getSuperclass展开后如下
static class_t *getSuperclass(class_t *cls)
{
if (!cls) return NULL;
return cls->superclass;
}
代码思路也很好理解,如果自己的isa等于aClass(aClass的父类,此处循环)就返回YES,否则返回NO
- (BOOL)isKindOf:aClass
{
register Classcls;
for (cls = isa;cls; cls = class_getSuperclass(cls))
if (cls ==(Class)aClass)
returnYES;
return NO;
}
// class_getSuperclass展开后如下
static class_t * getSuperclass(class_t *cls)
{
if (!cls) returnNULL;
return cls->superclass;
}
1.2.6 init
- init
{
return self;
}
没什么好说的
- init
{
return self;
}
1.2.7 alloc
+ alloc
{
return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());
}
+ alloc
{
return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());
}
这里有一个函数指针和一个结构体,我们跟进去看
id (*_zoneAlloc)(Class, size_t, voidvoid *) =_class_createInstanceFromZone;
PRIVATE_EXTERN id
_class_createInstanceFromZone(Class cls, size_t extraBytes,voidvoid *zone)
{
id obj;
size_t size;
// Can't createsomething for nothing
if (!cls) returnnil;
// Allocate andinitialize
size =_class_getInstanceSize(cls) + extraBytes;
// CF requires allobjects be at least 16 bytes.
if (size < 16)size = 16;
#if SUPPORT_GC
if (UseGC) {
obj =(id)auto_zone_allocate_object(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1);
} else
#endif
if (zone) {
obj = (id)malloc_zone_calloc (zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
obj->isa =cls;
if(_class_hasCxxStructors(cls)) {
obj =_objc_constructOrFree(cls, obj);
}
return obj;
}
id (*_zoneAlloc)(Class, size_t, void *) = _class_createInstanceFromZone;
PRIVATE_EXTERN id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
id obj;
size_t size;
// Can't create something for nothing
if (!cls) return nil;
// Allocate and initialize
size = _class_getInstanceSize(cls) + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
#if SUPPORT_GC
if (UseGC) {
obj = (id)auto_zone_allocate_object(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1);
} else
#endif
if (zone) {
obj = (id)malloc_zone_calloc (zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
obj->isa = cls;
if (_class_hasCxxStructors(cls)) {
obj = _objc_constructOrFree(cls, obj);
}
return obj;
}
上面那段代码的作用是:
1、得到这个类占用多少空间,最小占16 bytes;
2、然后就给这个实例分配多少空间, 如果失败的话就返回nil;
3、把这个实例的isa设置成这个类对象;
4、如果cls的info设置了get属性就用cls这个类在obj这个空间去构造一个实例,跟进去是
static BOOL object_cxxConstructFromClass(id obj, Class cls)
{
id (*ctor)(id);
Class supercls;
// Stop if neither this class nor any superclass has ctors.
if (!_class_hasCxxStructors(cls)) return YES; // no ctor - ok
supercls = _class_getSuperclass(cls);
// Call superclasses' ctors first, if any.
if (supercls) {
BOOL ok = object_cxxConstructFromClass(obj, supercls);
if (!ok) return NO; // some superclass's ctor failed - give up
}
// Find this class's ctor, if any.
ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
if (ctor == (id(*)(id))&_objc_msgForward_internal) return YES; // no ctor - ok
// Call this class's ctor.
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ constructors for class %s", _class_getName(cls));
}
if ((*ctor)(obj)) return YES; // ctor called and succeeded - ok
// This class's ctor was called and failed.
// Call superclasses's dtors to clean up.
if (supercls) object_cxxDestructFromClass(obj, supercls);
return NO;
}
大意是,先看自己有没有父类,有就递归调用自己,然后给自己添加方法,然后添加类别
1.2.8 new
+ new
{
id newObject =(*_alloc)((Class)self, 0);
Class metaClass =self->isa;
if(class_getVersion(metaClass) > 1)
return[newObject init];
else
returnnewObject;
}
跟进去看一下,发现是和alloc差不多
id (*_alloc)(Class, size_t) = _class_createInstance;
static id _class_createInstance(Class cls, size_textraBytes)
{
return_class_createInstanceFromZone (cls, extraBytes, NULL);
}
1.2.9 free
- free
{
return(*_dealloc)(self);
}
+ free
{
return nil;
}
跟进去看一下
static id _object_dispose(idanObject)
{
if (anObject==nil)return nil;
objc_destructInstance(anObject);
#if SUPPORT_GC
if (UseGC) {
auto_zone_retain(gc_zone, anObject); // gc free expects rc==1
} else
#endif
{
// onlyclobber isa for non-gc
anObject->isa = _objc_getFreedObjectClass ();
}
free(anObject);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa =_object_getClass(obj);
if(_class_hasCxxStructors(isa)) {
object_cxxDestruct(obj);
}
if(_class_instancesHaveAssociatedObjects(isa)) {
_object_remove_assocations(obj);
}
if (!UseGC) objc_clear_deallocating(obj);
}
return obj;
}
1、执行一个叫object_cxxDestruct的东西干了点什么事(沿着继承链逐层向上搜寻SEL_cxx_destruct这个selector,找到函数实现(void (*)(id)(函数指针)并执行);
2、 执行_object_remove_assocations去除和这个对象关联的对象;
3、执行objc_clear_deallocating,清空引用计数表并清除弱引用表,将所有weak引用指nil
1.2.10 respondsTo:
是查找有没有实现某个方法
- (BOOL)respondsTo:(SEL)aSelector
{
return class_respondsToMethod(isa, aSelector);
}
BOOL class_respondsToMethod(Class cls, SEL sel)
{
OBJC_WARN_DEPRECATED;
return class_respondsToSelector(cls, sel);
}
BOOL class_respondsToSelector(Class cls, SEL sel)
{
IMP imp;
if (!sel || !cls) return NO;
// Avoids+initialize because it historically did so.
// We're notreturning a callable IMP anyway.
imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/);
return (imp != (IMP)_objc_msgForward_internal)? YES : NO;
}
1.2.11 perform:
perform是发送消息到指定的接收器并返回值,下面是代码:
- perform:(SEL)aSelector
{
if(aSelector)
returnobjc_msgSend(self, aSelector);
else
return [selferror:_errBadSel, sel_getName(_cmd), aSelector];
}
原来就是objc_msgSend这玩意.objc_msgSend实现有很多个版本,大体逻辑应该差不多,首先在找缓存,找到就跳转过去,找不到就在Class的方法列表里找方法,如果还是没找到就转发.
下的是arm下的代码
ENTRY objc_msgSend
# check whether receiver is nil
teq a1, #0
itt eq
moveq a2, #0
bxeq lr
# save registers and load receiver's class forCacheLookup
stmfd sp!, {a4,v1}
ldr v1, [a1, #ISA]
# receiver is non-nil: search the cache
CacheLookup a2,v1, LMsgSendCacheMiss
# cache hit (imp in ip) and CacheLookup returns withnonstret (eq) set, restore registers and call
ldmfd sp!, {a4,v1}
bx ip
# cache miss: go search the method lists
LMsgSendCacheMiss:
ldmfd sp!, {a4,v1}
b _objc_msgSend_uncached
LMsgSendExit:
END_ENTRY objc_msgSend
STATIC_ENTRY objc_msgSend_uncached
# Push stack frame
stmfd sp!, {a1-a4,r7,lr}
add r7, sp, #16
# Load class and selector
ldr a1, [a1,#ISA] /* class = receiver->isa */
# MOVE a2, a2 /* selector already in a2 */
# Do the lookup
MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache)
MOVE ip, a1
# Prep for forwarding, Pop stack frame and call imp
teq v1, v1 /*set nonstret (eq) */
ldmfd sp!, {a1-a4,r7,lr}
bx ip
1.2.12 conformsTo:
返回是否遵循了某个协议
- (BOOL) conformsTo: (Protocol *)aProtocolObj
{
return [(id)isa conformsTo: aProtocolObj];
}
+ (BOOL) conformsTo: (Protocol *)aProtocolObj
{
Class class;
for (class = self; class; class = class_getSuperclass(class))
{
if(class_conformsToProtocol(class, aProtocolObj)) return YES;
}
return NO;
}
最终用的是class_conformsToProtocol,返回一个布尔值,表示一个类是否符合给定的协议。
class_conformsToProtocol的实现如下:
BOOL class_conformsToProtocol(Class cls_gen, Protocol*proto_gen)
{
struct old_class *cls = oldcls(cls_gen);
struct old_protocol *proto = oldprotocol(proto_gen);
if (!cls_gen) return NO;
if (!proto) returnNO;
if(cls->isa->version >= 3) {
structold_protocol_list *list;
for (list =cls->protocols; list != NULL; list = list->next) {
int i;
for (i =0; i < list->count; i++) {
if(list->list[i] == proto) return YES;
if(protocol_conformsToProtocol((Protocol *)list->list[i], proto_gen))
return YES;
}
if(cls->isa->version <= 4) break;
}
}
return NO;
}
可以看到是在cls->protocols里面找.protocols是协议的数组
1.2.13 copy
浅拷贝
- copy
{
return [self copyFromZone: [self zone]];
}
//返回指定区域的指针
- (voidvoid *)zone
{
void *z = malloc_zone_from_ptr(self);
return z ? z :malloc_default_zone();
}
- copyFromZone:(voidvoid *)z
{
return (*_zoneCopy)(self, 0, z);
}
id (*_zoneCopy)(id, size_t, void *) =_object_copyFromZone;
static id _object_copyFromZone(id oldObj, size_t extraBytes,voidvoid *zone)
{
id obj;
size_t size;
if (!oldObj) return nil;
//用旧对象的isa生成一个新的对象的空间
obj = (*_zoneAlloc)(oldObj->isa, extraBytes, zone);
size =_class_getInstanceSize(oldObj->isa) + extraBytes;
// fixme need C++copy constructor
//把旧对象的内存拷贝到新对象
objc_memmove_collectable(obj, oldObj, size);
}
2 概念原理
2.1 野指针与僵尸对象
2.1.1 野指针
C语言:
当我们声明1个指针变量,没有为这个指针变量赋初始值.这个指针变量的值是1个垃圾指指针,指向1块随机的内存空间。
OC语言:
指针指向的对象已经被回收掉了。这个指针就叫做野指针。
2.1.2 僵尸对象
僵尸对象:
1个已经被释放的对象 就叫做僵尸对象.
2.2 nil/Nil/NULL/NSNull的区别
nil:指向oc中对象的空指针
Nil:指向oc中类的空指针
NULL:指向其他类型的空指针,如一个c类型的内存指针
NSNull:在集合对象中,表示空值的对象
若obj为nil:
[obj message]将返回NO,而不是NSException
若obj为NSNull:
[obj message]将抛出异常NSException
nil和NULL从字面意思来理解比较简单,nil是一个对象,而NULL是一个值,我的理解为nil是将对象设置为空,而NULL是将基本类型设置为空的。而且我们对于nil调用方法,不会产生crash或者抛出异常。
看一下用法
NSURL *url = nil;
Class class = Nil;
int *pointerInt = NULL;
nil是一个对象指针为空,Nil是一个类指针为空,NULL是基本数据类型为空。
3 参考链接
IOS中类和对象还有,nil/Nil/NULL的区别
http://blog.sina.com.cn/s/blog_5fb39f910101akm1.html
cancelPreviousPerformRequestsWithTarget not cancelling anoutstanding performSelector:withDelay
iOS设置 延迟执行 与 取消延迟执行 方法 以及对runloop初步认识
http://www.cnblogs.com/someonelikeyou/p/5509878.html
IOS关于取消延迟执行函数的种种。performSelector与cancelPreviousPerformRequestsWithTarget
http://blog.csdn.net/samuelltk/article/details/8994313
IOS -延迟执行performSelector和取消延迟执行cancelPreviousPerformRequestsWithTarget