0%

我们都知道 weak 修饰的变量,在对象释放后,会自动置为 nil,这一机制减少了大量的野指针崩溃;我们还知道在 dealloc 里不要 weak 修饰 self 对象,否则当对象 dealloc 时就会崩溃掉;一起看下源码实现吧!

下载源码

虽然 iOS 不是开源的,但是 OBJC 这部分代码是 Open的,下载地址 : objc4-709.tar.gz 。这是第二次阅读 objc 源码,第一次是分析关联引用实现原理时阅读的 : 深入理解关联引用;我猜测 weak 的实现和关联对象应该是大同小异的,思路上应该是相同的,应该也是在创建后存表,dealloc 时置空,然后从表里移除掉。

注册 weak 变量

初始化一个 weak 指针时会走 objc_initWeak 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** 
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr.
* @param newObj Object ptr.
*/

id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}

return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}

内部会走 objc_storeWeak 方法来存储你声明的 weak 变量 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** 
* This function stores a new value into a __weak variable. It would
* be used anywhere a __weak variable is the target of an assignment.
*
* @param location The address of the weak pointer itself
* @param newObj The new object this weak ptr should now point to
*
* @return newObj
*/

id objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);
}

内部则调用了 storeWeak 这个函数,其实这个注释已经说的很明白了,如果 CrashIfDeallocating 是 true 的话,当 newObj 正在 dealloc 或者 newObj 不支持 weak 就会崩溃!调用的时候传的是枚举值 DoCrashIfDeallocating,正是 true !所以上一篇博客里提到的在 dealloc 里使用 weak self 会崩溃,立即 传送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Update a weak variable.
// If HaveOld is true, the variable has an existing value
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is
// deallocating or newObj's class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>

static id storeWeak(id *location, objc_object *newObj)
{
assert(haveOld || haveNew);
if (!haveNew) assert(newObj == nil);

Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;

// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
retry:
if (haveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}

SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}

// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));

// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
previouslyInitializedClass = cls;

goto retry;
}
}

// Clean up old value, if any.
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}

// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected

// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}

// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}

SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

return (id)newObj;
}

内部调用 weak_register_no_lock 注册一个 weak指针和对象的键值对,然后返回;如果注册的时候发现该对象正在 dealloc 则会崩溃,并且通过 _objc_fatal 方法留了遗言!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/** 
* Registers a new (object, weak pointer) pair. Creates a new weak
* object entry if it does not exist.
*
* @param weak_table The global weak table.
* @param referent The object pointed to by the weak reference.
* @param referrer The weak pointer address.
*/
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;

if (!referent || referent->isTaggedPointer()) return referent_id;

// ensure that the referenced object is viable
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}

if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}

// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}

// Do not set *referrer. objc_storeWeak() requires that the
// value not change.

return referent_id;
}

这里只能猜测 runtime 是通过引用计数是否为 0 来判断一个对象是否是处于 deallocing 状态的,因为 SEL_allowsWeakReference 这个函数的定义是找不到的!但是我们从源码上可以确切的了解到,如果对象正在 deallocing 不能让他使用 weak 修饰!否者就崩溃掉了!!

1
2
3
4
5
6
7
8
9
10
if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}

objc 里封装了 _objc_fatal 这个函数用于停止程序,并且搞个遗言,内部则是调用了 _objc_fatalv

1
2
3
4
5
6
7
8
void _objc_fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap,fmt);
_objc_fatalv(OBJC_EXIT_REASON_UNSPECIFIED,
OS_REASON_FLAG_ONE_TIME_FAILURE,
fmt, ap);
}

_objc_fatalv 属于内部私有方法,可以正常 exit,也可以 abort,取决于 DebugDontCrash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
{
char *buf1;
vasprintf(&buf1, fmt, ap);

char *buf2;
asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
_objc_syslog(buf2);

if (DebugDontCrash) {
char *buf3;
asprintf(&buf3, "objc[%d]: HALTED\n", getpid());
_objc_syslog(buf3);
_Exit(1);
}
else {
abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
}
}

上面的流程大致是将 weak 描述的变量存起来的过程,我们再挖掘下对象释放后,weak 变量自动置空的逻辑:

dealloc 时清理掉 weak 变量

既然我们知道了结论,那么就直接从 dealloc 开始看方法调用吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (void)dealloc {
_objc_rootDealloc(self);
}

void _objc_rootDealloc(id obj)
{
assert(obj);

obj->rootDealloc();
}

inline void objc_object::rootDealloc()
{
if (isTaggedPointer()) return;
object_dispose((id)this);
}

id object_dispose(id obj)
{
return (*_dealloc)(obj);
}

///注意 _dealloc 其实就是 _object_dispose
id (*_dealloc)(id) = _object_dispose;

static id _object_dispose(id anObject)
{
if (anObject==nil) return nil;

objc_destructInstance(anObject);

anObject->initIsa(_objc_getFreedObjectClass ());

free(anObject);
return nil;
}

看注释可以知道,这个方法是干清理的工作的,不释放对象的内存;具体是清理关联应用的对象和存储的 weak 指针 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
* CoreFoundation and other clients do call this under GC.
**********************************************************************/

void *objc_destructInstance(id obj)
{
if (obj) {
Class isa = obj->getIsa();

if (isa->hasCxxDtor()) {
object_cxxDestruct(obj);
}
///之前看关联引用的时候,已经看过这个方法
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
///今天来看下清理 weak 指针的方法吧
objc_clear_deallocating(obj);
}

return obj;
}

void objc_clear_deallocating(id obj)
{
assert(obj);

if (obj->isTaggedPointer()) return;
obj->clearDeallocating();
}


inline void objc_object::clearDeallocating()
{
sidetable_clearDeallocating();
}

void objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];

// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}

/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;

weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}

// zero out references
weak_referrer_t *referrers;
size_t count;

if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}

for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}

weak_entry_remove(weak_table, entry);
}

找了半天,我们想看到的代码无非是在dealloc里执行置空和从table里移除:

1
2
3
4
if (*referrer == referent) {
*referrer = nil;
}
weak_entry_remove(weak_table, entry);

截止到今日,了解了对象 dealloc 时系统框架到底做了什么,清楚的知道了关联引用对象的生命周期和 weak 变量置空的原理,对于日后修改一些问题,实现某些特性的逻辑(网络库里的自动取消就是通过关联引用做的,利用了对象释放时,自动释放关联对象这一特性),就更加有把握了!

注:objc 的源码是一直在更新的,如果你看的不是 objc4-709.tar.gz 这个包的话,可能源码与我贴的有差异!

我们都知道 weak 修饰的变量,在对象释放后,会自动置为 nil,这一机制减少了大量的野指针崩溃;可是如果你在对象的 dealloc 里使用 weak 修饰 self 会如何呢 ?答案是崩溃 ~_~ 最近我们 SDK 在集成到某个 App 之后,就遇到了这样的问题,一起看下吧!

先看下解析后的崩溃日志吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x0000000182c1987c __terminate_with_payload + 8
1 libsystem_kernel.dylib 0x0000000182c144f0 abort_with_payload + 0
2 libsystem_kernel.dylib 0x0000000182c1446c abort_with_payload_wrapper_internal + 0
3 libobjc.A.dylib 0x000000018267fea4 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 112
4 libobjc.A.dylib 0x000000018267fdfc __objc_error + 0
5 libobjc.A.dylib 0x0000000182693edc weak_entry_insert(weak_table_t*, weak_entry_t*) + 0
6 libobjc.A.dylib 0x000000018269c3ac objc_storeWeak + 332
7 SOHUVideo 0x00000001004b5ba0 -[SharePopView setDelegate:] (SharePopView.h:31)
8 SOHUVideo 0x00000001003ce7c8 -[ShareViewController shareView] (ShareViewController.m:70)
9 SOHUVideo 0x00000001003ce2a0 -[ShareViewController dealloc] (ShareViewController.m:43)
10 UIKit 0x000000018a0a5960 -[UIPresentationController .cxx_destruct] + 312
11 libobjc.A.dylib 0x000000018267ef00 object_cxxDestructFromClass(objc_object*, objc_class*) + 148
12 libobjc.A.dylib 0x000000018268c334 objc_destructInstance + 92
13 libobjc.A.dylib 0x000000018268c398 object_dispose + 28
14 UIKit 0x000000018a0a1348 -[UIPresentationController dealloc] + 64

如果连着线直接 debug 的话,可能控制台只输出了这样一句话,并且代码定位到使用 weak self 那一行,此时可以使用 bt 打印堆栈信息,打出来跟上面是一样:

1
objc[78013]: Cannot form weak reference to instance (0x7fbc9287e840) of class ShareViewController. It is possible that this object was over-released, or is in the process of deallocation.

接下来我们一行一行的分析崩溃日志,然后确定问题,这是函数调用栈,因此先走的方法在下面,最上面的是最后走的方法,因此我们倒着来看,从第 9 行看就好了:

1
2
3
4
5
6
7
8
9
9 : ShareViewController 的 dealloc 走了
8 : 访问了 ShareViewController 对象的属性 shareView
7 : 给 shareView 设置了代理对象
6 : 看库名知道这是走到了 objc 底层了,看方法名知道这个操作应该是要存储一个 weak 修饰的对象
5 : 将 weak entry 插入到 table 里
4 : 遇到了错误
3 : 处理致命错误
2 : 代码继续往下走,objc 交给了 system 处理
1 : 系统终结了你的程序

底层的错误肯定是上层调用错误引起的,因此我们只看 App 层最后一个方法调用,那就是 [SharePopView setDelegate:],源码是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)dealloc {
self.shareView.delegate = nil;
self.shareView = nil;
}

- (SharePopView *)shareView {
if (!_shareView) {
CGFloat height = titleHeight + ShareIconViewHeightForSL * 2 + bottomTitleHeight + cancelButtonHeight;
_shareView = [[SharePopView alloc] initWithFrame:CGRectMake(0, DeviceHeight - height, DeviceWidth, height)];
_shareView.delegate = self;///连着线调试时,崩溃后定位到了这一行!!!
}
return _shareView;
}

@interface SharePopView : UIView

////这里使用的是 weak 修饰的
@property (nonatomic, weak) id<SharePopupViewDelegate> delegate;

@end

我看到这个代码的第一反应是,dealloc 不应该使用 self 打点访问懒加载的属性,我也经常重写 get 方法来懒加载,因此我对于这个很敏感,因为对象正在释放,没有创建相关属性的必要了,这里之所以崩溃,是因为创建对象之后,立马指定了 self 为代理,并且是 weak 的,从崩溃堆栈信息也能看出,设置了weak修饰的代理后,objc 底层对 self 进行了处理,在处理的时候,发现不对劲,然后就 abort 了!!

ps : 不管是使用 weak 修饰的属性,还是使用 __weak 修饰的变量,objc 都会对这个变量处理,也只有这样才能让修饰对象在释放之后,自动置空!

看到源码问题就好解决了,加个判断就行了,不要懒加载去创建,而是判断下,如果创建过就将代理置空,这样就避免了在 dealloc 里使用 weak 修饰 self 对象!

1
2
3
4
5
6
- (void)dealloc {
if(_shareView){
_shareView.delegate = nil;
_shareView = nil;
}
}

问题是解决了,可是还没搞明白底层 objc 的处理流程呢,有兴趣的话接着往下看吧:源码分析 weak 对象置空原理.

我的 MBP 重装回 macOS Sierra 之后,博客系统也就需要重新搭建了,因此趁这个机会,顺便整理下在 macOS 上安装 Jekyll 的详细过程。

检查 Ruby 版本

因为 Jekyll 是使用 Rake 编写的,所以最 Ruby 版本是有要求的,最新 3.5 则需要 Ruby 2.1.0 以上版本才行!

ruby -v

1
2
///我刚升级的2.2.6
ruby 2.2.6p396 (2016-11-15 revision 56800) [x86_64-darwin16]

如果你的版本是 2.1.0 之前的,可参看我的这篇博客《使用 RVM 更新 Ruby 版本》,先升级下 Ruby,再来往下看。

检查 Command Line Tools

可以说要想在 Mac 使用终端,搞开发相关的东西,不装 Command Line Tools 是不可能的,使用命令 xcode-select -p 检查:

1
2
3
/Applications/Xcode.app/Contents/Developer
或者
/Library/Developer/CommandLineTools

如果看到这两个其中一个就表示已经安装了,无需再装;如果不安装 Command Line Tools,就直接装 Jekyll 的话,会报如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
gem install jekyll bundler
Password:
Fetching: public_suffix-2.0.5.gem (100%)
Successfully installed public_suffix-2.0.5
Fetching: addressable-2.5.1.gem (100%)
Successfully installed addressable-2.5.1
Fetching: colorator-1.1.0.gem (100%)
Successfully installed colorator-1.1.0
Fetching: sass-3.4.24.gem (100%)
Successfully installed sass-3.4.24
Fetching: jekyll-sass-converter-1.5.0.gem (100%)
Successfully installed jekyll-sass-converter-1.5.0
Fetching: rb-fsevent-0.9.8.gem (100%)
Successfully installed rb-fsevent-0.9.8
Fetching: ffi-1.9.18.gem (100%)
Building native extensions. This could take a while...
ERROR: Error installing jekyll:
ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/include/ruby.h


Gem files will remain installed in /Library/Ruby/Gems/2.0.0/gems/ffi-1.9.18 for inspection.
Results logged to /Library/Ruby/Gems/2.0.0/gems/ffi-1.9.18/ext/ffi_c/gem_make.out
Fetching: bundler-1.15.1.gem (100%)
Successfully installed bundler-1.15.1
Parsing documentation for bundler-1.15.1
Installing ri documentation for bundler-1.15.1
1 gem installed

在 OS X 10.9 之后有两种方法可选,一种是安装 Xcode,一种是单独安装 Command Line Tools,这个玩意之前是捆绑在 Xcode 里的!由于我日后需要使用 Xcode 开发,因此我选择安装 Xcode,让我纳闷的是,我移动硬盘里有 Xcode 7.3.1 的安装文件,直接安装后,仍旧报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Building native extensions.  This could take a while...
ERROR: Error installing jekyll:
ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
checking for ffi.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers. Check the mkmf.log file for more details. You may
need configuration options.

Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby
--with-ffi_c-dir
--without-ffi_c-dir
--with-ffi_c-include
--without-ffi_c-include=${ffi_c-dir}/include
--with-ffi_c-lib
--without-ffi_c-lib=${ffi_c-dir}/
--with-libffi-config
--without-libffi-config
--with-pkg-config
--without-pkg-config
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:434:in `try_do': The compiler failed to generate an executable file. (RuntimeError)
You have to install development tools first.
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:549:in `block in try_compile'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:502:in `with_werror'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:549:in `try_compile'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:1038:in `block in have_header'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:889:in `block in checking_for'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:340:in `block (2 levels) in postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:310:in `open'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:340:in `block in postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:310:in `open'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:336:in `postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:888:in `checking_for'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/mkmf.rb:1037:in `have_header'
from extconf.rb:16:in `<main>'


Gem files will remain installed in /Library/Ruby/Gems/2.0.0/gems/ffi-1.9.18 for inspection.
Results logged to /Library/Ruby/Gems/2.0.0/gems/ffi-1.9.18/ext/ffi_c/gem_make.out
Successfully installed bundler-1.15.1
Parsing documentation for bundler-1.15.1
1 gem installed

不过这次报错,可以明确找到原因:(RuntimeError) You have to install development tools first. 所以接下来老老实实地去安装 Command Line Tools 吧!

安装 Command Line Tools

在终端执行 xcode-select --install 后会弹出界面:

我选择了获取 Xcode,然后就自动打开了 AppStore ,然后点击下载安装就行了,只不过需要等上二十分钟了就 OK 了,Xcode安装成功后,记得启动下,因为启动后还会加载一些东西,确保万无一失,最好启动下,然后到 Xcdoe -> Perferences -> Locations 查看下 …

我查了下网上也有人遇到过类似的错误:

安装 Jekyll

如果你的 Ruby 版本小于 2.1.0 的话,就会报如下错误了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bogon:~ xuqianlong$ sudo gem install jekyll bundler
Password:
Building native extensions. This could take a while...
Successfully installed ffi-1.9.18
Fetching: rb-inotify-0.9.10.gem (100%)
Successfully installed rb-inotify-0.9.10
Fetching: listen-3.0.8.gem (100%)
Successfully installed listen-3.0.8
Fetching: jekyll-watch-1.5.0.gem (100%)
Successfully installed jekyll-watch-1.5.0
Fetching: kramdown-1.13.2.gem (100%)
Successfully installed kramdown-1.13.2
Fetching: liquid-4.0.0.gem (100%)
ERROR: Error installing jekyll:
liquid requires Ruby version >= 2.1.0.
Successfully installed bundler-1.15.1
Parsing documentation for bundler-1.15.1
1 gem installed

如果是按照上述步骤来的,那么你应该能够正常的安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Last login: Tue Jun 20 22:21:06 on ttys000
bogon:~ xuqianlong$ gem install jekyll bundler
Fetching: public_suffix-2.0.5.gem (100%)
Successfully installed public_suffix-2.0.5
Fetching: addressable-2.5.1.gem (100%)
Successfully installed addressable-2.5.1
Fetching: colorator-1.1.0.gem (100%)
Successfully installed colorator-1.1.0
Fetching: sass-3.4.24.gem (100%)
Successfully installed sass-3.4.24
Fetching: jekyll-sass-converter-1.5.0.gem (100%)
Successfully installed jekyll-sass-converter-1.5.0
Fetching: rb-fsevent-0.9.8.gem (100%)
Successfully installed rb-fsevent-0.9.8
Fetching: ffi-1.9.18.gem (100%)
Building native extensions. This could take a while...
Successfully installed ffi-1.9.18
Fetching: rb-inotify-0.9.10.gem (100%)
Successfully installed rb-inotify-0.9.10
Fetching: listen-3.0.8.gem (100%)
Successfully installed listen-3.0.8
Fetching: jekyll-watch-1.5.0.gem (100%)
Successfully installed jekyll-watch-1.5.0
Fetching: kramdown-1.13.2.gem (100%)
Successfully installed kramdown-1.13.2
Fetching: liquid-4.0.0.gem (100%)
Successfully installed liquid-4.0.0
Fetching: mercenary-0.3.6.gem (100%)
Successfully installed mercenary-0.3.6
Fetching: forwardable-extended-2.6.0.gem (100%)
Successfully installed forwardable-extended-2.6.0
Fetching: pathutil-0.14.0.gem (100%)
Successfully installed pathutil-0.14.0
Fetching: rouge-1.11.1.gem (100%)
Successfully installed rouge-1.11.1
Fetching: safe_yaml-1.0.4.gem (100%)
Successfully installed safe_yaml-1.0.4
Fetching: jekyll-3.5.0.gem (100%)
Successfully installed jekyll-3.5.0
Parsing documentation for public_suffix-2.0.5
Installing ri documentation for public_suffix-2.0.5
Parsing documentation for addressable-2.5.1
Installing ri documentation for addressable-2.5.1
Parsing documentation for colorator-1.1.0
Installing ri documentation for colorator-1.1.0
Parsing documentation for sass-3.4.24
Installing ri documentation for sass-3.4.24
Parsing documentation for jekyll-sass-converter-1.5.0
Installing ri documentation for jekyll-sass-converter-1.5.0
Parsing documentation for rb-fsevent-0.9.8
Installing ri documentation for rb-fsevent-0.9.8
Parsing documentation for ffi-1.9.18
Installing ri documentation for ffi-1.9.18
Parsing documentation for rb-inotify-0.9.10
Installing ri documentation for rb-inotify-0.9.10
Parsing documentation for listen-3.0.8
Installing ri documentation for listen-3.0.8
Parsing documentation for jekyll-watch-1.5.0
Installing ri documentation for jekyll-watch-1.5.0
Parsing documentation for kramdown-1.13.2
Installing ri documentation for kramdown-1.13.2
Parsing documentation for liquid-4.0.0
Installing ri documentation for liquid-4.0.0
Parsing documentation for mercenary-0.3.6
Installing ri documentation for mercenary-0.3.6
Parsing documentation for forwardable-extended-2.6.0
Installing ri documentation for forwardable-extended-2.6.0
Parsing documentation for pathutil-0.14.0
Installing ri documentation for pathutil-0.14.0
Parsing documentation for rouge-1.11.1
Installing ri documentation for rouge-1.11.1
Parsing documentation for safe_yaml-1.0.4
Installing ri documentation for safe_yaml-1.0.4
Parsing documentation for jekyll-3.5.0
Installing ri documentation for jekyll-3.5.0
Done installing documentation for public_suffix, addressable, colorator, sass, jekyll-sass-converter, rb-fsevent, ffi, rb-inotify, listen, jekyll-watch, kramdown, liquid, mercenary, forwardable-extended, pathutil, rouge, safe_yaml, jekyll after 30 seconds
Fetching: bundler-1.15.1.gem (100%)
Successfully installed bundler-1.15.1
Parsing documentation for bundler-1.15.1
Installing ri documentation for bundler-1.15.1
Done installing documentation for bundler after 5 seconds
19 gems installed

检查 Jekyll 是否安装成功

jekyll -v

终端会输出版本号:jekyll 3.5.0,接下来就可以创建自己的博客站点了!

1
2
3
jekyll new my-awesome-site
~ $ cd my-awesome-site
~/my-awesome-site $ bundle exec jekyll serve

这些操作我开了 VPN,如果不开的话,可能会出现不能安装某些gem的问题,这个需要换下 RubyGems 源,具体可查看淘宝的源 https://ruby.taobao.org/

Good Luck …

我的 MBP 重装回 macOS Sierra 之后,系统自带的 Ruby 环境为 ruby 2.0.0p648 (2015-12-16 revision 53162),我要安装 Jekyll,其中一个 gem 对 Ruby 版本有要求,必须是 2.1.0 以后版本才行!(liquid requires Ruby version >= 2.1.0.)我选择了 RVM 管理 Ruby 版本。

安装 RVM

简单理解下:RVM 管理 Ruby 版本的工具,具体 Ruby 版本通过 homebrew 去下载获取。接下来安装 RVM:

\curl -sSL https://get.rvm.io | bash -s stable

安装过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Downloading https://github.com/rvm/rvm/archive/1.29.1.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc
Found PGP signature at: 'https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc',
but no GPG software exists to validate it, skipping.

Installing RVM to /Users/xuqianlong/.rvm/
Adding rvm PATH line to /Users/xuqianlong/.profile /Users/xuqianlong/.mkshrc /Users/xuqianlong/.bashrc /Users/xuqianlong/.zshrc.
Adding rvm loading line to /Users/xuqianlong/.profile /Users/xuqianlong/.bash_profile /Users/xuqianlong/.zlogin.
Installation of RVM in /Users/xuqianlong/.rvm/ is almost complete:

* To start using RVM you need to run `source /Users/xuqianlong/.rvm/scripts/rvm`
in all your open shell windows, in rare cases you need to reopen all shell windows.

# xuqianlong,
#
# Thank you for using RVM!
# We sincerely hope that RVM helps to make your life easier and more enjoyable!!!
#
# ~Wayne, Michal & team.

In case of problems: https://rvm.io/help and https://twitter.com/rvm_io

更新 Ruby

安装好 RVM 之后,path 还没有生效,因此直接执行:

rvm list known

可能会报错:

-bash: rvm: command not found

这时重新开个新的终端窗口即可;这次执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
bogon:~ xuqianlong$ rvm list known
# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.7[-head] # security released on head
[ruby-]1.9.1[-p431]
[ruby-]1.9.2[-p330]
[ruby-]1.9.3[-p551]
[ruby-]2.0.0[-p648]
[ruby-]2.1[.10]
[ruby-]2.2[.6]
[ruby-]2.3[.3]
[ruby-]2.4[.0]
ruby-head

# for forks use: rvm install ruby-head-<name> --url https://github.com/github/ruby.git --branch 2.2

# JRuby
jruby-1.6[.8]
jruby-1.7[.26]
jruby[-9.1.7.0]
jruby-head

# Rubinius
rbx-1[.4.3]
rbx-2.3[.0]
rbx-2.4[.1]
rbx-2[.5.8]
rbx[-3.71]
rbx-head

# Opal
opal

# Minimalistic ruby implementation - ISO 30170:2012
mruby-1.0.0
mruby-1.1.0
mruby-1[.2.0]
mruby[-head]

# Ruby Enterprise Edition
ree-1.8.6
ree[-1.8.7][-2012.02]

# Topaz
topaz

# MagLev
maglev[-head]
maglev-1.0.0

# Mac OS X Snow Leopard Or Newer
macruby-0.10
macruby-0.11
macruby[-0.12]
macruby-nightly
macruby-head

# IronRuby
ironruby[-1.1.3]
ironruby-head

我不想安装那么新的版本,日后也要安装 CocoPods,他的最低要求版本是 2.2,所以我选择安装 2.2.6 版本:

bogon:~ xuqianlong$ rvm install ruby-2.2.6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
Searching for binary rubies, this might take some time.
No binary rubies available for: osx/10.12/x86_64/ruby-2.2.6.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for osx.
About to install Homebrew, press `Enter` for default installation in `/usr/local`,
type new path if you wish custom Homebrew installation (the path needs to be writable for user)
:
==> This script will install:
/usr/local/bin/brew
/usr/local/share/doc/homebrew
/usr/local/share/man/man1/brew.1
/usr/local/share/zsh/site-functions/_brew
/usr/local/etc/bash_completion.d/brew
/usr/local/Homebrew
==> The following existing directories will be made group writable:
/usr/local/bin
==> The following existing directories will have their owner set to xuqianlong:
/usr/local/bin
==> The following existing directories will have their group set to admin:
/usr/local/bin
==> The following new directories will be created:
/usr/local/Cellar
/usr/local/Homebrew
/usr/local/Frameworks
/usr/local/etc
/usr/local/include
/usr/local/lib
/usr/local/opt
/usr/local/sbin
/usr/local/share
/usr/local/share/zsh
/usr/local/share/zsh/site-functions
/usr/local/var

Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/chmod u+rwx /usr/local/bin
Password:
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/bin
==> /usr/bin/sudo /usr/sbin/chown xuqianlong /usr/local/bin
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/bin
==> /usr/bin/sudo /bin/mkdir -p /usr/local/Cellar /usr/local/Homebrew /usr/local/Frameworks /usr/local/etc /usr/local/include /usr/local/lib /usr/local/opt /usr/local/sbin /usr/local/share /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/Cellar /usr/local/Homebrew /usr/local/Frameworks /usr/local/etc /usr/local/include /usr/local/lib /usr/local/opt /usr/local/sbin /usr/local/share /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var
==> /usr/bin/sudo /bin/chmod 755 /usr/local/share/zsh /usr/local/share/zsh/site-functions
==> /usr/bin/sudo /usr/sbin/chown xuqianlong /usr/local/Cellar /usr/local/Homebrew /usr/local/Frameworks /usr/local/etc /usr/local/include /usr/local/lib /usr/local/opt /usr/local/sbin /usr/local/share /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/Cellar /usr/local/Homebrew /usr/local/Frameworks /usr/local/etc /usr/local/include /usr/local/lib /usr/local/opt /usr/local/sbin /usr/local/share /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var
==> /usr/bin/sudo /bin/mkdir -p /Users/xuqianlong/Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Users/xuqianlong/Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown xuqianlong /Users/xuqianlong/Library/Caches/Homebrew
==> /usr/bin/sudo /bin/mkdir -p /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown xuqianlong /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Counting objects: 6463, done.
remote: Compressing objects: 100% (3911/3911), done.
remote: Total 6463 (delta 3738), reused 4283 (delta 2347), pack-reused 0
Receiving objects: 100% (6463/6463), 3.58 MiB | 231.00 KiB/s, done.
Resolving deltas: 100% (3738/3738), done.
From https://github.com/Homebrew/brew
* [new branch] master -> origin/master
* [new tag] 0.1 -> 0.1
* [new tag] 0.2 -> 0.2
* [new tag] 0.3 -> 0.3
* [new tag] 0.4 -> 0.4
* [new tag] 0.5 -> 0.5
* [new tag] 0.6 -> 0.6
* [new tag] 0.7 -> 0.7
* [new tag] 0.7.1 -> 0.7.1
* [new tag] 0.8 -> 0.8
* [new tag] 0.8.1 -> 0.8.1
* [new tag] 0.9 -> 0.9
* [new tag] 0.9.1 -> 0.9.1
* [new tag] 0.9.2 -> 0.9.2
* [new tag] 0.9.3 -> 0.9.3
* [new tag] 0.9.4 -> 0.9.4
* [new tag] 0.9.5 -> 0.9.5
* [new tag] 0.9.8 -> 0.9.8
* [new tag] 0.9.9 -> 0.9.9
* [new tag] 1.0.0 -> 1.0.0
* [new tag] 1.0.1 -> 1.0.1
* [new tag] 1.0.2 -> 1.0.2
* [new tag] 1.0.3 -> 1.0.3
* [new tag] 1.0.4 -> 1.0.4
* [new tag] 1.0.5 -> 1.0.5
* [new tag] 1.0.6 -> 1.0.6
* [new tag] 1.0.7 -> 1.0.7
* [new tag] 1.0.8 -> 1.0.8
* [new tag] 1.0.9 -> 1.0.9
* [new tag] 1.1.0 -> 1.1.0
* [new tag] 1.1.1 -> 1.1.1
* [new tag] 1.1.10 -> 1.1.10
* [new tag] 1.1.11 -> 1.1.11
* [new tag] 1.1.12 -> 1.1.12
* [new tag] 1.1.13 -> 1.1.13
* [new tag] 1.1.2 -> 1.1.2
* [new tag] 1.1.3 -> 1.1.3
* [new tag] 1.1.4 -> 1.1.4
* [new tag] 1.1.5 -> 1.1.5
* [new tag] 1.1.6 -> 1.1.6
* [new tag] 1.1.7 -> 1.1.7
* [new tag] 1.1.8 -> 1.1.8
* [new tag] 1.1.9 -> 1.1.9
* [new tag] 1.2.0 -> 1.2.0
* [new tag] 1.2.1 -> 1.2.1
* [new tag] 1.2.2 -> 1.2.2
* [new tag] 1.2.3 -> 1.2.3
HEAD is now at 80ce43d Merge pull request #2776 from GauthamGoli/audit_checksum_rubocop_fix
==> Tapping homebrew/core
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core'...
remote: Counting objects: 4444, done.
remote: Compressing objects: 100% (4245/4245), done.
remote: Total 4444 (delta 35), reused 463 (delta 13), pack-reused 0
Receiving objects: 100% (4444/4444), 3.53 MiB | 93.00 KiB/s, done.
Resolving deltas: 100% (35/35), done.
Tapped 4243 formulae (4,487 files, 11MB)
==> Cleaning up /Library/Caches/Homebrew...
==> Migrating /Library/Caches/Homebrew to /Users/xuqianlong/Library/Caches/Homeb
==> Deleting /Library/Caches/Homebrew...
Already up-to-date.
==> Installation successful!

==> Homebrew has enabled anonymous aggregate user behaviour analytics.
Read the analytics documentation (and how to opt-out) here:
http://docs.brew.sh/Analytics.html

==> Next steps:
- Run `brew help` to get started
- Further documentation:
http://docs.brew.sh
Installing requirements for osx.
Updating system.........
Installing required packages: autoconf, automake, libtool, pkg-config, coreutils, libyaml, readline, libksba, openssl..........
Certificates in '/usr/local/etc/openssl/cert.pem' are already up to date.
Requirements installation successful.
Installing Ruby from source to: /Users/xuqianlong/.rvm/rubies/ruby-2.2.6, this may take a while depending on your cpu(s)...
ruby-2.2.6 - #downloading ruby-2.2.6, this may take a while depending on your connection...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 12.7M 100 12.7M 0 0 4958k 0 0:00:02 0:00:02 --:--:-- 4958k
ruby-2.2.6 - #extracting ruby-2.2.6 to /Users/xuqianlong/.rvm/src/ruby-2.2.6...-
ruby-2.2.6 - #configuring......................................................-
ruby-2.2.6 - #post-configuration.
ruby-2.2.6 - #compiling........................................................-
ruby-2.2.6 - #installing..........
ruby-2.2.6 - #making binaries executable..
ruby-2.2.6 - #downloading rubygems-2.6.12
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 749k 100 749k 0 0 858k 0 --:--:-- --:--:-- --:--:-- 858k
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.2.6 - #extracting rubygems-2.6.12....
ruby-2.2.6 - #removing old rubygems.........
ruby-2.2.6 - #installing rubygems-2.6.12.........................
ruby-2.2.6 - #gemset created /Users/xuqianlong/.rvm/gems/ruby-2.2.6@global
ruby-2.2.6 - #importing gemset /Users/xuqianlong/.rvm/gemsets/global.gems......|
ruby-2.2.6 - #generating global wrappers........
ruby-2.2.6 - #gemset created /Users/xuqianlong/.rvm/gems/ruby-2.2.6
ruby-2.2.6 - #importing gemsetfile /Users/xuqianlong/.rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.2.6 - #generating default wrappers........
ruby-2.2.6 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.2.6 - #complete
Ruby was built without documentation, to build it run: rvm docs generate-ri

查看版本:

1
2
bogon:~ xuqianlong$ ruby -v
ruby 2.2.6p396 (2016-11-15 revision 56800)

Ruby 版本已经成功升级,接下来就可以安装 Jekyll, CocoPods 这些库了。

可能出现的错误

1、在执行 rvm install ruby-2.2.6 的过程中可能会出错,比如这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Searching for binary rubies, this might take some time.
No binary rubies available for: osx/10.12/x86_64/ruby-2.2.6.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for osx.
Installing requirements for osx.
Updating system...........
Error running 'requirements_osx_brew_update_system ruby-2.2.6',
showing last 15 lines of /Users/qianlongxu/.rvm/log/1503014778_ruby-2.2.6/update_system.log
https://github.com/Homebrew/homebrew/wiki/Common-Issues
and make sure `brew update` works before continuing.'
++ rvm_pretty_print stderr
++ case "${rvm_pretty_print_flag:=auto}" in
++ case "${TERM:-dumb}" in
++ case "$1" in
++ [[ -t 2 ]]
++ return 1
++ printf %b 'Failed to update Homebrew, follow instructions here:
https://github.com/Homebrew/homebrew/wiki/Common-Issues
and make sure `brew update` works before continuing.\n'
Failed to update Homebrew, follow instructions here:
https://github.com/Homebrew/homebrew/wiki/Common-Issues
and make sure `brew update` works before continuing.
++ return 1
Requirements installation failed with status: 1.

发生这个错误的原因是: Failed to update Homebrew !
于是我就尝试执行: brew update

1
2
3
Error: /usr/local is not writable. You should change the ownership
and permissions of /usr/local back to your user account:
sudo chown -R $(whoami) /usr/local

这个问题是老版本的 Homwbrew 需要 /usr/local 目录的 ownership,因此执行下 sudo chown -R $(whoami) /usr/local 就可以继续了;

brew 更新完毕后有这样一个提示:

1
2
3
4
5
==> Migrating HOMEBREW_REPOSITORY (please wait)...
==> Migrated HOMEBREW_REPOSITORY to /usr/local/Homebrew!
Homebrew no longer needs to have ownership of /usr/local. If you wish you can
return /usr/local to its default ownership with:
sudo chown root:wheel /usr/local

于是更新完毕后,我又把权限修改回去了:sudo chown root:wheel /usr/local.

2、安装成功后,查看 ruby 版本还是系统自动的 2.0.0,使用rvm看的话,确实已经安装了,这是怎么回事?

1
2
3
4
5
6
7
8
9
10
11
qianlongxu:~ qianlongxu$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]
qianlongxu:~ qianlongxu$ rvm list

rvm rubies

=* ruby-2.2.6 [ x86_64 ]

# => - current
# =* - current && default
# * - default

rvm 可以切换 ruby 版本的,使用 rvm use,只要是 rvm list 列出来的,都可以切换,切换时直接跟上版本号就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
qianlongxu:~ qianlongxu$ rvm use 2.2.6

RVM is not a function, selecting rubies with 'rvm use ...' will not work.

You need to change your terminal emulator preferences to allow login shell.
Sometimes it is required to use `/bin/bash --login` as the command.
Please visit https://rvm.io/integration/gnome-terminal/ for an example.

qianlongxu:~ qianlongxu$ rvm use '2.2.6'

RVM is not a function, selecting rubies with 'rvm use ...' will not work.

You need to change your terminal emulator preferences to allow login shell.
Sometimes it is required to use `/bin/bash --login` as the command.
Please visit https://rvm.io/integration/gnome-terminal/ for an example.

再次遇到问题,rvm use 不是个命令!不要急继续往下看,问题是由于 shell 的执行环境不对,修改下登录shell即可:

1
2
3
4
5
qianlongxu:~ qianlongxu$ /bin/bash --login
qianlongxu:~ qianlongxu$ rvm use 2.2.6
Using /Users/qianlongxu/.rvm/gems/ruby-2.2.6
qianlongxu:~ qianlongxu$ ruby -v
ruby 2.2.6p396 (2016-11-15 revision 56800) [x86_64-darwin16]

3、如果你遇到了第二个问题的话,你可能会发现,关闭终端后再次打开,ruby的版本又变成系统的2.0.0了!

原因可能是,你在设置-用户与群组-高级选项里-登录shell里设置了其他shell导致的,改为 /bin/bash 即可。

以上问题是我切换 Mac 用户名之后导致的,一般情况下应该不会遇到的。

参考链接

由于 Mac 无法进入系统,已经分析出了原因 — 磁盘爆满的缘故,因此就想通过进入 PE 的方式,删除掉无用的文件,这样理论上就应该能够顺利进入系统了!

刻盘进 PE

在大学时期曾用 PE 装过 Windows,只不过几年不弄过了,于是就去网上下了 大白菜 PE,然后刻成启动盘,然后开始进入 PE 啦!在 Window 上进 PE 要按下 delete,然后选择从 U 盘启动,而 macOS 则是开机时按下 Option 键,然后就会出现选择启动盘 :

开机前插上 U 盘

按住 Option 开机

PE 是 Win8 系统

可惜进入后无法识别苹果分区

安装识别苹果分区软件

然并卵,高版本的系统都是逻辑分区,所以无法识别!!

Mac PE

网上继续搜寻了一番,发现还有个办法可行,那就是搞个 Mac PE 来识别苹果分区,因为 Mac PE 是使用 macOS 做出来的,所以天然性的就具备识别苹果分区的能力了!啥也不说了,开始装 Mac PE 了:

首先下载镜像文件,google 才能找到官网,百度根本找不到,唉,不说了,去下载吧:

FireWolf OS X PE V7.0 Download Center

Mac 上使用磁盘工具恢复到 U 盘就行了!同样的开机按住 Option 选择 FireWolf 盘符就行了,当时没拍照,直接看下进去后的情况吧 :

启动后我也傻眼了,妈的磁盘是加密的,虽然识别了,但都是加密的文件,备份了也没啥用。新亏我摸索到了方法,找到磁盘实用工具,选择 从 Time Machine 恢复

一步步往下走就行了,走着走着会提示输入密码,因为解密磁盘需要权限,解密后会去搜索备份,结果当然搜不出来了,不过这时文件已经全部解密了!看后面的文件夹名字 : xuqianlong

这下以为总算可以了呢,唉,fuck 了,文件是只读的,不能删除!!!好吧,能解密就谢天谢地了,我用移动硬盘copy下资料吧!资料备份后我就放心了,实现是没别的办法了,我选择了重做系统!

刻盘装 Mac 系统

若是刻录一个 Windows 的启动盘很简单,很容易就能下到 ios 镜像文件,可是由于苹果官方现在只提供了 macOS Sierra 的升级程序,而没提供完整的镜像,想要全新安装的话,只能自己去制作一个 macOS Sierra 的 U 盘启动盘/安装盘了。没有别的办法先下载安装器吧,然后再想办法恢复到 U 盘,这是 macOS Sierra 下载地址,打开 AppStore 下载即可

共 4.96GB,网速慢的话,需要等一阵子了,下载完毕后,你会发现 Launchpad 多出一个 安装 macOS Sierra

然后插上 U 盘,最好是 3.0 的,这样会快些,打开磁盘工具,将其抹掉,格式化为 Mac OS 扩展 (日志式) ,命名为 Sierra,然后打开终端执行以下命令 :

1
sudo /Applications/Install\ macOS\ Sierra.app/Contents/Resources/createinstallmedia --volume /Volumes/Sierra --application /Applications/Install\ macOS\ Sierra.app --nointeraction

前面是安装器执行文件路径,通过 volume 参数指定安装路径,这里指定 U 盘 : /Volumes/Sierra,通过 application 参数指定安装程序,在输入以上命令后,执行过程如下 :

1
2
3
4
5
6
7
Erasing Disk: 0%... 10%... 20%... 30%...100%...
Copying installer files to disk... //这一步会执行好长时间
Copy complete.
Making disk bootable...
Copying boot files...
Copy complete.
Done.

看到 Done 之后,就可以了,你会发现 U 盘名称变为了 Install macOS Sierra,种种迹象表明 U 盘刻录成功了,可以去装机了,按住 Option 开机吧 :

哇咔咔,选择从 Install macOS Sierra 启动!

等待这个进度条走完

好不激动么,选择简体中文

选择安装 macOS

继续吧

选择 MBP 的硬盘

不行,没空间了,所以返回去,找到磁盘工具,格式化硬盘

抹掉就行了

这些可干净了,啥也没了

继续安装

说是7分钟,实际要长些,主要是我 U 盘不是 3.0 的,拷贝文件速度慢

还有 1 分钟

重启是正常的,看来就快好了

选择中国

选择 WiFi

不用传输了

设置下 iCloud

选择时区

哇咔咔,进来了!!!好熟悉的桌面-内华达山脉

刚格式化的时候可用空间是 249G,现在是 239G,看来系统占用了 10G。

使用 U 盘安装系统或者安装 Mac PE 的前提都是要有个 Mac 才行,这样才能做出启动盘来,这对于很多用户而言是不太容易满足的,所以最好别让我们的 Mac 挂掉,省得如此麻烦,特别建议和我一样使用 256 G 磁盘的用户,多给系统预留一些空间,至少 20G 左右,最好是买个移动做下日常硬盘备份,才不会出现我今天的悲剧! 当磁盘空间小时,Mac 经常弹出来一个提示让我清理,我没有去深度清理,我记得我好像剩下 13G 左右,现在想来还挺后悔的,要不然就不用在这折腾了!