【译】《A look inside blocks: Episode 1》(chatgpt plus in uk)
ChatGPT Plus在英国的发布
最近,OpenAI宣布了他们的付费定阅方案ChatGPT Plus在英国的发布。这个付费定阅方案每个月20美元,为用户提供了一些关键优势和更好的AI体验。
ChatGPT Plus的推出时间是2023年7月15日,在这以后,英国的用户可以通过OpenAI的官方网站或App进行定阅。这对英国用户来讲是一个期待已久的好消息。
ChatGPT Plus的价值主要体现在以下因素有哪些。
首先,通过定阅ChatGPT Plus,用户可以取得更高级的AI功能。这意味着它可以回答更多的追问和挑战性问题。对那些希望在对话中进行深入探讨和提出更具挑战性问题的用户来讲,ChatGPT Plus是一个很好的选择。
其次,ChatGPT Plus提供了更快的响应速度和更高的容量。不要钱版的ChatGPT可能会有容量和速度的限制,而ChatGPT Plus可以消除这些限制。这意味着用户可以更快地得到答案,并且可以处理更长、更复杂的对话。
最后,虽然ChatGPT Plus是付费定阅,但价格是公道的。相比初期访问用户,ChatGPT Plus的价格只有一半。依照每个月20美元的定阅费用来看,对提供更好的AI体验来讲是非常划算的。
现在让我们看一下如何定阅ChatGPT Plus。
首先,用户需要在OpenAI的官方网站或App上创建一个账户。然后,在定阅页面上选择ChatGPT Plus方案并完成付款。可能会需要额外的验证步骤,以确保账户的安全。
需要注意的是,由于ChatGPT Plus目前还处于测试阶段,可能会有一定的用户数量限制。一些用户可能需要等待一段时间才能取得定阅资历。
根据来自英国的用户的反馈和使用情况来看,他们对ChatGPT Plus的评价非常积极。他们赞美ChatGPT Plus提供的更高级的功能和体验,并对它的响应速度和容量感到满意。
同时,一些用户也提出了一些建议和期望。他们希望在未来能有更多的付费定阅方案选择,以满足区别用户的需求。他们还建议OpenAI提供更多的定制选项,让用户可以根据自己的需求对ChatGPT进行个性化的设置。
综上所述,ChatGPT Plus在英国的发布为用户提供了更好的AI体验。通过付费定阅,用户可以享受更高级的功能和体验,同时消除不要钱版的容量和速度限制。每个月20美元的定阅费用对提供更好的AI体验来讲是公道的。用户可以通过OpenAI的官方网站或App进行定阅,但需要注意可能的限制和验证要求。根据用户的反馈和使用情况来看,他们对ChatGPT Plus的评价非常积极,并对其提供的功能和体验表示满意。用户希望在未来能有更多的付费定阅方案选择,并希望OpenAI能提供更多的定制选项,以满足个人需求。
chatgpt plus in uk的进一步展开说明
title: 深入理解 Objective-C Block 的工作原理
## 引言
Objective-C中的Block是一种闭包,它提供了一种捕获上下文的方法,能够在代码中嵌入行动。在本文中,我将从编译器的角度深入探究Block是如何工作的,和它是如何作为Objective-C对象出现的。
## Block的基本使用方法
在Objective-C中,我们可使用以下方式定义一个Block:
“`objective-c
void (^block)(void) = ^{
NSLog(@”I’m a block!”);
};
“`
这个例子中,我们定义了一个名称为`block`的变量,并将其赋值为一个简单的Block。
除简单的Block外,我们还可以将变量传递给Block:
“`objective-c
void (^block)(int) = ^(int a){
NSLog(@”I’m a block! a = %i”, a);
};
“`
还可以从Block中返回一个值:
“`objective-c
int (^block)(void) = ^{
NSLog(@”I’m a block!”);
return 1;
};
“`
而且,作为闭包,Block可以捕获其所在位置的上下文:
“`objective-c
int a = 1;
void (^block)(void) = ^{
NSLog(@”I’m a block! a = %i”, a);
};
“`
上述例子中,我将通过对编译后的指令集的分析,来揭露编译器是怎么处理这些代码的细节。
## 简单例子的探究
我选择了一个简单的例子来研究编译器是如何编译Block的。下面是我的例子代码:
“`objective-c
typedef void (^BlockA)(void);
__attribute__((noinline))
void runBlockA(BlockA block) {
block();
}
void doBlockA() {
BlockA block = ^{
};
runBlockA(block);
}
“`
这里,我将Block的设置和调用代码分别写在两个方法中,是为了查看编译器是怎样设置和调用Block。为了确保编译器不会将这两个方法优化为一个方法,我在`runBlockA`方法上添加了`noinline`属性。
编译后的指令以下(以ARMv7为例):
“`assembly
.globl _runBlockA
.align 2
.code 16
.thumb_func _runBlockA
_runBlockA:
ldr r1, [r0, #12]
bx r1
“`
从这段指令中可以看出,`runBlockA`方法只是简单地调用了Block。ARM的EABI规定,r0寄存器用于存储方法的第一个参数。因此,第一条指令就是将r0+12地址处的值加载到r1寄存器中。可以将其理解为对指针的解援用,加载了12个字节的内容。然后我们看到了r1的地址被使用,这意味着r0依然存储着Block本身。所以,这个调用的函数很有可能将Block作为其第一个参数。
根据以上分析,我们可以判定Block是一个结构体,存储着需要调用的函数。当传递一个Block时,实际传递的是指向这个结构体的指针。
接下来,看一下`doBlockA`方法的指令集:
“`assembly
.globl _doBlockA
.align 2
.code 16
.thumb_func _doBlockA
_doBlockA:
movw r0, :lower16:(___block_literal_global-(LPC1_0+4))
movt r0, :upper16:(___block_literal_global-(LPC1_0+4))
LPC1_0:
add r0, pc
b.w _runBlockA
“`
这也很简单。这是一个加载程序计数器相关内容的指令。可以将其理解为将`__block_literal_global`变量的地址加载到r0寄存器中。然后调用了`runBlockA`方法。从汇编指令中可以看出,传递给`runBlockA`的Block对象实际上就是`__block_literal_global`。
现在我们有了一些进展。但是,`__block_literal_global`究竟是甚么呢?通过分析汇编指令,我们发现以下内容:
“`assembly
.align 2
@ @__block_literal_global
___block_literal_global:
.long __NSConcreteGlobalBlock
.long 1342177280 @ 0x50000000
.long 0 @ 0x0
.long ___doBlockA_block_invoke_0
.long ___block_descriptor_tmp
“`
哦,看起来这就像是一个结构体。这个结构体有5个值,每一个值占4个字节(long)。这个结构体极可能就是`runBlockA`操作的Block对象。注意,这个结构体中的第12字节处的值看起来像是一个指针。记住,在汇编指令中,这个指针就是`_doBlockA_block_invoke_0`标记的位置。
那末,`__NSConcreteGlobalBlock`是甚么呢?我会继续看下去。`_doBlockA_block_invoke_0`和`_block_descriptor_tmp`也值得关注,由于它们在下面的汇编代码中也出现了:
“`assembly
.align 2
.code 16
@ @__doBlockA_block_invoke_0
.thumb_func ___doBlockA_block_invoke_0
___doBlockA_block_invoke_0:
bx lr
.section __DATA,__const
.align 2
@ @__block_descriptor_tmp
___block_descriptor_tmp:
.long 0 @ 0x0
.long 20 @ 0x14
.long L_.str
.long L_OBJC_CLASS_NAME_
.section __TEXT,__cstring,cstring_literals
L_.str:
@ @.str
.asciz “v4@?0″
.section __TEXT,__objc_classname,cstring_literals
L_OBJC_CLASS_NAME_:
@ @”\01L_OBJC_CLASS_NAME_”
.asciz “\001”
“`
`_doBlockA_block_invoke_0`看起来更像是实际Block的实现,虽然我们的例子使用的是一个空的Block。这个函数只是简单地返回,这是我们编译空函数的预期结果。
现在来看一下`_block_descriptor_tmp`。这仿佛是另外一个结构体,其中有4个值。第二个值为20,看起来像是`__block_literal_global`结构体的大小。我猜想这多是一个size的值。还有一个名为`.str`的C字符串,值为`v4@?0`,这看起来像是某种类型编码的标识。这多是Block类型的标识(返回空且没有参数的类型)。至于其他值,我没法解释。
通过源代码推测
好了,现在我们可以开始根据以上的线索进行推测了。这些线索都来自于LLVM中的一个名为`compiler-rt`项目。通过浏览这个项目的源代码,我找到了`Block_private.h`文件中的以下定义:
“`c
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, …);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
“`
这看起来非常类似!`Block_layout`结构体就是我们之前分析的`__block_literal_global`,`Block_descriptor`结构体就是我们之前分析的`_block_descriptor_tmp`。我之前对描写符的第二个值是size的猜想是正确的。但是,`Block_descriptor`中的第三和第四个值让人困惑。在我们编译后的指令集中,这两个值是两个字符串,看起来应当是函数指针。这里的解释暂时不得而知。
`Block_layout`中的`isa`非常值得关注,由于它可能就是`_NSConcreteGlobalBlock`。而且,它也多是Block如何具有Objective-C对象行动的关键所在。如果`_NSConcreteGlobalBlock`是一个类,那末Objective-C的消息传递机制会将Block对象视为普通对象对待。这与无缝桥接(toll-free bridging)机制非常类似。如果想了解更多关于无缝桥接的信息,请参考Mike Ash的博文。
综合以上所述,编译器处理该代码的方式可能以下:
“`c
void runBlockA(struct Block_layout *block) {
block->invoke();
}
void block_invoke(struct Block_layout *block) {
}
void doBlockA() {
struct Block_descriptor descriptor;
descriptor->reserved = 0;
descriptor->size = 20;
descriptor->copy = NULL;
descriptor->dispose = NULL;
struct Block_layout block;
block->isa = _NSConcreteGlobalBlock;
block->flags = 1342177280;
block->reserved = 0;
block->invoke = block_invoke;
block->descriptor = descriptor;
runBlockA(&block);
}
“`
至此,我们就能够深入了解Block的工作原理了。
## 下一步
接下来,我会继续探究带有参数的Block是如何捕获变量的。这部份肯定会有所区别,请继续关注!
chatgpt plus in uk的常见问答Q&A
问题1:ChatGPT Plus 是甚么?
答案:ChatGPT Plus 是OpenAI推出的高级定阅计划,它为用户提供了更多功能和好处。ChatGPT是一种对话人工智能模型,可以与用户进行聊天,回答后续问题,并进行挑战。
ChatGPT Plus 定阅计划的特点和好处包括:
- 更快的响应时间:ChatGPT Plus 定阅计划用户将享有更快的模型响应时间,这意味着他们的问题将得到更快的答复。
- 优先访问权:ChatGPT Plus 定阅计划用户在新功能和更新上享有优先访问权,这意味着他们可以尝试和体验新功能。
- 更好的用户体验:ChatGPT Plus 定阅计划用户将取得更好的用户体验,由于他们可以免不要钱用户可能遇到的等待时间和容量限制。
问题2:ChatGPT Plus 如何取得?
答案:要定阅 ChatGPT Plus 计划和使用其中的功能,可以依照以下步骤操作:
- 登录到OpenAI网站:首先,您需要登录OpenAI官方网站。
- 定阅 ChatGPT Plus:然后,在官方网站上选择 ChatGPT Plus 计划,并依照页面上的唆使完成定阅进程。
- 付费和访问功能:完成定阅后,您将需要支付定阅费用(每个月20美元),然后便可访问 ChatGPT Plus 提供的功能。
问题3:ChatGPT Plus 值得吗?
答案:会不会值得定阅 ChatGPT Plus 取决于个人需求和偏好。以下是一些评估 ChatGPT Plus 会不会值得的因素:
- 使用频率:如果您常常使用 ChatGPT 进行聊天和获得答案,那末定阅 ChatGPT Plus 可能值得,由于它提供了更快的响应时间和更好的用户体验。
- 优先访问权:如果您希望在新功能和更新推出时第一时间尝试和体验,那末 ChatGPT Plus 的优先访问权对您来讲多是有吸引力的。
- 预算和价值:斟酌定阅费用(每个月20美元)并权衡它与您对快速响应和高级功能的需求之间的价值。
综上所述,如果您在使用 ChatGPT 进程中感到满意,希望取得更好的体验,并且预算允许,那末定阅 ChatGPT Plus 多是值得的。