发表时间:2010-03-09
最后修改:2010-03-10
原文地址:使用NSOperation为你的app加速
在app store中的很多应用程序非常的笨重,他们有好的界面,但操作性很差,比如说当程序从网上或本地载入数据的时候,界面被冻结了,用户只能等程序完全载入数据之后才能进行操作。
当打开一个应用程序时,iphone会产生一个包含main方法的线程,所用程序中的界面都是运行在这个线程之中的(table views, tab bars, alerts…),有时候我们会用数据填充这些view,现在问题是如何有效的载入数据,并且用户还能自如的操作程序。
下面要说方法的并不是要在用户载入数据的时候在界面上提示“loading”的信息,虽然这种方式在有些时候是可以被接受的,但当数据在main线程之外被载入是并不是最有效的方式。
先看一下要演示的程序:
这个程序将从网络上下载10,000条数据,并填入到UITableView中,现面的代码将首先演示一种错误的方式:
错误 (源码
)
@implementation
RootViewController
@synthesize
array
;
-
(
void
)
viewDidLoad {
[
super viewDidLoad]
;
/* Adding the button */
self
.navigationItem.rightBarButtonItem =
[
[
UIBarButtonItem alloc
]
initWithTitle
:
@"Load"
style
:
UIBarButtonItemStyleDone
target
:
self
action
:
@selector
(
loadData)
]
;
/* Initialize our array */
NSMutableArray
*
_array =
[
[
NSMutableArray
alloc
]
initWithCapacity
:
10000
]
;
self
.array
=
_array;
[
_array release
]
;
}
// Fires when the user presses the load button
-
(
void
)
loadData {
/* Grab web data */
NSURL
*
dataURL =
[
NSURL
URLWithString
:
@"http://icodeblog.com/samples/nsoperation/data.plist"
]
;
NSArray
*
tmp_array =
[
NSArray
arrayWithContentsOfURL
:
dataURL]
;
/* Populate our array with the web data */
for
(
NSString
*
str in
tmp_array)
{
[
self
.array
addObject
:
str]
;
}
/* reload the table */
[
self
.tableView
reloadData
]
;
}
#pragma mark Table view methods
-
(
NSInteger)
numberOfSectionsInTableView:
(
UITableView *
)
tableView
{
return
1
;
}
-
(
NSInteger)
tableView
:
(
UITableView *
)
tableView
numberOfRowsInSection:
(
NSInteger)
section {
return
[
self
.array
count
]
;
}
-
(
UITableViewCell *
)
tableView
:
(
UITableView *
)
tableView
cellForRowAtIndexPath:
(
NSIndexPath
*
)
indexPath {
static
NSString
*
CellIdentifier =
@"Cell"
;
UITableViewCell *
cell
=
[
tableView
dequeueReusableCellWithIdentifier:
CellIdentifier]
;
if
(
cell
==
nil
)
{
cell
=
[
[
[
UITableViewCell alloc
]
initWithStyle:
UITableViewCellStyleDefault
reuseIdentifier:
CellIdentifier]
autorelease
]
;
}
/* Display the text of the array */
[
cell
.textLabel setText:
[
self
.array
objectAtIndex
:
indexPath.row
]
]
;
return
cell
;
}
-
(
void
)
dealloc
{
[
super dealloc
]
;
[
array
release
]
;
}
@end
|
当点击“load”按钮时程序会被冻结,直到将数据完全下载并填入Tableview,在这期间用户不能做任何的事情。
在给出解决方式之前先来看一下NSOperationQueue和NSOperation:
The NSOperation and NSOperationQueue classes alleviate much of the pain of multi-threading, allowing you to simply define your tasks, set any dependencies that exist, and fire them off. Each task, or operation
, is represented by an instance of an NSOperation class; the NSOperationQueueclass takes care of starting the operations, ensuring that they are run in the appropriate order, and accounting for any priorities that have been set.
下面要做的是建立NSOperationQueue和NSOperations。NSOperationQueue会建立一个线程,每个加入到线程operation会有序的执行。
下面是使用NSOperationQueue的过程:
- 建立一个NSOperationQueue的对象
- 建立一个NSOperation的对象
- 将operation加入到NSOperationQueue中
- release掉operation
使用NSOperation有几种,现在介绍最简单的一种NSInvocationOperation,NSInvocationOperation是NSOperation的子类,允许运行在operation中的targer和selector。 下面是执行NSInvocationOperation的一个例子:
NSOperationQueue
*
queue =
[
NSOperationQueue
new
]
;
NSInvocationOperation
*
operation
=
[
[
NSInvocationOperation
alloc
]
initWithTarget
:
self
selector
:
@selector
(
methodToCall)
object
:
objectToPassToMethod]
;
[
queue addOperation:
operation
]
;
[
operation
release
]
;
|
下面是我们用正确的方式实现的程序:
正确的方式(下载源码
)
@implementation
RootViewController
@synthesize
array
;
-
(
void
)
viewDidLoad {
[
super viewDidLoad]
;
self
.navigationItem.rightBarButtonItem =
[
[
UIBarButtonItem alloc
]
initWithTitle
:
@"Load"
style
:
UIBarButtonItemStyleDone
target
:
self
action
:
@selector
(
loadData)
]
;
NSMutableArray
*
_array =
[
[
NSMutableArray
alloc
]
initWithCapacity
:
10000
]
;
self
.array
=
_array;
[
_array release
]
;
}
-
(
void
)
loadData {
/* Operation Queue init (autorelease) */
NSOperationQueue
*
queue =
[
NSOperationQueue
new
]
;
/* Create our NSInvocationOperation to call loadDataWithOperation, passing in nil */
NSInvocationOperation
*
operation
=
[
[
NSInvocationOperation
alloc
]
initWithTarget
:
self
selector
:
@selector
(
loadDataWithOperation)
object
:
nil
]
;
/* Add the operation to the queue */
[
queue addOperation:
operation
]
;
[
operation
release
]
;
}
-
(
void
)
loadDataWithOperation {
NSURL
*
dataURL =
[
NSURL
URLWithString
:
@"http://icodeblog.com/samples/nsoperation/data.plist"
]
;
NSArray
*
tmp_array =
[
NSArray
arrayWithContentsOfURL
:
dataURL]
;
for
(
NSString
*
str in
tmp_array)
{
[
self
.array
addObject
:
str]
;
}
[
self
.tableView
reloadData
]
;
}
#pragma mark Table view methods
-
(
NSInteger)
numberOfSectionsInTableView:
(
UITableView *
)
tableView
{
return
1
;
}
-
(
NSInteger)
tableView
:
(
UITableView *
)
tableView
numberOfRowsInSection:
(
NSInteger)
section {
return
[
self
.array
count
]
;
}
-
(
UITableViewCell *
)
tableView
:
(
UITableView *
)
tableView
cellForRowAtIndexPath:
(
NSIndexPath
*
)
indexPath {
static
NSString
*
CellIdentifier =
@"Cell"
;
UITableViewCell *
cell
=
[
tableView
dequeueReusableCellWithIdentifier:
CellIdentifier]
;
if
(
cell
==
nil
)
{
cell
=
[
[
[
UITableViewCell alloc
]
initWithStyle:
UITableViewCellStyleDefault reuseIdentifier:
CellIdentifier]
autorelease
]
;
}
[
cell
.textLabel setText:
[
self
.array
objectAtIndex
:
indexPath.row
]
]
;
return
cell
;
}
-
(
void
)
dealloc
{
[
super dealloc
]
;
[
array
release
]
;
}
|
再次运行程序,当点击“load”按钮时界面是否还被“冻结”呢,程序并没有增加很多的代码,但确大大的提高了用户体验。