Python print trick

Although we have powerful debugging tools in most of IDE, I still love just printing out what I want in the console. It’s fast and simple. Well, In Python, we use ‘print’.
Sometimes I need to print out some mixed sentence with string and variable, I use print ‘Hello ‘ + name to make it easy to read(Notice the whitespace right after the ‘Hello’.

Now I’m using print ‘Hello’, name to do the same thing. In print statement, comma serves the same as ‘ ‘.

Change project name in XCode 4

http://stackoverflow.com/questions/5043066/change-name-of-iphone-app-in-xcode-4

So I just steal the right answer from the link above (pasted below) because it’s not the accepted answer.

“OK, here is what I found, took me a bit of hunting after reading this post, so hopefully this answer will help everyone:

  1. In the project contents pane at left, click the Folder icon.
  2. Select the top-level blue line representing the project.
  3. If you don’t have the Inspector pane (right pane) open, click the button to enable it. This is the third button in the “View” toolbar towards the upper right corner.
  4. In the Inspector pane, click the little folded-corner paper (file properties) icon.
  5. First thing is Project Name. Type in a new one, you will be prompted to rename project content files. I should add, this will leave your old app with the original name in the simulator, so you still need to delete it from the simulator. Also any files you created in the old app bundle will not be brought forward to the new bundle (unless you find them in Finder and move them over).

(EDIT:) One more important note. Renaming your bundle will mean that the App Bundle Identifier you registered with Apple Provisioning Portal will no longer work for that app. You will need to generate a new App ID and Provisioning Profile in order to distribute your app to a physical device.”

Determine a list is sub-list of another list in Python

Use set:

a = [1,2,3,4,5,6,7]
b = [2,3]
c = [4,8]

>>print set(b).issubset(set(a))
>>True

>>print set(c).issubset(set(a))
>>False

Use collections.defaultdict([type]) in python

Generally, if you want add a key/value pair into a dict, probably you would do:

a = []
if 'name' not in a.keys():
    a['name'] = 0
else:
    a['name'] += 1

With collections.defaultdict(type), you can make it much simpler:

import collections
a = collections.defaultdict(int)
a['name'] += 1 

# Output
>>a
>>{'name': 1}

Woot!

Efficient String Concatenation in Python

Very good article on efficiency of different string concatenation approaches in Python: http://www.skymind.com/~ocrow/python_string/

Recovering from “std:old lock file” when firing mongod up

I got into trouble when I was trying to fire mongod up on my laptop. It throws me “std:old lock file” exception.

There’s several threads out there up on Google saying that using “mongod –repair” or “mongod –unlock” command. Unfortunately, it doesn’t work for me. And I figure out the solution is pretty simple

Search for the mongod.lock file and then remove it.


It works. =)

MongoDB安装指南

由于工作需要,开始学习MongoDB。在这里先精简一下官网上的tutorial,把MongoDB的安装关键流程罗列一下,希望对同学们有所帮助。暂时只有Macos版本。

安装MongoDB

(原文:http://www.mongodb.org/display/DOCS/Quickstart+OS+X

1. 安装MongoDB

对于用Unix-based操作系统的童鞋,推荐用homebrew安装,homebrew是安装UNIX工具十分方便的平台(详情察看:http://mxcl.github.com/homebrew/

Homebrew顺利安装后,运行:

$ brew update
$ brew install mongodb

32位版本MongoDB:

$ curl http://downloads.mongodb.org/osx/mongodb-osx-i386-x.y.z.tgz > mongo.tgz
$ tar xzf mongo.tgz

64位版本MongoDB(推荐) :

$ curl http://downloads.mongodb.org/osx/mongodb-osx-x86_64-x.y.z.tgz > mongo.tgz
$ tar xzf mongo.tgz

注意把x.y.z替换为最新的稳定版本号

2. 创建数据库文件夹

安装完成后,我们需要手动为MongoDB创建一个存放数据的文件夹,MongoDB默认将数据存入/data/db文件夹, 但是它在第一次运行时并不会自动为你创建该文件夹。

$ mkdir -p /data/db

如果你想自定义文件夹路径,可以使用–dbpath选项进行设置。

3. 运行并连接mongodb服务器

至此,mongoDB已经顺利被安装了。你可以运行以下script测试:

首先在一个terminal中运行

$ ./mongodb-xxxxxxx/bin/mongod

mongodb-xxxxxxx是mongodb的安装路径。 你将会看到一系列的mongdodb服务器启动的信息。

接着打开另一个terminal作为client,运行如下script:

$ ./mongodb-xxxxxxx/bin/mongo
> db.foo.save( { a : 1 } )
> db.foo.find()

如果含有{ a : 1 }的信息被检索出现,那么恭喜你,mongodb已经被正确安装成功了。

 

单节点多副本安装流程

(原文:http://www.mongodb.org/display/DOCS/Replica+Set+Tutorial

下面介绍一下在本地进行多副本测试的安装流程:

(对于对mongodb副本规则不了解的童鞋可以先学习一下副本规则文档,在此只作简单介绍)

在MongoDB中,一个副本集(Replica Set)由N个mongod节点组成。他们相互协作提供所必需的容错机制。在现今工业标准中,N一般被设为3。它的意义是,每一分数据都会被复制两分连同原始数据一起被存放到三个不同的节点上。由于每个节点都是普通的个人计算机,各种原因造成的系统崩溃在所难免,当一个节点系统崩溃后,由另外两个节点作为后援替代已崩溃节点以保证数据的完整性。当然,其内部有一整套复杂的容错机制,并且不同的系统的容错机制都略有不同,有兴趣的同学可以学习一下几篇云计算领域内比较重量级的论文,如: Google File System, Amazon Dynamo, Google Map-Reduce System, Google BigTable

1. 启动节点

首先,在本地磁盘上为每个节点创建3个数据库文件

 mkdir -p /data/r0
 mkdir -p /data/r1
 mkdir -p /data/r2

接着,使用–replSet启动第一个mongod数据库进程。–replSet的参数是副本集的名字。在此我们将副本集命名为”foo”:

 mongod --replSet foo --port 27017 --dbpath /data/r0 --rest

在此我个人加上了–rest参数,这是为了启动http interface。 启动后,你就可以用浏览器登陆localhost:28017监控副本集的详细信息了。

同样的方法启动第二第三个mongod:

mongod --replSet foo --port 27018 --dbpath /data/r1 --rest
mongod --replSet foo --port 27019 --dbpath /data/r2 --rest
至此,你已经有三个运行的mongod节点了,但是它们还没有被初始化,所以你应该可以看到如下警告:
Mon Aug  2 11:30:19 [startReplSets] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)

 

2. 初始化副本集

我们可以对三个之中的一个副本节点操作以初始化整个副本集,方法如下:

mongo localhost:27017
[kyle@arete ~$]$ mongo localhost:27017
MongoDB shell version: 1.5.7
connecting to: localhost:27017/test
> config = {_id: 'foo', members: [
                          {_id: 0, host: 'localhost:27017'},
                          {_id: 1, host: 'localhost:27018'},
                          {_id: 2, host: 'localhost:27019'}]
           }

> rs.initiate(config);
{
   "info" : "Config now saved locally.  Should come online in about a minute.",
   "ok" : 1
}

在此,我们制定了配置对象config,并且把配置信息传入rs.initiate() 方法中。 如果运行顺利,在一分钟内我们就可以得到副本集初始化成功的回复,在这一分钟里,有一个节点被选举成为master。

你可以使用rs.status()方法来验证副本集的配置成功与否。

 

 

 

如何卸载Xcode

最近又开始需要用xcode了,这是才发现许久前下载的xcode4不太好用。 速度变慢,而且许多layout都变了,很不习惯。(当然Apple发布了自己的xcode3过度到xcode4的指南,有需要的童鞋可以点这里。居然洋洋洒洒七八十页。。holy crap!)决定还是退回xcode3作业。下面是如何完整卸载xcode的方法。

只需要在Terminal内输入: sudo /Developer/Library/uninstall-devtools –mode=all

Boom! It’s gone.

iPhone/Mac Objective-C内存管理教程和原理剖析

最近在学objective-c,在学习过程中对obj-c的内存管理和delegate/protocol机制挺头痛的。 由于以前大一的时候学c语言内存管理和指针这方面就没学好,后来coding方面就荒废了。直到今年开始学学java,又是全自动的garbage collection。 所以内存指针方面一直是我的隐痛。 在cocoachina.com上面新手区晃荡时候发现有一个帖子对内存管理总结归纳解释得非常清晰,看完以后豁然开朗。所以在此分享给大家,共同学习。

--------------------------------------

版权声明

此文版权归作者Vince Yuan (vince.yuan#gmail.com)所有。欢迎非营利性转载,转载时必须包含原始链接http://vinceyuan.cnblogs.com/,且必须包含此版权声明的完整内容。

版本 1.1  发表于2010-03-08

前言

初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理机制的研究成果和经验,写了这么一个由浅入深的教程。希望对大家有所帮助,也欢迎大家一起探讨。

此文涉及的内存管理是针对于继承于NSObject的Class。

一 基本原理

Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。

1           Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。

ClassA *obj1 = [[ClassA alloc] init];

2           Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。

[obj1 dealloc];

这带来了一个问题。下面代码中obj2是否需要调用dealloc?

ClassA *obj1 = [[ClassA alloc] init];

ClassA *obj2 = obj1;

[obj1 hello]; //输出hello

[obj1 dealloc];

[obj2 hello]; //能够执行这一行和下一行吗?

[obj2 dealloc];

不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。

如何避免无效指针?请看下一条。

3           Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。

ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1

[obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁

我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj1 hello]; //输出hello

[obj1 release]; //retain count = 0,对象被销毁

[obj2 hello];

[obj2 release];

[obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。

4           Objective-C指针赋值时,retain count不会自动增加,需要手动retain。

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj2 retain]; //retain count = 2

[obj1 hello]; //输出hello

[obj1 release]; //retain count = 2 – 1 = 1

[obj2 hello]; //输出hello

[obj2 release]; //retain count = 0,对象被销毁

问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)

这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。

5           Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)

5.1          新生成的对象,只要调用autorelease就行了,无需再调用release!

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release

5.2          对于存在指针赋值的情况,代码与前面类似。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj2 retain]; //retain count = 2

[obj1 hello]; //输出hello

//对于obj1,无需调用(实际上不能调用)release

[obj2 hello]; //输出hello

[obj2 release]; //retain count = 2-1 = 1

细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。

6           autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)

6.1          autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

6.2          NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中

6.3          NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。

6.4          默认只有一个autorelease pool,通常类似于下面这个例子。

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool;

pool = [[NSAutoreleasePool alloc] init];

// do something

[pool release];

return (0);

} // main

所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

int i, j;

for (i = 0; i < 100; i++ )

{

for (j = 0; j < 100000; j++ )

[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

}

[pool release];

return (0);

} // main

(可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。

7           Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

int i, j;

for (i = 0; i < 100; i++ )

{

NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

for (j = 0; j < 100000; j++ )

[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

[loopPool release];

}

[pool release];

return (0);

} // main

版本 1.1 发表于2010-03-08

二 口诀与范式

1           口诀。

1.1          谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。

1.2          除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。

1.3          谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。

2           范式。
范式就是模板,就是依葫芦画瓢。由于不同人有不同的理解和习惯,我总结的范式不一定适合所有人,但我能保证照着这样做不会出问题。

2.1          创建一个对象。

ClassA *obj1 = [[ClassA alloc] init];

2.2          创建一个autorelease的对象。

ClassA *obj1 = [[[ClassA alloc] init] autorelease];

2.3          Release一个对象后,立即把指针清空。(顺便说一句,release一个空指针是合法的,但不会发生任何事情)

[obj1 release];

obj1 = nil;

2.4          指针赋值给另一个指针。

ClassA *obj2 = obj1;

[obj2 retain];

//do something

[obj2 release];

obj2 = nil;

2.5          在一个函数中创建并返回对象,需要把这个对象设置为autorelease

ClassA *Func1()

{

ClassA *obj = [[[ClassA alloc]init]autorelease];

return obj;

}

2.6          在子类的dealloc方法中调用基类的dealloc方法

-(void) dealloc

{

[super dealloc];

}

2.7          在一个class中创建和使用property。

2.7.1     声明一个成员变量。

ClassB *objB;

2.7.2     声明property,加上retain参数。

@property (retain) ClassB* objB;

2.7.3     定义property。(property的默认实现请看第三章)

@synthesize objB;

2.7.4     除了dealloc方法以外,始终用.操作符的方式来调用property。

self.objB 或者objA.objB

2.7.5     在dealloc方法中release这个成员变量。

[objB release];

示例代码如下(详细代码请参考附件中的memman-property.m,你需要特别留意对象是在何时被销毁的。):

@interface ClassA : NSObject

{

ClassB* objB;

}

@property (retain) ClassB* objB;

@end

@implementation ClassA

@synthesize objB;

-(void) dealloc

{

[objB release];

[super dealloc];

}

@end

2.7.6     给这个property赋值时,有手动release和autorelease两种方式。

void funcNoAutorelease()

{

ClassB *objB1 = [[ClassB alloc]init];

ClassA *objA = [[ClassA alloc]init];

objA.objB = objB1;

[objB1 release];

[objA release];

}

void funcAutorelease()

{

ClassB *objB1 = [[[ClassB alloc]init] autorelease];

ClassA *objA = [[[ClassA alloc]init] autorelease];

objA.objB = objB1;

}

版本 1.1  发表于2010-03-08

三 @property (retain)和@synthesize的默认实现

在这里解释一下@property (retain) ClassB* objB;和@synthesize objB;背后到底发生了什么(retain property的默认实现)。property实际上是getter和setter,针对有retain参数的property,背后的实现如下(请参考附件中的memman-getter-setter.m,你会发现,结果和memman-property.m一样):

@interface ClassA : NSObject

{

ClassB *objB;

}

-(ClassB *) getObjB;

-(void) setObjB:(ClassB *) value;

@end

@implementation ClassA

-(ClassB*) getObjB

{

return objB;

}

-(void) setObjB:(ClassB*) value

{

if (objB != value)

{

[objB release];

objB = [value retain];

}

}

在setObjB中,如果新设定的值和原值不同的话,必须要把原值对象release一次,这样才能保证retain count是正确的。

由于我们在class内部retain了一次(虽然是默认实现的),所以我们要在dealloc方法中release这个成员变量。

-(void) dealloc

{

[objB release];

[super dealloc];

}

版本 1.1 发表于2010-03-08

四 系统自动创建新的autorelease pool

在生成新的Run Loop的时候,系统会自动创建新的autorelease pool(非常感谢网友hhyytt和neogui的提醒)。注意,此处不同于xcode在新建项目时自动生成的代码中加入的autorelease pool,xcode生成的代码可以被删除,但系统自动创建的新的autorelease pool是无法删除的(对于无Garbage Collection的环境来说)。Objective-C没有给出实现代码,官方文档也没有说明,但我们可以通过小程序来证明。

在这个小程序中,我们先生成了一个autorelease pool,然后生成一个autorelease的ClassA的实例,再在一个新的run loop中生成一个autorelease的ClassB的对象(注意,我们并没有手动在新run loop中生成autorelease pool)。精简的示例代码如下,详细代码请见附件中的memman-run-loop-with-pool.m。

int main(int argc, char**argv)

{

NSLog(@”create an autorelasePooln”);

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSLog(@”create an instance of ClassA and autoreleasen”);

ClassA *obj1 = [[[ClassA alloc] init] autorelease];

NSDate *now = [[NSDate alloc] init];

NSTimer *timer = [[NSTimer alloc] initWithFireDate:now

interval:0.0

target:obj1

selector:@selector(createClassB)

userInfo:nil

repeats:NO];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];

[timer release];

[now release];

[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例

NSLog(@”releasing autorelasePooln”);

[pool release];

NSLog(@”autorelasePool is releasedn”);

return 0;

}

输出如下:

create an autorelasePool

create an instance of ClassA and autorelease

create an instance of ClassB and autorelease

ClassB destroyed

releasing autorelasePool

ClassA destroyed

autorelasePool is released

注意在我们销毁autorelease pool之前,ClassB的autorelease实例就已经被销毁了。

有人可能会说,这并不能说明新的run loop自动生成了一个新的autorelease pool,说不定还只是用了老的autorelease pool,只不过后来drain了一次而已。我们可以在main函数中不生成autorelease pool。精简的示例代码如下,详细代码请见附件中的memman-run-loop-without-pool.m。

int main(int argc, char**argv)

{

NSLog(@”No autorelasePool createdn”);

NSLog(@”create an instance of ClassAn”);

ClassA *obj1 = [[ClassA alloc] init];

NSDate *now = [[NSDate alloc] init];

NSTimer *timer = [[NSTimer alloc] initWithFireDate:now

interval:0.0

target:obj1

selector:@selector(createClassB)

userInfo:nil

repeats:NO];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];

[timer release];

[now release];

[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例

NSLog(@”Manually release the instance of ClassAn”);

[obj1 release];

return 0;

}

输出如下:

No autorelasePool created

create an instance of ClassA

create an instance of ClassB and autorelease

ClassB destroyed

Manually release the instance of ClassA

ClassA destroyed

我们可以看出来,我们并没有创建任何autorelease pool,可是ClassB的实例依然被自动销毁了,这说明新的run loop自动创建了一个autorelease pool,这个pool在新的run loop结束的时候会销毁自己(并自动release所包含的对象)。

补充说明

在研究retain count的时候,我不建议用NSString。因为在下面的语句中,

NSString *str1 = @”constant string”;

str1的retain count是个很大的数字。Objective-C对常量字符串做了特殊处理。

当然,如果你这样创建NSString,得到的retain count依然为1

NSString *str2 = [NSString stringWithFormat:@”123”];

涉及的示例程序代码可在作者博客上获取:

Macbook使用外接显示器作为单一主显示桌面的方法

最近终于换上了新Macbook Air(2010),了却了我期盼多年的心愿。 同时,家里原本就备着的Samsung 24inch显示器也终于等到了好主子。但接上后发现一个问题,困扰我许久。Mac OS 原生并不支持将外置显示器作为单一主显示桌面,换而言之,就是将桌面显示在外接显示器并且仅显示在外接显示器上。 系统内可以选择延伸显示 (extend)和镜像显示 (mirror) ,但是两者皆会带来使用上不同程度得不变。所幸,一次偶然的机会让我发现了实现该外接单一显示的方法,并结合Google上搜的他人经验整理得实现步骤。在此与各位分享。

1. 将笔记本与Macbook连接。

2. 连接完成后,你会发现你现在正处于延伸显示或镜像显示模式之一。如果当前处于延伸显示,那请切换到镜像显示 (在镜像显示模式前打钩即可)。如果当前是镜像显示,请继续看步骤3。

3. 合上笔记本。使系统睡眠。(或者手动选择睡眠)

4. 拔出或插入任意USB设备。此时,系统会自动被唤醒。

5. 恭喜! 现在你的外接显示器已经成为唯一的显示设备了。可以尝试移动鼠标至桌面四周,检查是否会跨屏。

( 注:有些筒子们可能并没有外接键盘,仍然要依靠笔记本自带键盘操作。此时,你可以打开之前被你合上的笔记本,所有设备依然工作唯独笔记本屏幕没有被点亮。)

本文部分内容参考: http://hints.macworld.com/article.php?story=20060607121656766

最新发现更简单步骤:如果macbook有外接usb或者蓝牙设备 比如鼠标键盘等。只需要在睡眠状态下点击鼠标或者按键即可唤醒并且进入非extend display模式。 如果没有鼠标键盘等外接设备,在睡眠状态下插把任何usb设备即可。