所用版本:
熟悉类加载前, 先看下类的初始化方法_objc_init( 留意看下下面的注释 ):
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
// 环境变量初始化
environ_init();
// 线程处理
tls_init();
// 运行C++静态构造函数。
static_init();
// runtime运行时初始化
runtime_init();
// objc异常处理系统初始化
exception_init();
#if __OBJC2__
// 缓存初始化
cache_t::init();
#endif
// 启动回调机制
_imp_implementationWithBlock_init();
// dyld通知注册
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}


再次运行可发现, 新增打印信息

可看到打印了很多相关环境变量, OBJC_PRINT_IMAGES, OBJC_PRINT_CLASS_SETUP, OBJC_DISABLE_NONPOINTER_ISA等等。详细见: Xcode环境变量说明
针对本地线程处理做处理, 如果满足SUPPORT_DIRECT_THREAD_KEYS析构, 不满足初始化

其中
// Thread keys 由libc保留供我们使用。
# define SUPPORT_DIRECT_THREAD_KEYS 1 - 满足 0 - 不满足
pthread_key_init_np
tls_create
如果有C++静态构造函数, libc会在dyld 调用_dyld_objc_notify_register之前, 先调用static_init执行。

举个例子, 我们写一个全局构造函数, 运行可发现


如图可看出, 在_dyld_objc_notify_register之前如果有静态C++构造函数, 那么通过static_init方法直接运行。

可看出主要分对两部分, 分类初始化、类的表初始化进行
初始化libobjc的异常处理系统。其实是注册异常处理的回调,从而监控异常的处理

举个例子:

数组越界例子肯定会发生crash, 接着我们运行一下
先走了_objc_init中的exception_init

执行old_terminate = std::set_terminate(&_objc_terminate);, 留意下此时还没有执行_dyld_objc_notify_register。

执行_dyld_objc_notify_register → main

执行_objc_terminate


其实当 crash发生时,会走_objc_terminate方法,接着走到uncaught_handler, 扔出异常并传入一个参数(e), 而e的回调往下看

e = fn

可看出将objc_uncaught_exception_handler fn(设置的异常) 赋值给uncaught_handler, 即 uncaught_handler 等于 fn, 由此可看出uncaught_handler, 本质是一个回调函数。

如图,系统其实会针对crash进行拦截处理,app会抛出一个异常句柄NSSetUncaughtExceptionHandler,传入一个函数给系统,当异常发生后,调用函数(函数中可以线程保活、收集并上传崩溃日志),然后回到原有的app层中,其本质是一个回调函数。


首先可以看到_dyld_objc_notify_register(&map_images, load_images, unmap_image);
3个参数&map_images、load_images、unmap_image
&map_images: 映射整个镜像文件, 管理文件中, 动态库所有符号 (class, Protocol, selector, category)先留意下&, 说明是指针传递, 传递是一个函数。这里用指针传递的好处是为了让map_images同步发生变化, 主要原因这个函数很重要, 苹果不希望它会因为一些重复加载发生错乱。同时这个映射操作也比较耗时, 如果不是一起的话, 也会增加耗时性。看下其内部

接下来看下map_images_nolock内部

代码有点长, 直接看重点代码: 读取镜像_read_images

read_images这个方法很重要, 先说下_read_images都做了什么
@selector混乱问题
接下来看下_read_images底层实现, 并依次看下上面内容


略过一些代码看重点NXCreateMapTable

可看出第一次加载会创建一个表(key-value 哈希表): gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
创建一张类的总表,这个表包含所有的类。4/3 因子这个我稍微讲一下 , 先了解哈希表负载因子一个概念
负载因子 = 总键值对数/数组的个数。
负载因子是哈希表的一个重要属性,用来衡量哈希表的空/满程度,一定程度也可以提现查询的效率。负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。所以当负载因子大于某个常数(一般是0.75 即 3 / 4)时,哈希表将自动扩容。
哈希表扩容时,一般会创建两倍于原来的数组长度,因此即使 key的哈希值没有变化,对数组个数取余的结果会随着数组个数的扩容发生变化,因此键值对的位置都有可能发生变化,这个过程也成为重哈希(rehash)。
那么回来再看下, 表的大小也遵循负载因子,这里 namedClassesSize = totalClasses * 4 / 3相当于是负载因子``3/4的逆过程。namedClassesSize相当于总容量,totalClasses相当于要占用的空间。
例如我们想创建一张表 , 总容积: totalClass = x * 4 / 3
开辟表大小 x = totalClass * (3 / 4) = x * (4 / 3) * (3 / 4) = x = namedClassesSize
gdb_objc_realized_classes:
其实gdb_objc_realized_classes是一张总表含所以类的表, 而runtime_init中的allocatedClasses
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}

allocatedClasses只是一个alloc的分表. gdb_objc_realized_classes包含它。

sel 会有 名字 + 地址, 有些时候名字可能相同但是地址不相同, 这个时候需要修复一下

其中_getObjc2SelectorRefs是获取Mach-O中的静态段__objc_selrefs
GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
再看下sel_registerNameNoLock方法


调成一致, 将SEL覆盖到中namedSelectors集合Set对应位置上, 这里用Set原因: 虽然都是集合但是相比array, set处理hash方面效率确实是更高一些
举个例子: 比如你要存储元素A, 一个hash算法直接就能直接找到A应该存储的位置; 同样, 当你要访问A时, 一个hash过程就能找到A存储的位置. 而对于array,若想知道A到底在不在数组中, 则需要便利整个数组, 显然效率较低了;
综上: UnfixedSelectors修复sel就是把相不同的@selector统一化, 同时要以dyld的sel为准.

主要是从Mach-O中取出所有类,在遍历进行读取, 核心方法readClass
我们看下它的底层

先加一个打印, 看看都能读到什么类
printf("%s - Test - %s \n", __func__, mangledName);


可看出能把系统类和自定义类都读取到, 没有用到的自定义类也会读取, 自定义类后添加的先读取。接下来我们跟一下自定义类的流程
const char *SRTest = "SRTest";
// 是否匹配
if (strcmp(mangledName, SRTest) == 0) {
printf("%s - 当前类 - %s \n", __func__, mangledName);
}

运行发现SRTest已进入

先走修正方法

如果类要求稳定, 那么会修正下不稳定的类

接下来跟流程可发现会走addNamedClass

稍微看下addNamedClass内部实现


addNamedClass将当前类添加到之前创建好的gdb_objc_realized_classes总表中
(之前有写, 往上翻第一次加载会创建一个表(key-value 哈希表): gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);)
继续跟流程可发现走addClassTableEntry


将类和元类插入allocatedClasses表中。这张表是在runtime_init中创建的。(之前也有写, 往上翻runtime_init )
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}
之后就会走readClass中的return cls;方法返回
综上,可看出readClass的主要将Mach-O中的类, 添加进内存 (插入到表中, 总表, alloc的分表都插一份)

鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下
我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty
我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来
我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内