Effective Objective-C 2.0: Item 42: Prefer GCD to performSelector and Friends
来源:互联网 发布:gta5捏脸数据日本妹子 编辑:程序博客网 时间:2024/04/30 04:41
Item 42: Prefer GCD to performSelector and Friends
Thanks to the extremely dynamic nature of Objective-C (see Item 11), a few methods defined on NSObject
allow you to call any method you wish. They allow delayed execution of a method call or specification of which thread it should be run on. They were once a very useful feature; now, however, technologies such as Grand Central Dispatch and blocks are making their use less important. Although you will often still see code using them, I encourage you to stay clear of them.
The most basic method in this family is performSelector:
. It takes a single argument, which is the selector to perform, with the following signature:
- (id)performSelector:(SEL)selector
This is equivalent to calling the selector directly. So the following two lines of code are equivalent:
Click here to view code image
[object performSelector:@selector(selectorName)];
[object selectorName];
It might seem as though this is redundant. It would be if this were the only way the method could be used. However, its real power comes from the fact that the selector can be decided at runtime. Such dynamic binding on top of dynamic binding means that you can do something like this:
Click here to view code image
SEL selector;
if ( /* some condition */ ) {
selector = @selector(foo);
} else if ( /* some other condition */ ) {
selector = @selector(bar);
} else {
selector = @selector(baz);
}
[object performSelector:selector];
This code is extremely flexible and can often be used to simplify complex code. Another use is to store a selector that should be performed after an event has happened. In either case, the compiler doesn’t know until runtime which selector is going to be performed. But the cost of this feature is that if you compile this under ARC, the compiler emits the following warning:
Click here to view code image
warning: performSelector may cause a leak because its selector
is unknown [-Warc-performSelector-leaks]
You probably weren’t expecting that! If you were, you probably know why you should be careful with these methods. This message may look strange to you, and you’re wondering why a leak is mentioned. After all, you were simply trying to call a method. The reason is that the compiler doesn’t know what selector is going to be invoked and therefore doesn’t know the method signature, the return type, or even if there is a returned value. Also, the compiler doesn’t know the method name and therefore cannot apply ARC’s memory-management rules to determine whether the return value should be released. For this reason, ARC plays it safe and doesn’t add a release. However, the result might be a memory leak, as the object might be being returned as a retained object.
Consider the following code:
Click here to view code image
SEL selector;
if ( /* some condition */ ) {
selector = @selector(newObject);
} else if ( /* some other condition */ ) {
selector = @selector(copy);
} else {
selector = @selector(someProperty);
}
id ret = [object performSelector:selector];
This is a slight variation on the preceding example to show the problem. In the case of the first two selectors, the ret
object would need to be released by this code; with the third selector, it would not. This is true not only in an ARC world but also in a non-ARC world, which is strictly following the method-naming guidelines. Without ARC (and therefore no compiler warning), theret
object would need to be released if either of the first two conditions were true but not otherwise. This could easily be overlooked, and even a static analyzer would not help detect the subsequent memory leak. This is one reason for treating the performSelector
family of methods with caution.
Another reason these methods are not ideal is that the return type can be only void or an object type. The performSelector
method’s return type is id
, although it’s also valid that the selector being performed returns void
. Although intricate casting can use selectors that return other values, such as integers or floats, it can be fragile. It’s technically possible to return any type that has the same size as a pointer, as the id
type is a pointer to any Objective-C object: on 32-bit architectures, any type that is 32 bits wide; on 64-bit architectures, any type that is 64 bits wide. If the return type is a C struct,
the performSelector
method cannot be used.
A couple of other performSelector
variants that can pass arguments with the message are defined as follows:
Click here to view code image
- (id)performSelector:(SEL)selector
withObject:(id)object
- (id)performSelector:(SEL)selector
withObject:(id)objectA
withObject:(id)objectB
For example, these variants can be used to set a property called value
on an object:
Click here to view code image
id object = /* an object with a property called 'value' */;
id newValue = /* new value for the property */;
[object performSelector:@selector(setValue:)
withObject:newValue];
These methods may seem useful, but they have serious limitations. The objects being passed in must be objects, as the type is always id
. So if the selector takes an integer or a float, these methods cannot be used. In addition, the selector can take a maximum of two parameters, using the method performSelector:withObject:withObject:
. There are no equivalent methods to perform selectors that take more than two parameters.
One of the other features of the performSelector
family of methods is the fact that the selector can be run after a delay or on another thread. Some of the more common of these methods are as follows:
Click here to view code image
- (void)performSelector:(SEL)selector
withObject:(id)argument
afterDelay:(NSTimeInterval)delay
- (void)performSelector:(SEL)selector
onThread:(NSThread*)thread
withObject:(id)argument
waitUntilDone:(BOOL)wait
- (void)performSelectorOnMainThread:(SEL)selector
withObject:(id)argument
waitUntilDone:(BOOL)wait
However, these methods soon become too constraining. For example, there is no method to perform a given selector that takes two arguments after a delay. The threading methods are not very generic, for the same reason. Code that wants to make use of these methods often packs multiple arguments into a dictionary and unpacks in the called method, thereby adding overhead and the potential for bugs.
All these limitations are solved by using one of the alternatives. The main alternative is using blocks (see Item 37). Furthermore, using blocks with Grand Central Dispatch (GCD) enables you to achieve all the threading-related reasons for using one of the performSelector
methods. Performing after a delay can be achieved with dispatch_after,
and performing on another thread can be achieved with dispatch_sync
and dispatch_async
.
For example, to perform a task after a delay, you should prefer the latter to the former:
Click here to view code image
// Using performSelector:withObject:afterDelay:
[self performSelector:@selector(doSomething)
withObject:nil
afterDelay:5.0];
// Using dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
[self doSomething];
});
To perform a task on the main thread:
Click here to view code image
// Using performSelectorOnMainThread:withObject:waitUntilDone:
[self performSelectorOnMainThread:@selector(doSomething)
withObject:nil
waitUntilDone:NO];
// Using dispatch_async
// (or if waitUntilDone is YES, then dispatch_sync)
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
Things to Remember
The performSelector
family of methods is potentially dangerous with respect to memory management. If it has no way of determining what selector is going to be performed, the ARC compiler cannot insert the appropriate memory-management calls.
The family of methods is very limited with respect to the return type and the number of parameters that can be sent to the method.
The methods that allow performing a selector on a different thread are better replaced with certain Grand Central Dispatch (GCD) calls using blocks.
- Effective Objective-C 2.0: Item 42: Prefer GCD to performSelector and Friends
- Effective Objective-C 2.0: Item 4: Prefer Typed Constants to Preprocessor #define
- Effective Objective-C 2.0: Item 41: Prefer Dispatch Queues to Locks for Synchronization
- Effective Objective-C 2.0:Item 48: Prefer Block Enumeration to for Loops
- Effective Objective-C 2.0: Item 41: Prefer Dispatch Queues to Locks for Synchronization
- Effective Objective-C 2.0: Item 43: Know When to Use GCD and When to Use Operation Queues
- <Effective Mordern C++>笔记:Item 8:prefer nullptr to 0 and NULL.
- Effective Objective-C 2.0: Item 3: Prefer Literal Syntax over the Equivalent Methods
- Effective C++(Item1) Prefer const and inline to #define
- Effective C++ Item 2:Prefer constS, enumS, and inlineS to #defineS
- effective C++ Item 2: Prefer consts, enums, and inlines to #defines
- Effective C++读书笔记(3)-Item 2: Prefer consts, enums, and inlines to #defines
- <Effective Mordern C++>笔记:Item 5:Prefer auto to explicit type declarations.
- <Effective Mordern C++>笔记:Item 9:prefer alias declarations to typedefs.
- Effective Objective-C(第41-46条)gcd大中枢派发、performSelector
- Effective C++(Item3) Prefer new and delete to malloc and free
- Effective C#之Item 35:Prefer Overrides to Event Handlers
- Effective C#之Item 41:Prefer DataSets to Custom Structures
- 《重构》读书笔记(1)
- 图像匹配算法研究之sift算法
- 基于MINA框架快速开发网络应用程序
- 黑盒测试和白盒测试区别
- 电脑快捷键命令
- Effective Objective-C 2.0: Item 42: Prefer GCD to performSelector and Friends
- ArcGIS for Android 例子Offline Editor (BETA)(二)
- tomcat 7.x 报错javax.servlet.ServletContext.getSessionCookieConfig
- Opencv学习笔记(四):运用Canny算子边缘检测及Opencv中拖动条事件处理
- 黑马程序员基础加强---类加载器
- 《重构》读书笔记(2)
- A. K-Periodic Array
- 主成成分分析pca算法 原理解析
- 再谈降维算法--PCA算法