第一:(数组)NSArray
//创建一个可变数组
NSMutableArray *anArray = [@[@"菜鸟", @"大神"] mutableCopy];
//添加一个数据
[anArray addObject:@"大牛"];
//打印数组长度
NSLog(@"Array has %ld items", [anArray count]);
//Iterate over array items 打印数组全部数据
for (NSString *person in anArray) {
NSLog(@"Person: %@", person);
}
//Access item with index
NSString *superman = anArray[0];
//Remove Object @"Clark Kent"
[anArray removeObject:@"大神"];
//Remove the first Object 删除菜鸟
[anArray removeObjectAtIndex:0];
for (NSString *person1 in anArray) {
NSLog(@"Person1: %@", person1);
}
第二:字典(NSDictionary)
//Create a dictionary
NSMutableDictionary *person2 = [@{
@"firstname" : @"Clark",
@"lastname" : @"Kent",
@"age" : [NSNumber numberWithInt:35]
} mutableCopy];
//Access values
NSLog(@"Superman's first name is %@", person2[@"firstname"]);
//or
NSLog(@"Superman's first name is %@", [person2 objectForKey:@"firstname"]);
//Find number of items in dicitonary
[person2 count];
// Add an object to a dictionary
[person2 setObject:@"job" forKey:@"teacher"];
//Remove an object to a dictionary
[person2 removeObjectForKey:@"firstname"];
//NSArray访问语法:
NSArray *example = @[ @"hi", @"there", @23, @YES ];
NSLog(@"item at index 0: %@", example[0]);
// //NSDictionary访问语法:
// NSDictionary *example = @{ @"hi" : @"there", @"iOS" : @"people" };
// NSLog(@"hi %@", example[@"hi"]);
第三:枚举类型
苹果的示例:
每个枚举被赋予一个响应的整数值,所以
typedef
enum
{
UIButtonTypeCustom = 0,
UIButtonTypeSystem,
UIButtonTypeDetailDisclosure,
UIButtonTypeInfoLight,
UIButtonTypeInfoDark,
UIButtonTypeContactAdd,
UIButtonTypeRoundedRect,
} UIButtonType;
与下述代码一样
typedef
enum
{
UIButtonTypeCustom = 0,
UIButtonTypeSystem = 1,
UIButtonTypeDetailDisclosure = 2,
UIButtonTypeInfoLight = 3,
UIButtonTypeInfoDark = 4,
UIButtonTypeContactAdd = 5,
UIButtonTypeRoundedRect = 6,
} UIButtonType;
|
不要求明确地定义第一个枚举的值,并且它默认为0
使用枚举类型
UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight];
|
或者创建一个变量传递到方法,像这样
UIButtonType myButtonType = UIButtonTypeCustom;
UIButton *myButton = [UIButton buttonWithType:myButtonType];
|
由于它们不是对象,所以你必须打印枚举类型为整数
UIButtonType myButtonType = UIButtonTypeRoundedRect;
// Bad, will give you a warning and might even crash
NSLog(@
"%@"
, myButtonType);
// Good, will properly print the value as an integer
NSLog(@
"%d"
, myButtonType);
|
第四:委托
委托是一种设计模式。当一个事件发生时,委托允许一个对象把消息传递给其他对象。请查阅苹果官方文档。
成为一个框架类的委托:
第一步,声明类采用了类/父类名字后,方括号里类定义中的协议。
//MyTableViewController.h
@interface MyTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
@end
|
第二步,设置对象作为委托
//MyTableViewController.m
[tableView setDelegate:self];
@end
|
第三步,实现委托方法
为自定义的类实现委托:
第一步,声明协议方法
// Superman.h
#import <Foundation/Foundation.h>
@protocol SupermanDelegate <NSObject>
- (
void
)dodgeBullet;
- (
void
)seeThroughThings;
- (
void
)fly;
@optional
- (
void
)eat;
@end
@interface Superman : NSObject
// Create a property for the delegate reference
@property (nonatomic, weak) id <SupermanDelegate> delegate;
// Define other methods and properties
@end
|
第二步,设置委托对象
// Superman.m
[self setDelegate:anObject];
|
第三步,开始发送委托消息
// Superman.m
[self.delegate fly];
|
第四常用:Blocks
Blocks是添加到C,Objective-C和C++中一个语言级别的特性,它允许你创建不同的代码段,这些代码段就像值一样可以在方法和函数间传递。更多信息请查阅Programming with Objective-C – Working with Blocks
声明block局部变量:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
|
声明block属性:
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
|
接收block作为一个方法参数:
- (
void
)aMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
|
在方法调用中将block作为一个参数传递
[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
|
定义一个block类型:
typedef
returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
|
第五常用:
NSNotificationCenter(通知中心)
通知是广播消息,用于在运行时分离类间耦合并在对象之间建立匿名通信。通知可以由任何数量的对象发布和接受,因此在对象间可以建立一对多和多对多的关系。
注:通知是同步发送的,所以如果你的观察方法需要很长时间才能返回,你实际上是阻止了给其他观察对象传递通知。
注册观察者
为了获得某一事件发生的通知,你可以先注册。事件包括系统通知,例如UITextField开始编辑。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidBeginEditing:)
name:UITextFieldTextDidBeginEditingNotification object:self];
|
当OS系统框架广播UITextFieldTextDidBeginEditingNotification通知时,NSNotificationCenter将调用textFieldDidBeginEditing:并且对象与包含数据的通知一起发送。
textFieldDidBeginEditing:方法的一种可能实现是:
#pragma mark - Text Field Observers
- ( void )textFieldDidBeginEditing:(NSNotification *)notification
{
// Optional check to make sure the method was called from the notification
if ([notification.name isEqualToString:UITextFieldTextDidBeginEditingNotification])
{
// Do something
}
}
|
删除观察者
释放类的同时删除观察者是很重要的,否则NSNotificationCenter将试图在已经释放掉的类中调用方法,这将会引起崩溃。
- ( void )dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
|
发布通知
你也可以创建和发送自己的通知。最好将通知名称保存在一个常量文件中,这样你就不会因为一不小心拼错了通知名称而坐在那里试图找出为什么不能发送或者接收通知。
通知的命名
通知是由NSString对象的识别的,通知的名称是有以下方式组成的:
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
声明一个字符串常量,将通知的名称作为字符串的值:
// Remember to put the extern of this in the header file
NSString * const kRPAppDidResumeFromBackgroundNotification = @ "RPAppDidResumeFromBackgroundNotification" ;
|
发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:kRPAppDidResumeFromBackgroundNotification object:self];
|
视图控制器属性
当你准备显示一个新的视图控制器时,你可以在展示之前为属性分配数据:
MyViewController *myVC = [[MyViewController alloc] init];
myVC.someProperty = self.someProperty;
[self presentViewController:myVC animated:YES completion:nil];
|
Storyboard Segue
当你在storyboard中在两个视图控制器之间切换时,使用prepareForSegue:sender:方法可以简单的实现数据的传递,如下:
#pragma mark - Segue Handler
- ( void )prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@ "showLocationSearch" ] {
[segue.destinationViewController setDelegate:self];
} else if ([segue.identifier isEqualToString:@ "showDetailView" ]) {
DetailViewController *detailView = segue.destinationViewController;
detailView.location = self.location;
}
}
|
用户默认值
用户默认值是存储简单偏好的基本方法,在应用程序启动时可以保存和还原这些偏好值。这并不意味着它被用作像Core Data或者sqlite那样的数据存储层。
存储值
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setValue:@ "Some value" forKey:@ "RPSomeUserPreference" ];
[userDefaults synchronize];
|
记住一定要在默认实例上调用synchronize以确保正确保存值。
检索值
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
id someValue = [userDefaults valueForKey:@ "RPSomeUserPreference" ];
|
NSUserDefaults实例中也有其他简单的方法,例如boolForKey:, stringForKey:等
常见模式
单例模式
单例模式是一种特殊的类,在当前进程中只能一个类包含一个实例。单例模式对于在一个应用程序的不同部分中共享数据很便利,并且不需要创建全局变量或者手动传递数据。但是,因为它们经常创建类之间的紧耦合,所以尽量少使用单例模式。
将一个类转换成单例模式,你需要将下面的方法写到实现文件(.m)中,方法名由shared加上一个单词组成,这样可以很好的描述你的类。例如,如果这个类是一个网络或位置管理器,你可以将这个方法命名为sharedManager,而不是sharedInstance。
+ (instancetype)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
|
使用dispatch_once可以保证这个方法只被执行一次,即使它在很多类或者进程之间调用很多次。
如果在MyClass中替换上面的代码,那么用下面的代码你将会在另一个类中得到一个单例模式类的引用:
MyClass *myClass = [MyClass sharedInstance];
[myClass doSomething];
NSLog(@ "Property value is %@" , myClass.someProperty);
|
3.3 NSTimer
NSTimers在apps中经常用来执行周期性任务。NSTimer虽然很有用但是也会产生问题。当有几个定时器在用的时候,他们可能间断性的触发。这就是意味着CPU是间断性处于活动状态的。这样做是更加有效率的,当CPU换起的时候执行一些任务,然后进入睡眠状态。为了解决这个问题,Apple给NSTimer添加了一个容忍属性来适应这种行为。
容忍提供系统一个指导在timer在计划之后允许延迟多长时间。为了减少CPU负荷底层系统将要集合这些活动。新属性的方法是:
– (NSTimeInterval)tolerance;
– (void)setTolerance:(NSTimeInterval)tolerance;
你可能永远都不需要用到这个属性,但是当你在非常密切相近的触发了几个定时器,你可能发现他是有用的,当你在用Instruments检测CPU使用率的时候。
3.3 NSProgress
不经常见到Foundation会完整的添加一个新类。他是一个稳定的框架。主要是因为不经常用到核心的类。然而iOS7提供了一个完整的新类NSProgress。
本质上,NSProgress是用来通过Objective-C代码产生进度报告的,分离每一个独立模块的进度。例如,你可以在一些数据上执行几个不同的任务,然后每个任务可以管理他自己的进度然后报告给他的父任务。
3.3.1NSProgress结构
NSProgress最简单的使用方法是报告一些任务集合的进度。例如,你有10个任务执行,当每个任务完成的时候你可以报告进度。当有一个任务完成的时候进度增加%10。然后在NSProgress的实例上使用Key Value Observing(KVO),你能够了解到这个实例的进度。你可以使用这个通知来更新进度条或者显示一个指示文字。
NSProgress有更多的用途。Apple通过这个父子类的关系结构使他更加强大。NSProgress的结构更像是网状树。每一个NSProgress有一个父类和多个子类。每一个实例有一个执行的工作单元的总数,当前任务会处理完成的子任务数的更新来反馈当前状态。这么做的话,父类也会被通知进度。
为了减少NSProgress实例的传递,每个线程有自己的NSProgress实例然后子实例可以直接从这个实例创建。没有这个功能,每个想要报告进度的任务不得不通过参数的方式来通知。
3.3.2报告进度
NSProgress使用非常简单。以下面的方法开始:
+(NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount;
这个方法创建了一个NSProgress实例作为当前实例的子类,以要执行的任务单元总数来初始化。例如,如果任务是循环一个数组,然后你可能用数组数来初始化NSProgress实例。例如:
NSArray*array = /* ... */ ;
NSProgress*progress =
[NSProgressprogressWithTotalUnitCount:array.count];
[arrayenumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
// Perform an expensive operation onobj
progress.completedUnitCount = idx;
}];
|
随着迭代的进行,上面的代码会更新NSProgress实例来反映当前进度。
3.3.3接收进度更新
你可以通过下面的属性在任何时候获取任务进度:
@property(readonly) double fractionCompleted;
返回值是0到1,显示了任务的整体进度。当没有子实例的话,fractionCompleted就是简单的完成任务数除以总得任务数。
Key Value Observing(KVO)是最好的方法来获取fractionCompleted值得变化。这么做非常简单。你只需要做的是添加一个NSProgress的fractionCompleted属性的观察者。像下面这样:
[_progress addObserver:self
forKeyPath:@ "fractionCompleted"
options:NSKeyValueObservingOptionNew
context:NULL];
|
然后覆盖KVO的这个方法来获取改变:
-( void )observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:( void *)context
{
if (object == _progress) {
// Handle new fractionCompleted value
return ;
}
// Always call super, incase it uses KVOalso
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
|
在这个方法中你可以获取fractionCompleted的值的改变。例如你可能改变进度条或者提示文字。
当然,当你处理完的时候记得注销KVO是很重要的。
[_progress removeObserver:self
forKeyPath:@ "fractionCompleted"
context:NULL];
|
你必须总是要注销的,如果你没有注销,当被注册的Object释放的时候就会Crash。所以如果必要的话在dealloc中注销作为最后的保障。