【API使用系列,整理】NSObject专题
栏目: Objective-C · 发布时间: 5年前
内容简介:Objective-C NSObject的实现分析(2014-10-23更新)http://blog.csdn.net/uxyheaven/article/details/38120335是一个指向Class的指针,具体请看这篇文章Objective-C objc_class介绍
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
http://stackoverflow.com/questions/8697648/cancelpreviousperformrequestswithtarget-not-cancelling-an-outstanding-performsel
iOS设置 延迟执行 与 取消延迟执行 方法 以及对runloop初步认识
http://www.cnblogs.com/someonelikeyou/p/5509878.html
IOS关于取消延迟执行函数的种种。performSelector与cancelPreviousPerformRequestsWithTarget
http://blog.csdn.net/samuelltk/article/details/8994313
IOS -延迟执行performSelector和取消延迟执行cancelPreviousPerformRequestsWithTarget
http://www.cnblogs.com/HermitCarb/p/4740773.html
---------------------
作者:junbaozi
原文:https://blog.csdn.net/junbaozi/article/details/79452135
以上所述就是小编给大家介绍的《【API使用系列,整理】NSObject专题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
We Are the Nerds
Christine Lagorio-Chafkin / Hachette Books / 2018-10-2 / USD 18.30
Reddit hails itself as "the front page of the Internet." It's the third most-visited website in the United States--and yet, millions of Americans have no idea what it is. We Are the Nerds is an eng......一起来看看 《We Are the Nerds》 这本书的介绍吧!