47

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

 5 years ago
source link: http://www.cocoachina.com/ios/20181129/25641.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK