Objective-C块属性和Xcode代码补全

34

在Xcode 4中,是否可以定义一个Objective-C块属性并且仍然拥有完整的代码完成功能?

如果我使用typedef来定义块:

typedef void (^CompletionBlock)(MyObject *myObj);

然后定义该属性:

@property (nonatomic, copy) CompletionBlock completionBlock;

然后合成属性@synthesize时,在调用设置器时不会获得完整的代码完成。Xcode会使用typedef,因此代码完成不会使用具有块参数的完整块语法,而是使用typedef。

如果我在头文件中定义了一个使用完整块语法而非typedef的方法原型:

@property (nonatomic, copy) void (^completionBlock)(MyObject *myObj);

然后我使用@synthesize,提供的setter接近于使用完整的代码完成语法,但关键是它省略了参数名称:

[self setCompletionBlock:(void (^)(MyObject *)) { ... }

最后,如果我尝试合成@synthesize,然后覆盖setter实现或将原型放入头文件中:

- (void)setCompletionBlock:(void (^)(MyObject *myObj))completionBlock {...}

会出现警告,指出属性类型与访问器类型不匹配。无论如何,我都无法定义块属性和具有完整语法以进行代码完成的setter。我能否同时拥有这两个功能?

谢谢!


这引发了一个问题... 为什么要使用typedef呢?为了减少功能和多余的代码行吗?也许这是苹果在暗示... "不要这样做!"? - Alex Gray
4个回答

39
你完全可以拥有你的蛋糕并吃掉它,只需要在你的类接口中添加一行额外的代码即可。
首先,使用typedef定义块并创建一个属性,就像你在问题中所做的那样:
typedef void (^CompletionBlock)(MyObject *myObj);

...

@property (nonatomic, copy) CompletionBlock completionBlock;

接下来,正如MobileOverload在他的答案中所指出的那样,我们知道如果在独立的方法声明中使用typedef块,则Xcode会为其提供正确的代码补全。因此,让我们为completionBlock的setter方法添加一个显式声明:

- (void)setCompletionBlock:(CompletionBlock)completionBlock;
当调用此方法时,它将解析为属性声明的setter方法。但是,由于我们在类接口中明确定义了它,因此Xcode会看到它并应用完整的代码完成。因此,如果您包含这三行代码,您应该会得到期望的结果。显然,这种行为是Xcode的缺陷,因为使用@property语句定义的setter与单独定义的相同方法不应有不同的代码完成。

2
这个可行,谢谢!我会提交一个错误报告,希望将来我们不需要显式方法声明。 - user836263
如果我在接口文件中添加显式的setter声明,那么我会收到一个项目警告,指出我的实现不完整。如果我在接口中声明了这个setter,那么我是否需要在.m文件中实现它? - djibouti33
如果您没有声明属性,或者属性定义的 setter 与您明确声明的不同,则应只收到该警告。 - Matt
为具有块的属性显式添加setter就可以解决问题。 - nsuinteger

4

当将块作为类方法的参数传递时,您可以获得一些漂亮的代码完成功能。在头文件中,我使用typedef定义了块,如下所示:

typedef void (^MyCompletionBlock)(id obj1, id obj2);

然后,我可以将它用作我的方法的参数,我也已经在这个类头中声明了这个方法。
-(void)doThisWithBlock:(MyCompletionBlock)block;

在m文件中,我声明了该方法。
-(void)doThisWithBlock:(MyCompletionBlock)block {
    NSLog(@"Something");
}

当我调用它时,我得到了这样的高级代码完成提示。

CodeCompletion1

CodeCompletion2

希望这能回答你的问题。


2
不幸的是,行为会有所不同,并且当您定义具有typedef块类型并使用手动定义的setter方法或生成的@ synthesize的setter时,Xcode不会使用完整的块语法。 - user836263
2
我遇到了同样的问题。从Xcode 4.3.2 (4E2002)开始,这是正确的。你必须在接口部分使用typedef和显式方法声明。当你想要获得完整的代码补全时,既不能使用属性,也不能定义没有typedef块的方法。 - Klaas
Xcode还是这样(在4之后的数字,请自行打码)。唉! - Alex Gray
1
被接受的解决方案仍然似乎是实现所需行为最简单的方法,因为它只需要在接口中声明setter方法。 - user836263
我认为这根本没有回答他的问题。应该在每个单独的属性上显示高亮,而不是整个参数集。他想用一个真实的值替换例如id obj1。你的代码不允许你这样做;相反,它返回你已经传递给完成块的值,以便你可以在块内使用它们。要将值传递到obj1和obj2中,你必须调用block(<一些值>, <一些值>); 这样做就失去了目的。 - James Bush

1

好的,所以我找到了一种权宜之计的方法来解决这个问题,不会导致警告/错误... 而且实际上使事情更加易读/输入更短等。

定义一个带有我们的“缩写”的宏,然后在属性声明中使用完整格式,如...

#define TINP NSString*(^)(NSString *typed, const char *raw)
@interface ....
@property (copy) NSString*(^termDidReadString)(NSString *typed, const char *raw);

Subsequently(随后),你可以引用那种类型的参数,等等,例如...
+ (void)addInputBlock:(TINP)termDidReadString;

然后,不仅您的代码将更加简洁!而且代码完成功能也会像魔法一样正常运行...

enter image description here


0

我不知道完整的代码自动补全,但是你可以使用代码片段来获得类似于代码自动补全的行为,并且你可以在代码片段中使用占位符 <#PLACE HOLDER#>。 希望这能帮到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接