gpt4 book ai didi

ios - 使用 block 在现场创建代表

转载 作者:可可西里 更新时间:2023-11-01 04:10:04 25 4
gpt4 key购买 nike

我喜欢积木,当我不能使用积木时,这让我很难过。特别是,这主要发生在我每次使用委托(delegate)时(例如:使用 UIKit 类,主要是预块功能)。

所以我想知道......是否有可能 - 使用 ObjC 的疯狂力量 - 做这样的事情?

   // id _delegate; // Most likely declared as class variable or it will be released
_delegate = [DelegateFactory delegateOfProtocol:@protocol(SomeProtocol)];
_delegate performBlock:^{
// Do something
} onSelector:@selector(someProtocolMethod)]; // would execute the given block when the given selector is called on the dynamic delegate object.
theObject.delegate = (id<SomeProtocol>)_delegate;
// Profit!
performBlock:onSelector:
YES , 如何?是否有理由不让我们尽可能多地这样做?

编辑

看起来这是可能的。当前的答案集中在问题的第一部分,即如何做。但最好就“我们应该这样做”部分进行一些讨论。

最佳答案

好的,我终于开始放 WoolDelegate在 GitHub 上。现在我只需要再花一个月的时间来编写一个合适的 README(虽然我想这是一个好的开始)。

委托(delegate)类本身非常简单。它只是维护一个字典映射 SEL s 阻止。当一个实例收到一条没有响应的消息时,它以 forwardInvocation: 结束。并在字典中查找选择器:

- (void)forwardInvocation:(NSInvocation *)anInvocation {

SEL sel = [anInvocation selector];
GenericBlock handler = [self handlerForSelector:sel];

如果找到,块的调用函数指针被拉出并传递给 juicy 位:
    IMP handlerIMP = BlockIMP(handler);

[anInvocation Wool_invokeUsingIMP:handlerIMP];
}

( BlockIMP() 函数以及其他块探测代码都归功于 Mike Ash 。实际上,这个项目的很多内容都是建立在我从他的周五问答中学到的东西的基础上的。如果你还没有读过这些文章,你错过了。)

我应该注意到,每次发送特定消息时,这都会通过完整的方法解析机制;那里有速度。替代方案是 Erik H. 和 EMKPantry 的路径每个都接受了,它为您需要的每个委托(delegate)对象创建了一个新类,并使用 class_addMethod() .由于 WoolDelegate 的每个实例有自己的处理程序字典,我们不需要这样做,但另一方面,没有办法“缓存”查找或调用。方法只能添加到类中,不能添加到实例中。

我这样做有两个原因:这是一个练习,看看我是否能完成接下来的部分——来自 NSInvocation 的交接。阻止调用——并且为每个需要的实例创建一个新类对我来说似乎很不雅观。它是否不如我的解决方案优雅,我将留给每个读者的判断。

继续,这个程序的主要内容实际上在 NSInvocation category 中。这是在项目中找到的。这利用了 libffi调用一个在运行时之前未知的函数——块的调用——使用在运行时之前也是未知的参数(可以通过 NSInvocation 访问)。通常,这是不可能的,原因与 va_list 相同。不能传递:编译器必须知道有多少参数以及它们有多大。 libffi 包含每个平台的汇编程序,这些平台知道/基于这些平台的 calling conventions .

这里有三个步骤:libffi 需要一个被调用函数的参数类型列表;它需要将参数值本身放入特定格式;然后需要通过 libffi 调用函数(块的调用指针)并将返回值放回 NSInvocation .

第一部分的实际工作主要由 Mike Ash 编写的函数处理,该函数从 Wool_buildFFIArgTypeList 调用。 . libffi 有内部 struct s 用于描述函数参数的类型。在准备调用函数时,库需要一个指向这些结构的指针列表。 NSMethodSignatureNSInvocation允许访问每个参数的编码字符串;从那里翻译成正确的 ffi_type由一组 if 处理/ else查找:
arg_types[i] = libffi_type_for_objc_encoding([sig getArgumentTypeAtIndex:actual_arg_idx]);

...

if(str[0] == @encode(type)[0]) \
{ \
if(sizeof(type) == 1) \
return &ffi_type_sint8; \
else if(sizeof(type) == 2) \
return &ffi_type_sint16; \

接下来,libffi 需要指向参数值本身的指针。这是在 Wool_buildArgValList 中完成的: 再次从 NSMethodSignature 获取每个参数的大小,并分配一块该大小的内存,然后返回列表:
NSUInteger arg_size;
NSGetSizeAndAlignment([sig getArgumentTypeAtIndex:actual_arg_idx],
&arg_size,
NULL);
/* Get a piece of memory that size and put its address in the list. */
arg_list[i] = [self Wool_allocate:arg_size];
/* Put the value into the allocated spot. */
[self getArgument:arg_list[i] atIndex:actual_arg_idx];

(旁白:代码中有几个关于跳过 SEL 的注释,它是(隐藏的)第二个传递给任何方法调用的参数。块的调用指针没有一个插槽来保存 SEL ;它只是将自己作为第一个参数,其余的是“正常”参数。由于用客户端代码编写的 Block 无论如何都无法访问该参数(当时它不存在),因此我决定忽略它.)

libffi 现在需要做一些“准备”;只要成功(并且可以分配返回值的空间),现在可以“调用”调用函数指针,并且可以设置返回值:
ffi_call(&inv_cif, (genericfunc)theIMP, ret_val, arg_vals);
if( ret_val ){
[self setReturnValue:ret_val];
free(ret_val);
}

项目中的 main.m 中有一些功能的演示。

最后,对于你的“应该这样做吗?”的问题,我认为答案是“可以,只要它能让你更有效率”。 WoolDelegate是完全通用的,并且一个实例可以像任何完全写出的类一样工作。不过,我的目的是制作简单的一次性委托(delegate)——只需要一两种方法,并且不需要超越委托(delegate)人——比编写一个全新的类更少的工作,而且更清晰/maintainable 而不是将一些委托(delegate)方法粘贴到 View Controller 中,因为这是放置它们的最简单的地方。像这样利用运行时和语言的动态性希望可以以同样的方式提高代码的可读性,例如, Block-based NSNotification handlers做。

关于ios - 使用 block 在现场创建代表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15438410/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com