Objective-C NSObject 的实现分析(2014-10-23更新)
NSObject 的实现分析
转载请注名出处 http://blog.csdn.net/uxyheaven
iOS 的 NSObject 类没有开源, 可是呢 runtime开源了,里面有个类 Object 看接口和NSObject差点儿相同,以下我就对着 Object 的代码来分析下 NSObject
runtime代码在http://opensource.apple.com/tarballs/objc4/objc4-493.9.tar.gz下载,Object在<Object.h>, 这里的目录写着Obsolete, 呃.
属性
isa
是一个指向Class的指针,详细请看这篇文章Objective-C objc_class 介绍
方法
class
实例方法返回的是isa指针, 类方法返回的是本身
代码实现例如以下:
- class
{
return (id)isa;
} + class
{
return self;
}
superclass
返回父类
代码实现例如以下:
+ 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;
}
isEqual
就是直接比較
- (BOOL)isEqual:anObject
{
return anObject == self;
}
isMemberOf:
- (BOOL)isMemberOf:aClass
{
return isa == (Class)aClass;
}
看代码能够得知是通过比較实例对象的isa是否和 传过来的[类 Class] 一样来推断的.而实例对象的isa确实就是指着实例对象的类的.
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
init
- init
{
return self;
}
没什么好说的
alloc
+ alloc
{
return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());
}
这里有一个函数指针和一个结构体,我们跟进去看
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;
}
上面那段代码的作用是
- 得到这个类占用多少空间,最小占16 bytes
- 然后就给这个实例分配多少空间, 假设失败的话就返回nil
- 把这个实例的isa设置成这个类对象
- 假设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;
}
大意是,先看自己有没有父类,有就递归调用自己,然后给自己加入方法,然后加入类别
new
+ new
{
id newObject = (*_alloc)((Class)self, 0);
Class metaClass = self->isa;
if (class_getVersion(metaClass) > 1)
return [newObject init];
else
return newObject;
}
跟进去看一下, 发现是和 alloc差点儿相同
id (*_alloc)(Class, size_t) = _class_createInstance; static id _class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone (cls, extraBytes, NULL);
}
free
- free
{
return (*_dealloc)(self);
} + free
{
return nil;
}
跟进去看一下
static id
_object_dispose(id anObject)
{
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
{
// only clobber 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;
}
- 运行一个叫object_cxxDestruct的东西干了点什么事(沿着继承链逐层向上搜寻SEL_cxx_destruct这个selector, 找到函数实现(void (*)(id)(函数指针)并运行)
- 运行_object_remove_assocations去除和这个对象关联的对象
- 运行objc_clear_deallocating。清空引用计数表并清除弱引用表。将全部weak引用指nil
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 not returning a callable IMP anyway.
imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/);
return (imp != (IMP)_objc_msgForward_internal) ? YES : NO;
}
perform:
perform是发送消息到指定的接收器并返回值, 以下是代码:
- perform:(SEL)aSelector
{
if (aSelector)
return objc_msgSend(self, aSelector);
else
return [self error:_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 for CacheLookup
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 with nonstret (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
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) return NO; if (cls->isa->version >= 3) {
struct old_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 是协议的数组
copy
浅拷贝
- copy
{
return [self copyFromZone: [self zone]];
} // 返回指定区域的指针
- (void *)zone
{
void *z = malloc_zone_from_ptr(self);
return z ? z : malloc_default_zone();
} - copyFromZone:(void *)z
{
return (*_zoneCopy)(self, 0, z);
} id (*_zoneCopy)(id, size_t, void *) = _object_copyFromZone; static id _object_copyFromZone(id oldObj, size_t extraBytes, void *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); }
Objective-C NSObject 的实现分析(2014-10-23更新)的更多相关文章
- App Store审核指南中文版(2014.10.11更新)
App Store审核指南中文版(2014.10.11更新) 2014-10-11 16:36 编辑: suiling 分类:AppStore研究 来源:CocoaChina 2 8657 App ...
- boost.asio包装类st_asio_wrapper开发教程(2014.5.23更新)(一)-----转
一:什么是st_asio_wrapper它是一个c/s网络编程框架,基于对boost.asio的包装(最低在boost-1.49.0上调试过),目的是快速的构建一个c/s系统: 二:st_asio_w ...
- Contest - 2014 SWJTU ACM 手速测试赛(2014.10.31)
题目列表: 2146 Problem A [手速]阔绰的Dim 2147 Problem B [手速]颓废的Dim 2148 Problem C [手速]我的滑板鞋 2149 Problem D [手 ...
- Linux - Eclipse CDT + GCC 安装(2014.10.2)
Eclipse CDT + GCC 安装 (2014.10.2) 本文地址:http://blog.csdn.net/caroline_wendy 1. 安装Eclipse,在官方站点下载Eclips ...
- phpStudy + JspStudy 2014.10.02 下载
phpStudy + JspStudy 2014.10.02 下载 目标:让天下没有难配的php环境. phpStudy Linux版&Win版同步上线 支持Apache/Nginx/Teng ...
- 联想ERP项目实施案例分析(10):回到最初再反思IT价值
联想ERP项目实施案例分析(10):回到最初再反思IT价值 投入上千万(未来每年的维护费也非常高),投入一年实施时间,高级副总裁亲自挂帅,各级业务部门管理者亲自负责.骨干业务人员充当区域IT实施者/推 ...
- [原创] 【2014.12.02更新网盘链接】基于EasySysprep4.1的 Windows 7 x86/x64 『视频』封装
[原创] [2014.12.02更新网盘链接]基于EasySysprep4.1的 Windows 7 x86/x64 『视频』封装 joinlidong 发表于 2014-11-29 14:25:50 ...
- OpenStack 2014.1(Icehouse) 更新说明
OpenStack 2014.1(Icehouse) 更新说明 1.综合升级说明 Windows安装包应使用PBR 0.8版本,以避免发生bug1294246 log-config选项 ...
- 背水一战 Windows 10 (23) - MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过 ButtonBase 触发命令
[源码下载] 背水一战 Windows 10 (23) - MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过 ButtonBase 触发命令 作者:webabcd ...
随机推荐
- boost::asio设置同步连接超时
boost::asio设置同步连接超时 CSDN上求助无果,只好用自创的非主流方法了.asio自带的例子里是用deadline_timer的async_wait方法来实现超时的,这种方法需要单独写 ...
- 3D空间中射线与三角形的交叉检測算法
引言 射线Ray,在3D图形学中有非常多重要的应用.比方,pick操作就是使用射线Ray来实现的,还有诸如子弹射线的碰撞检測等等都能够使用射线Ray来完毕. 所以,在本次博客中,将会简单的像大家介绍下 ...
- MongoDB学习笔记(三) 在MVC模式下通过Jqgrid表格操作MongoDB数据
看到下图,是通过Jqgrid实现表格数据的基本增删查改的操作.表格数据增删改是一般企业应用系统开发的常见功能,不过不同的是这个表格数据来源是非关系型的数据库MongoDB.nosql虽然概念新颖,但是 ...
- 谈论multistage text input(中国输入法)下一个UITextView内容长度的限制
我以前写<如何更好地限制UITextField输入长度>.接使用 UIKIT_EXTERN NSString *const UITextFieldTextDidChangeNotifica ...
- 在StatusBar中显示当前时间
在StatusBar中显示当前时间,如下: 1.在String Table中插入一项 (注意:状态栏将根据字符串的长度来确定相应窗格的缺省宽度,所以指定为00:00:00就为时间的显示预留了空间) ...
- ibatis新手入门
ibatis 是什么 iBATIS是以SQL为中心的持久化层框架. 能支持懒载入.关联查询.继承等特性. iBATIS不同于一般的OR映射框架. OR映射框架,将数据库表.字段等映射到类.属性,那是一 ...
- ios html5 设定PhoneGap开发环境
怎么样IOS平台搭建PhoneGap开发环境(PhoneGap2.5) (2013-03-13 14:44:51) 标签: c=blog&q=it&by=tag" targe ...
- 地大邀请赛d
Problem D: Tetrahedron Inequality Time Limit: 1 Sec Memory Limit: 128 MB Submit: 15 Solved: 3 [ ...
- 【MFC两种视频图像採集方法】DirectShow与Opencv
效果图: DirectShow採集核心代码: 创建线程调用该函数,採集图像通过x264解码封装rtmp协议包.推送至FMSserver,可实现视频直播 UINT __stdcall StartVide ...
- 【总结】在VirtualBox上面安装Mac的注意事项
看此文之前 http://www.crifan.com/category/work_and_job/virtual_machine/virtualbox-virtual_machine/ 此文仅仅是针 ...