说起数据的传递方式,我们大概第一想到的就是参数了,而在OC中新增加了两种比较常用的传值方式——单例(静态对象)、通知(NSNotification)与匿名函数(Block)。
在介绍这两种传值方式之前,我们先介绍一下目前常用的一些传值方式:
1、参数传值
2、方法传值
3、属性传值
4、单例传值
5、通知
6、协议
7、匿名函数/代码块/block/闭包
通过参数的方式将值进行传递,常见于各种语言中,一种基础的传值方式。
方法传值
通过方法的返回值来将需要的值传递,与参数传值相同也是一种常见的传值方式。
属性传值
通过对象属性的setter与getter语法进行传值,可在主程序中通过点语法进行调用。
单例传值
什么是单例?
单例是oc中设计模式的一种,它的大体概念是:设置一个全局的、不可销毁的静态对象,通过在内部方法中将其赋值,然后在外部通过读取这个静态对象的值来获得所需要的值的传值方式。
在程序中我们可以通过创建一个静态对象的方法实现。
如:我们需要在下面的对象中创建一个单例。
Book.h
#import "Book.h"
@interface Person : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, retain)Book *book;
@end
Book.m:
@implementation Person
static Person *person = nil;
@end
#import "Book.h"
@interface Person : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, retain)Book *book;
//以default,share,standard,getinstance开头
+ (Person *)standardPerson;
@end
实现文件:
@implementation Person
static Person *person = nil;
+ (Person *)standardPerson{
//保证单例唯一
if (!person) {
person = [[Person alloc] init];
}
return person;
}
@end
这样单例就创建好了,那么怎么通过单例传值呢?
我们在外部可以通过访问单例的方法和属性获取单例的值,如:
在外部类中我们可以直接通过方法访问单例:
@interface Book : NSObject
@property (nonatomic, copy)NSString *name;
//利用单例获取对象
- (void)getPerson;
@end
实现文件中:
#import "Person.h"
@implementation Book
//利用单例获取对象
- (void)getPerson{
//getter方法
NSLog(@"对象:%@, PersonName:%@", [Person standardPerson], [[Person standardPerson] name]);
}
@end
通过这样设置后,我们在main中就可以这样访问数据,即,数据不再通过参数的形式传递。
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//setter方法设值
[[Person standardPerson] setName:@"1"];
//获取Person对象
[[Book alloc] getPerson];
}
return 0;
}
运行后:
对象:, PersonName:1
通知
通知的形式与我们生活中常见的广播类似,先创建一个通知中心,然后通过通知中心向四面八方发送数据,当有接收器接收到通知时做出相应的响应。
在程序中我们先创建一个用接收器的类,通过重写init方法来创建接收器
- (instancetype)init
{
self = [super init];
if (self) {
//notification为自定义selector
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification:) name:@"informationKey" object:nil];
}
return self;
}
//自定义@selector
- (void)notification:(id)notification{
NSLog(@"notification = %@", notification);
//输出接到的通知,object为默认key
NSLog(@"notification value = %@", [notification valueForKey:@"object"]);
}
- (void)dealloc{
NSLog(@"Person dealloc");
[super dealloc];
//不需要时,移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"informationKey" object:nil];
}
在main中创建通知中心,
然后创建对象并通知,最后释放对象和通知(注意:必须先有接收的对象才能发出通知)NSNotificationCenter *notification = [NSNotificationCenter defaultCenter];
Person *person = [[Person alloc] init];
//informationKey为键,value为要传的值
[notification postNotificationName:@"informationKey" object:@"value"];
//运行完后释放值
[person release];
运行后会发现结果:
notification = NSConcreteNotification 0x100204de0 {name = informationKey; object = value}
notification value = value
协议与通知不同,它是在两个或几个对象之间建立数据通路,然后通过通路来传递值的一种方式,是OC语言中较为重要也较为常用的一种传值方式。由于较为复杂,这里就不做详细讲解了,需要了解的看友请看其他文章。
匿名函数(block)
现在OC中数值的传递block运用越来越广泛,那么我们一起来学学block
什么是block?
block其实就是一个对象化封装的代码块,一个引用自动变量的函数,一个闭包。它能当做一个参数进行传递,也能当做方法的返回值,也可以直接运行。(详细请看:点击打开链接)
block的定义与初始化:
在OC中我们定义block与初始化的方式如下:
block返回值 (^block标识符)(block参数列表) = ^(block参数) {...};
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
return number + anotherNumber;
};
调用方式与函数类似
sumBlock(10, 3);
block的优势?
1、block直接运行:直接运行block调用方法与函数类似,如求两个数的和:
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
return number + anotherNumber;
};
NSLog(@"Sum = %ld", sumBlock(10, 3));
运行结果:
Sum = 13
2、block作为参数进行回调:将block作为参数传递入方法中,调用方法为(返回值 (^)(参数列表))block名,如求两个人的年龄和:
@interface Person : NSObject
@property (nonatomic, assign)NSInteger age;
+ (NSInteger)sumOfAgeWithPerson:(Person *)person AnotherPerson:(Person *)anotherPerson SumBlock:(NSInteger (^)(NSInteger age, NSInteger anotherAge))sumBlock;
@end
实现:
+ (NSInteger)sumOfAgeWithPerson:(Person *)person AnotherPerson:(Person *)anotherPerson SumBlock:(NSInteger (^)(NSInteger, NSInteger))sumBlock{
return sumBlock(person.age, anotherPerson.age);
}
调用:
Person *per1 = [[Person alloc] init];
Person *per2 = [[Person alloc] init];
per1.age = 20;
per2.age = 30;
NSInteger sum = [Person sumOfAgeWithPerson:per1 AnotherPerson:per2 SumBlock:^NSInteger(NSInteger age, NSInteger anotherAge) {
return age +anotherAge;
}];
NSLog(@"sum = %ld", sum);
结果:
sum = 50
3、用block对外部数据进行修改:block仍然可以对外部数据进行操作,如修改某一个数:
1)对基本数据类型的访问,不能直接在block中进行修改,因为系统默认为readonly类型
SInteger number1 = 10;
NSInteger anotherNumber1 = 3;
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
number1 = 9;//程序崩溃
return number + anotherNumber;
};
NSLog(@"Sum = %ld", number1);
所以当我们对基本数据进行修改时,需要将基本数据定义为__block类型,如下:
__block NSInteger number1 = 10;
__block NSInteger anotherNumber1 = 3;
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
number1 = 9;
return number + anotherNumber;
};
NSLog(@"Sum = %ld", number1);
这样就得到了运行结果:
Sum = 10
2)对对象的访问:
[1]不可变对象的访问:不可直接访问,需将对象变为__block类型。
NSArray *array = [NSArray arrayWithObjects:@"1", nil];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"1", nil];
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
dic = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"1",@"2", @"2",nil];//程序崩溃
array = [array arrayByAddingObject:@"2"];//程序崩溃
return number + anotherNumber;
};
sumBlock(1, 2);
NSLog(@"Array = %@", array);NSLog(@"Dictionary = %@", dic);
我们将对象加上__block
__block NSArray *array = [NSArray arrayWithObjects:@"1", nil];
__block NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"1", nil];
NSInteger (^sumBlock)(NSInteger, NSInteger) = ^ (NSInteger number, NSInteger anotherNumber){
dic = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"1",@"2", @"2",nil];//程序正常
array = [array arrayByAddingObject:@"2"];//程序正常
return number + anotherNumber;
};
sumBlock(1, 2);NSLog(@"Array = %@", array);NSLog(@"Dictionary = %@", dic);
我们会发现运行结果如下:
Array = (
1,
2
)
Dictionary = {
1 = 1;
2 = 2;
}
说明我们在block中的修改起效。
修改数组中的数:
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1", nil];
void (^block)() = ^ {
[mutableArray addObject:@"2"];
};
block();
NSLog(@"mutableArray = %@", mutableArray);
运行结果如下:
mutableArray = (
1,
2
)
修改成功了。
*重要:利用block修改外部与内部参数,修改两人中年龄较大的人的年龄:
先在类中定义方法和block,都无需返回值。
@interface Person : NSObject
//利用typedef对block进行重命名
typedef void(^CHANGEAGE)(Person *maxPerson);
@property (nonatomic, assign)NSInteger age;
+ (void)changeMaxPersonAge:(CHANGEAGE)block;
@end
实现方法:
+ (void)changeMaxPersonAge:(CHANGEAGE)block{
Person *person = [[Person alloc] init];
block([person autorelease]);
}
- (void)dealloc{
NSLog(@"Person dealloc");
[super dealloc];
}
无需参数直接在main中实现:
Person *per1 = [[Person alloc] init];
Person *per2 = [[Person alloc] init];
per1.age = 20;
per2.age = 30;
//同时省去了参数传递与返回值,直接更改
[Person changeMaxPersonAge:^(Person *maxPerson) {
maxPerson = per1 > per2 ? per1 : per2;
maxPerson.age = 55;
}];
NSLog(@"changAge = %ld", (per1 > per2 ? per1 : per2).age);
[per1 release];
[per2 release];
运行结果如下:
changAge = 55
Person dealloc
Person dealloc
Person dealloc
我们会发现使用block以后不仅没有降低程序的封装,而且由于block可以访问外部参数的性质,使得block可以直接进行参数操作,无需将参数传入和传出。不仅提高代码便捷性,同时可以重复使用降低了代码复杂度。