- 浏览: 253185 次
- 性别:
- 来自: 深圳
最新评论
-
whizkid:
[img] private void enableNdefEx ...
android通过NFC读写数据 -
zhangminglife:
您好!不错,最近正在弄这个东西,能否把demo发给我一份谢谢了 ...
SSL双向认证java实现(转) -
water卡:
android如何调用显示和隐藏系统默认的输入法 -
water卡:
android如何调用显示和隐藏系统默认的输入法 -
sjp524617477:
good
生成android使用的BKS证书
以下是开发初期收集整理的一点资料,简单实用,希望对新人有帮助,都是网络上收集的,原始出处以不明,若侵犯您的权益,请告知,本人将及时删除相关内容。
多线程之NSInvocationOperation
多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。
本次介绍NSOperation的子集,简易方法的NSInvocationOperation:
@implementation MyCustomClass
- (void)launchTaskWithData:(id)data
{
//创建一个NSInvocationOperation对象,并初始化到方法
//在这里,selector参数后的值是你想在另外一个线程中运行的方法(函数,Method)
//在这里,object后的值是想传递给前面方法的数据
NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(myTaskMethod:) object:data];
// 下面将我们建立的操作“Operation”加入到本地程序的共享队列中(加入后方法就会立刻被执行)
// 更多的时候是由我们自己建立“操作”队列
[[MyAppDelegate sharedOperationQueue] addOperation:theOp];
}
// 这个是真正运行在另外一个线程的“方法”
- (void)myTaskMethod:(id)data
{
// Perform the task.
}
@end一个NSOperationQueue 操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等。下面是建立并初始化一个操作队列:
@interface MyViewController : UIViewController {
NSOperationQueue *operationQueue;
//在头文件中声明该队列
}
@end
@implementation MyViewController
- (id)init
{
self = [super init];
if (self) {
operationQueue = [[NSOperationQueue alloc] init]; //初始化操作队列
[operationQueue setMaxConcurrentOperationCount:1];
//在这里限定了该队列只同时运行一个线程
//这个队列已经可以使用了
}
return self;
}
- (void)dealloc
{
[operationQueue release];
//正如Alan经常说的,我们是程序的好公民,需要释放内存!
[super dealloc];
}
@end简单介绍之后,其实可以发现这种方法是非常简单的。很多的时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。
///////////////////////////////////////////////////////////////////////////////////////////////////
1 在主线程里加入一个loading画面……
2 {
3 [window addSubview:view_loading];
4 [NSThread detachNewThreadSelector:@selector(init_backup:) toTarget:self withObject:nil];
5 }
可以通过performSelectorOhMainThread更新UI元素,比如设置进度条等等。最后消除loading画面,载入主View。
7 - (void)init_backup:(id)sender
8 {
9 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
10
11 // ...
12 int i = status;
13 [self performSelectorOnMainThread:@selector(show_loading:) withObject:[NSNumber numberWithInt:i] waitUntil Done:NO];
14
15 [view_loading removeFromSuperview];
16 [window addSubview:tabcontroller_main.view];
17 [pool release];
18 }
///////////////////////////////////////////////////////
利用iphone的多线程实现和线程同步
从接口的定义中可以知道,NSThread和大多数iphone的接口对象一样,有两种方式可以初始化:
一种使用initWithTargetid)target selector:(SEL)selector object:(id)argument,但需要负责在对象的retain count为0时调用对象的release方法清理对象。
另一种则使用所谓的convenient method,这个方便接口就是detachNewThreadSelector,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
然后在实现中添加如下代码:
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
// 锁对象
ticketCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
// 上锁
[ticketsCondition lock];
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else
{
break;
}
[ticketsCondition unlock];
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[ticketsCondition release];
[window release];
[super dealloc];
}
@end
-------------------------------------------------------------------------------------
// 定义
#import <UIKit/UIKit.h>
@interface ThreadSyncSampleViewController : UIViewController {
int _threadCount;
NSCondition *_myCondition;
}
@end
//实现文件如下:
#import "ThreadSyncSampleViewController.h"
@implementation ThreadSyncSampleViewController
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//
//_myCondition = nil;
//
_myCondition = [[NSCondition alloc] init];
//
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(threadTester)
userInfo:nil
repeats:YES];
[timer fire];
}
- (void)threadTester{
[_myCondition lock];
_threadCount = -2;
//如果有n个要等待的thread,这里置成 -n
[_myCondition unlock];
//
NSLog(@"");
NSLog(@"------------------------------------------------------------------------------");
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadThree) toTarget:self withObject:nil];
return;
}
- (void)threadOne{
NSLog(@"@@@ In thread 111111 start.");
[_myCondition lock];
int n = rand()%5 + 1;
NSLog(@"@@@ Thread 111111 Will sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
sleep(n);
//[NSThread sleepForTimeInterval:n];
_threadCount ++ ;
NSLog(@"@@@ Thread 111111 has sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
[_myCondition signal];
NSLog(@"@@@ Thread 1111111 has signaled ,now _threadCount is : %d",_threadCount);
[_myCondition unlock];
NSLog(@"@@@ In thread one complete.");
[NSThread exit];
return;
}
- (void)threadTwo{
NSLog(@"### In thread 2222222 start.");
[_myCondition lock];
int n = rand()%5 + 1;
NSLog(@"### Thread 2222222 Will sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
sleep(n);
// [NSThread sleepForTimeInterval:n];
_threadCount ++ ;
NSLog(@"### Thread 2222222 has sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
[_myCondition signal];
NSLog(@"### Thread 2222222 has signaled ,now _threadCount is : %d",_threadCount);
[_myCondition unlock];
//_threadCount ++ ;
NSLog(@"### In thread 2222222 complete.");
[NSThread exit];
return;
}
- (void)threadThree{
NSLog(@"<<< In thread 333333 start.");
[_myCondition lock];
while (_threadCount < 0) {
[_myCondition wait];
}
NSLog(@"<<< In thread 333333 ,_threadCount now is %d ,will start work.",_threadCount);
[_myCondition unlock];
NSLog(@"<<< In thread 333333 complete.");
[NSThread exit];
return;
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[_myCondition release];
[super dealloc];
}
@end
http://www.devdiv.com/?fromuid=39833
多线程之NSInvocationOperation
多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。
本次介绍NSOperation的子集,简易方法的NSInvocationOperation:
@implementation MyCustomClass
- (void)launchTaskWithData:(id)data
{
//创建一个NSInvocationOperation对象,并初始化到方法
//在这里,selector参数后的值是你想在另外一个线程中运行的方法(函数,Method)
//在这里,object后的值是想传递给前面方法的数据
NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(myTaskMethod:) object:data];
// 下面将我们建立的操作“Operation”加入到本地程序的共享队列中(加入后方法就会立刻被执行)
// 更多的时候是由我们自己建立“操作”队列
[[MyAppDelegate sharedOperationQueue] addOperation:theOp];
}
// 这个是真正运行在另外一个线程的“方法”
- (void)myTaskMethod:(id)data
{
// Perform the task.
}
@end一个NSOperationQueue 操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等。下面是建立并初始化一个操作队列:
@interface MyViewController : UIViewController {
NSOperationQueue *operationQueue;
//在头文件中声明该队列
}
@end
@implementation MyViewController
- (id)init
{
self = [super init];
if (self) {
operationQueue = [[NSOperationQueue alloc] init]; //初始化操作队列
[operationQueue setMaxConcurrentOperationCount:1];
//在这里限定了该队列只同时运行一个线程
//这个队列已经可以使用了
}
return self;
}
- (void)dealloc
{
[operationQueue release];
//正如Alan经常说的,我们是程序的好公民,需要释放内存!
[super dealloc];
}
@end简单介绍之后,其实可以发现这种方法是非常简单的。很多的时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。
///////////////////////////////////////////////////////////////////////////////////////////////////
1 在主线程里加入一个loading画面……
2 {
3 [window addSubview:view_loading];
4 [NSThread detachNewThreadSelector:@selector(init_backup:) toTarget:self withObject:nil];
5 }
可以通过performSelectorOhMainThread更新UI元素,比如设置进度条等等。最后消除loading画面,载入主View。
7 - (void)init_backup:(id)sender
8 {
9 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
10
11 // ...
12 int i = status;
13 [self performSelectorOnMainThread:@selector(show_loading:) withObject:[NSNumber numberWithInt:i] waitUntil Done:NO];
14
15 [view_loading removeFromSuperview];
16 [window addSubview:tabcontroller_main.view];
17 [pool release];
18 }
///////////////////////////////////////////////////////
利用iphone的多线程实现和线程同步
从接口的定义中可以知道,NSThread和大多数iphone的接口对象一样,有两种方式可以初始化:
一种使用initWithTargetid)target selector:(SEL)selector object:(id)argument,但需要负责在对象的retain count为0时调用对象的release方法清理对象。
另一种则使用所谓的convenient method,这个方便接口就是detachNewThreadSelector,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
然后在实现中添加如下代码:
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
// 锁对象
ticketCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
// 上锁
[ticketsCondition lock];
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else
{
break;
}
[ticketsCondition unlock];
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[ticketsCondition release];
[window release];
[super dealloc];
}
@end
-------------------------------------------------------------------------------------
// 定义
#import <UIKit/UIKit.h>
@interface ThreadSyncSampleViewController : UIViewController {
int _threadCount;
NSCondition *_myCondition;
}
@end
//实现文件如下:
#import "ThreadSyncSampleViewController.h"
@implementation ThreadSyncSampleViewController
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//
//_myCondition = nil;
//
_myCondition = [[NSCondition alloc] init];
//
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(threadTester)
userInfo:nil
repeats:YES];
[timer fire];
}
- (void)threadTester{
[_myCondition lock];
_threadCount = -2;
//如果有n个要等待的thread,这里置成 -n
[_myCondition unlock];
//
NSLog(@"");
NSLog(@"------------------------------------------------------------------------------");
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadThree) toTarget:self withObject:nil];
return;
}
- (void)threadOne{
NSLog(@"@@@ In thread 111111 start.");
[_myCondition lock];
int n = rand()%5 + 1;
NSLog(@"@@@ Thread 111111 Will sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
sleep(n);
//[NSThread sleepForTimeInterval:n];
_threadCount ++ ;
NSLog(@"@@@ Thread 111111 has sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
[_myCondition signal];
NSLog(@"@@@ Thread 1111111 has signaled ,now _threadCount is : %d",_threadCount);
[_myCondition unlock];
NSLog(@"@@@ In thread one complete.");
[NSThread exit];
return;
}
- (void)threadTwo{
NSLog(@"### In thread 2222222 start.");
[_myCondition lock];
int n = rand()%5 + 1;
NSLog(@"### Thread 2222222 Will sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
sleep(n);
// [NSThread sleepForTimeInterval:n];
_threadCount ++ ;
NSLog(@"### Thread 2222222 has sleep %d seconds ,now _threadCount is : %d",n,_threadCount);
[_myCondition signal];
NSLog(@"### Thread 2222222 has signaled ,now _threadCount is : %d",_threadCount);
[_myCondition unlock];
//_threadCount ++ ;
NSLog(@"### In thread 2222222 complete.");
[NSThread exit];
return;
}
- (void)threadThree{
NSLog(@"<<< In thread 333333 start.");
[_myCondition lock];
while (_threadCount < 0) {
[_myCondition wait];
}
NSLog(@"<<< In thread 333333 ,_threadCount now is %d ,will start work.",_threadCount);
[_myCondition unlock];
NSLog(@"<<< In thread 333333 complete.");
[NSThread exit];
return;
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[_myCondition release];
[super dealloc];
}
@end
http://www.devdiv.com/?fromuid=39833
发表评论
-
iPhone开发关于UDID和UUID的一些理解
2013-03-20 15:39 1100一.UDID(Unique Device Identifi ... -
卸载xcode4.2
2012-11-20 22:59 902卸载xcode4.2 Terminal: sudo < ... -
XCode 4.2支持iOS 5.1.1真机调试的实现 .
2012-11-20 22:25 1449目前市面上的ios设备,操作系统都是5.1.1的版本了。但是偶 ... -
Objective-C实现信用卡校验
2012-07-09 11:00 966Objective-C实现信用卡校验(Luhn algorit ... -
object-c:property,assign,copy,retain,release区别
2012-06-01 17:03 1053例子: @property(nonatomic,retain) ... -
mac终端命令大全
2012-05-25 17:17 1483可以在终端里用 rm -rf ... -
xcode 编译静态库
2012-05-25 16:53 1375这里以libcurl 为例: 1.首先需要下载 ... -
IOS中设置使用GDataXML解析类库
2012-05-24 18:01 1986如何在项目中设置使用GDataXML解析类库 1. 从http ... -
ios框架
2012-05-21 16:02 10951.ASIHTTPRequest 大名鼎鼎的asi很多人应该 ... -
UISearchBar and UITableView搜索例子
2011-09-20 23:36 1755UISearchBar and UITableView是我们很 ... -
iphone里读取gb2312(中文)编码文件或者二进制流。
2011-09-06 17:37 1512说到文字编码,与文件读写打过交道的软件开发人员应该都知道,比如 ... -
IPhone短信相关部分研究(转载)
2011-09-06 10:06 1468短信发送和截获 方式一: 打 开/dev/tty.debu ... -
iOS4.0程序内发短信
2011-09-06 10:05 1266OS4.0新加入了MFMessageComposeViewCo ... -
Xcode4 SVN配置
2011-09-01 17:17 1049Xcode SVN配置 编辑 ~/.subversion/co ... -
stretchableImageWithLeftCapWidth:topCapHeight:函数用法 总结
2011-08-09 16:25 2121stretchableImageWithLeftCapWidt ... -
iPhone开发中的问题整理(一)
2011-08-08 23:16 1039看到很刚开始开发iPhone软件的朋友问很多问题,其实同样的问 ... -
iPhone多线程编程初体验
2011-08-08 22:24 1034iPhone多线程编程初体验 2011-06-07 17:35 ... -
多线程之NSInvocationOperation
2011-08-08 22:09 626多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始 ... -
iPhone上的JSON
2011-08-08 22:04 884JSON我就不多解释了,需要更多信息的朋友请到json.org ... -
循环使用整个NSArray内的对象
2011-08-08 22:04 914循环使用整个NSArray内的对象是非常常用的了,而且最近我在 ...
相关推荐
iPhone 多线程 CoreData使用 源代码 如何创建多线程,以及如何在多线程中使用CoreData
iphone开发之多线程NSThread和NSInvocationOperation 的 使用
iphone多线程开发指导文档。讲述线程的使用,多线程的同步,线程开发的注意事项等等。
多线程下载器的小例子,希望对大家有用,有需要改进的地方请指出
iPhone 多线程 多线程在各种编程语言中都是难点,很多语言中实现起来很麻烦,objective-c虽然源于c,但其多线程编程却相当简单,可以与java相媲美。这篇文章主要从线程创建与启动、线程的同步与锁、线程的交互、...
支持多线程下载 对下载下来的MP3进行歌曲列表分类 把歌曲分类列表保存SQLIST数据库里 在分类列表里进行MP3播放
一个很不错的IOS多线程的例子,代码简单明了!
在iphone下多线程的开发,能够让你熟悉在iphne下如何开发多个线程之间的相互通信。
线程是单个应用中可以并发执行多个代码路径的多种技术之一。虽然更新的技术如操作对象(Operation objects)和Grand Central Dispatch(GCD),提供一个更加现代化和高效率的基础设施来实现多核并发,但是Mac OS X和...
UITableView,ToolBar,Navigation 页面设计。。。
这是iPhone开发的多线程入门示例程序,具体参考: http://blog.csdn.net/htttw/article/details/7881015
多线程 iphone开发 object-c ios
iphone开发中,一个利用多线程(序列)下载图片的例子。
非常详细、经典的ios平台多线程知识详细的文档(其它平台的开发者也可以参考)。 两个中英文的高质量的pdf文件,也可做为英文学习用。
如题所示: ASIHTTP+JSON+Google地图,多线程实现加载DEMO, iphone 平台下经典代码
不管使用任何编程语言,在实现多线程时都是一件很麻烦的事情。更糟糕的是,一旦出错,这种错误通常相当糟糕。然而,幸运的是apple从os x10.5在这方面做了很多的改进,NSThread的引入,使得开发多线程应用程序容易多...
帮助学习关于OC中内存管理的知识点
比较详细的讲解了ios下多线程开发的技术,很实用。
在本文中,您将学习如何组织和调试SWIFT代码、使用Grand Central Dispatp进行多线程编程、在视图控制器之间传递数据以及为多种语言设计应用程序的技巧。这是最畅销的iPhone开发入门版SWIFT的后续工作。您还将看到...