Effective Objective-C 2.0: Item 38: Create typedefs for Common Block Types
2013-12-11 22:20
429 查看
Item 38: Create typedefs for Common Block Types
Blocks have an inherent type; that is, they can be assigned to an appropriately typed variable. The type is made up of the parameters the block takes along and with the returntype of the block. For example, consider the following block:
^(BOOL flag, int value){
if (flag) {
return value * 5;
} else {
return value * 10;
}
}
This block takes two parameters of type
BOOLand
intand returns a value of
type
int. If it were to be assigned to a variable, this block would need to be appropriately typed. The type and assignment to the variable would look like this:
Click here to view code image
int (^variableName)(BOOL flag, int value)
=
^(BOOL flag, int value){
// Implementation
return someInt;
}
This looks quite different from a normal type but should look familiar if you’re used to function pointers. The
layout of the type is as follows:
return_type (^block_name)(parameters)
A block-variable definition is different from other types in that the variable name is in the middle of the type rather than on the right. This makes it quite difficult to remember
the syntax and to read. For these reasons, it is a good idea to make type definitions for commonly used block types, especially if you are shipping an API that you expect others to use. However, it
is possible to hide block types like this behind a name that is easy to read and indicates what the block is meant to do.
To hide the complicated block type, you use a language feature from C called type definitions. The keyword
typedefallows
you to define an easy-to-read name that becomes an alias for another type. For example, to define a new type for the block that returns an
intand takes two parameters—a
BOOLand
an
int—you would use the following type definition:
typedef int(^EOCSomeBlock)(BOOL flag, int value);
Just as the variable name of a block is in the
middle prefixed by a caret, so too is the new type name. This adds a new type, called
EOCSomeBlock, to the type system. So instead of creating a
variable with a complicated type, you can simply use this new type:
Click here to view code image
EOCSomeBlock block = ^(BOOL flag, int value){
// Implementation
};
This code is now much easier to read, as the variable definition is back to the type on the left and the variable name on the right, just like you’re used to seeing with
other variables.
Using this feature can make APIs that use blocks much easier to use. Any class that takes a block as a parameter to a method—for example, a completion handler for an asynchronous
task—can make use of this feature to make it a lot easier to read. Consider, for example, a class that has a
startmethod that takes a block as a handler
to be run when the task finishes. Without making use of a type definition, the method signature may look like this:
Click here to view code image
- (void)startWithCompletionHandler:
(void(^)(NSData *data, NSError *error))completion;
Note that the way the syntax for a block type is laid out is different again from that for defining a variable. It’s much easier to read if the type in the method signature is
a single word. So you can define a type definition and then use that instead:
Click here to view code image
typedef void(^EOCCompletionHandler)
(NSData *data, NSError *error);
- (void)startWithCompletionHandler:
(EOCCompletionHandler)completion;
This is now much simpler to read and understand what the parameter is. Any good, modern Integrated Development Environment (IDE) will automatically expand the type definition,
making this easy to use.
Using a type definition is also useful if you ever need to refactor to change the block’s type signature. For
example, if you decide that the completion handler block now needs to take an additional parameter to pass the time the task took, you can simply change the type definition:
Click here to view code image
typedef void(^EOCCompletionHandler)
(NSData *data, NSTimeInterval duration, NSError *error);
Anywhere the type definition is used, such as method signatures, will now fail to compile in the same way, and you can go
through and fix things. Without the type definition, you would find that you needed to change many types throughout your code. It would potentially be easy to miss one or two of these, leading to hard-to-find bugs.
It is usually best to define these type definitions along with the class that uses them. It is also
prudent to prefix the new type’s name with the class that uses the type definitions. This makes the block’s use clear. If multiple type definitions end up being created for the same block signature type, fine. It’s better to have more types than
fewer.
An example of this point can be seen in the Accounts framework from Mac OS X and iOS. This framework defines, among others, the following block type definitions:
Click here to view code image
typedef void(^ACAccountStoreSaveCompletionHandler)
(BOOL success, NSError *error);
typedef void(^ACAccountStoreRequestAccessCompletionHandler)
(BOOL granted, NSError *error);
These block type definitions have the same signature but are used in distinct places. The name of the type and the name of the parameters in the signature make it easy
for the developer to understand how the type is to be used. The developer could have defined a single type definition, perhaps called
ACAccountStoreBooleanCompletionHandler,
used in place of both of these. However, that would lose the clarity of how the block and parameters are used.
Similarly, if you have a few classes that perform a similar but distinct asynchronous task but that don’t fit into a class hierarchy, each class should have its own completion
handler type. The signature may be exactly the same for each one, but it’s better to use a type definition for each class rather than a single one. On the other hand, if the classes fit into a hierarchy, you could define the type definition along with the
base class and have each subclass use it.
Things to Remember
Use
type definitions to make it easier to use block variables.
Always
follow the naming conventions when defining new types such that you do not clash with other types.
Don’t
be afraid to define multiple types for the same block signature. You may want to refactor one place that uses a certain block type by changing the block signature but not another.
相关文章推荐
- Effective Objective-C 2.0:Item 48: Prefer Block Enumeration to for Loops
- [MST] Create Dynamic Types and use Type Composition to Extract Common Functionality
- 对The method createBlob() is undefined for the type Hibernate异常的解决办法
- 'date' is an invalid value for the SoapElementAttribute.DataType property. The property may only be specified for primitive types.
- ArcGIS Engine中初始化许可常见问题归纳,the application is not licensed to create or modify schema for this type of data
- Effective C#之Item 49:Prepare for C# 2.0
- TypeError: Error #1010: 术语尚未定义,并且无任何属性。at mx.controls::List/createItemRenderer()[E:\dev\4.0.0\framew
- 今天下载安装了Enterprise Library for .NET Framework 2.0 - January 2006,准备试试dataAccess application block
- java.lang.UnsatisfiedLinkError: No implementation found for int com.baidu.platform.comjni.map.commonmemcache.JNICommonMemCache.Create()
- Line 38: No source code is available for type XXX; did you forget to inherit a required module?
- HTTPDOWNLOADER for updater application block for .Net2.0
- Vue 2.0 v-for 响应式key, index及item.id参数对v-bind:key值造成差异研究
- [GraphQL] Create an Input Object Type for Complex Mutations
- Auto-Layout 的各种坑Unable to create description in descriptionForLayoutAttribute_layoutItem_coefficient. Something is nil'
- Learning Data Binding with Windows Forms 2.0(1)-The Quest for Type Safety
- Open quote is expected for attribute "name" associated with an element type "item".
- ComboBoxItem Class for .NET 2.0
- Effective Objective-C 2.0: Item 45: Use dispatch_once for Thread-Safe Single-Time Code Execution
- React.createElement: type is invalid -- expected a string (for built-in components) or a class/funct
- Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value