首页 >iOS开发

【API使用系列,整理】NSObject专题

2018-11-29 11:23 编辑: Gboy 分类:iOS开发 来源:junbaozi

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)self0, malloc_default_zone());  
}
+ alloc
{
    return (*_zoneAlloc)((Class)self0, 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, 01); 
    } 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, 01);
    } 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)self0);
    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 = selfclassclass = class_getSuperclass(class)) 

    { 
         if(class_conformsToProtocol(class, aProtocolObj)) return YES

    } 

     return NO


最终用的是class_conformsToProtocol,返回一个布尔值,表示一个类是否符合给定的协议。

class_conformsToProtocol的实现如下:

BOOL class_conformsToProtocol(Class cls_genProtocol*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 != NULLlist = 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 <= 4break
        } 

    } 

    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)(self0, 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 


搜索CocoaChina微信公众号:CocoaChina
微信扫一扫
订阅每日移动开发及APP推广热点资讯
公众号:
CocoaChina
我要投稿   收藏文章
上一篇:fastlane使用说明书
我来说两句
发表评论
您还没有登录!请登录注册
所有评论(0

综合评论

相关帖子

sina weixin mail 回到顶部