欢迎你们关注个人公众号,我会按期分享一些我在项目中遇到问题的解决办法和一些iOS实用的技巧,现阶段主要是整理出一些基础的知识记录下来
编程
文章也会同步更新到个人博客:
ppsheep.com安全
本篇文章,主要是对OC中的一些原理的讲解,可能会有一些枯燥,可是真正当你理解时,会有一种豁然开朗的感受。这里会涉及到 对象,属性,消息以及运行期的一些介绍,只有咱们真正理解了这些原理以后,咱们的开发水平才会进一步提高,而不是止步于view的简单编写,view的编写想要写得好,也须要了解这一些原理。框架
在开始讲属性以前,咱们先来理解一下几个概念:函数
上面三个概念,在OC编程中尤为重要,虽然如今可能你没有很深入的理解,可是随着学习深刻,你确定可以体会到。性能
"属性(property)"相信你们都很熟悉,是OC中用来存储对象的数据的实例变量。实例变量通常是经过"存取方法"来访问,"设置方法"来设置实例变量。在以前咱们已经讲过,对于实例变量,若是是自己访问,那么读取最好是直接读取(采用下划线方式直接访问),而设置最好使用属性来设置。具体的,能够参见上一篇(iOS-有效编写高质量Objective-C方法-二)。学习
对于一些简单的概念性的东西我就不讲了,给出结论就行:atom
接下来,咱们来讲几个关键字:spa
@synthesize线程
咱们能够在代码中经过这个关键字来指定咱们想要的实例变量代理
例如:在头文件中
@interface : NSObject
@property(nonatomic, copy) NSString *name;
@end复制代码
这个属性,在咱们运行期环境下,生成的实例变量为_name,可是咱们在.m中并不想使用这个名称,那么咱们在实现文件里就能够这样写:
@implementation
@synthesize name = _myName
@end复制代码
那么咱们在.m实现文件中,均可以直接使用_myName来操做属性name
不过为了书写的规范,和团队之间协做,我仍是建议按照规范的OC代码风格来编写代码,团队成员之间,一看就可以看清楚代码
@dynamic
这个关键字 是用来阻止编译器自动合成存取方法,不过这个关键字我都用的不多,上面的关键字一样的,也使用较少。
这个关键字的意思是:阻止编译器合成属性所对应的实例变量,也不要合成实例变量的存取方法
这里讲一下实例变量就是带下划线的_name而属性是经过property声明的name
这两个须要区分开来
所谓的属性特质,就是指咱们在申明属性的时候,property括号中跟的一些关键字
@property(nonatomic, readwrite, copy);复制代码
其中的nonatomic,readwrite,copy这些都是属性特质,咱们先来讲nonatomic
这个关键字叫作属性的原子性,通俗来讲,这个关键字主要是来控制属性的同步锁。
同步锁:不一样的线程在读取属性的时候,若是属性是经过atomic来声明的,那么这两个线程老是可以读到属性的有效值(注意这里是有效的属性值,并无说是正确的属性值),若是属性是经过nonatomic声明的,那么不一样的线程读取属性值时,若是有线程正在修改该属性的值,另外的线程正在读取属性值,那么就可能将还未修改完成的属性值读取出来(这里是还没有修改完成的属性值,有可能读出一个彻底没有任何意义的属性值)。
那么又要来讲一说,为何咱们老是看到在编写iOS程序时,属性老是使用nonatomic来声明的呢,这是由于在iOS中使用同步锁的开销太大,这会带来性能问题。在通常状况下,咱们并不要求属性必须具备原子性,由于这个原子性并非说就是"线程安全了",若是咱们须要实现线程安全,那么还须要使用更为底层的同步锁定机制才行,即使是使用atomic来声明,不一样的线程仍是可能读取到不一样的属性值,只是说这个属性值是有效的,有意义的。
因此咱们在开发iOS程序时,仍是使用nonaomic来声明,可是在macOS程序开发中,却不会遇到这种性能瓶颈,性能配置不同嘛
这个属性特质,咱们根据字面意思就能看出来,就是声明属性权限的,这个也没什么好说的了。
这个多是咱们平时用的最多的,也是思考最多的,其实平时咱们怎么用,都是知道的,可是为何这么用呢?
assign:"设置方法"只会针对"纯量类型"进行赋值,例如CGFloat、NSInteger这种
strong:此特质象征了一种拥有关系,在"设置方法"中,这种属性是先保留新值,而且释放旧值,而后将新值设置上去
copy:这种方法和strong类型有点类似,可是它并非保留新值,而是直接就想新值拷贝,当属性为NSString类型时,咱们常用这种,那么为何咱们在NSString常用拷贝呢,觉得咱们在设置时,可能会传进来一个NSMutableString对象,这个对象是NSString的子类,是能够赋值给NSString的,若是咱们不使用拷贝,那么当外部改变NSMultableString值时,咱们的属性值也会直接被修改掉,因此这时,咱们就须要拷贝一份不可变的
weak:这个是一种弱引用,为这种方法设置时,既不会保留新值,也不会释放旧值,当属性所指的对象销毁时,属性值也会被清空,在ViewController中定义view时咱们常常会使用到weak,可是咱们常常仍是将view声明为strong,固然这使用起来不会有很大的影响,可是咱们的应用在运行过程当中,就会产生不少的没用view属性值没有被释放掉,占用无效内存。因此建议你们在使用view时,仍是声明为weak
@property(nonatomic,weak) UILable *lable;
//初始化lable时
UILable * lable = [[UILable alloc] init];
[self.view addSubview: lable];
self.lable = lable;复制代码
在咱们定义的属性为Boolean值时,咱们的习惯是获取方法,通常是"is"
开头,那么咱们就能够在声明时,这样书写
@property(nonatomic,getter=isOn) Bool on;复制代码
属性的获取方法,就成了isOn;
咱们在属性中定义了属性特质,那么咱们在书写赋值时,就应该严格按照属性特质赋值,例如,咱们有一个初始方法,须要对咱们的属性NSString name赋值
- (instancetype)initWithName:(NSString *)name{
if(self = [super init]){
//此处就应该使用copy来对name赋值
_name = [name copy];
}
}复制代码
类族是一种隐藏抽象基类背后实现细节的颇有用的模式。。那么什么叫作类族呢?
举个例子:
在UIKit中有一个名叫UIButton的类,若是想要建立按钮,咱们能够调用一个类方法
+ (UIButton *)buttonWithType:(UIButtonType)type;复制代码
该方法返回的对象,取决于传入按钮的类型,然而,无论传入的是什么类型,返回的类都是继承自同一个基类:UIButton。 这样,全部继承自UIButton的类组成了一个类族。
在系统框架中,使用到了不少的类族。。
那么为何要这样作呢?
UIButton这个例子,在实现时,使用者无需关心建立出来的按钮是属于哪个类,也不用考虑按钮的绘制细节,我只须要知道,我怎么建立按钮,如何设置标题。如何增长点击操做等。
咱们如今来假设一个处理雇员的类,每一个雇员都拥有本身的名字和薪水这两个属性,管理者能够命令器执行平常工做。可是,各类雇员的工做内容却不一样,经理在带领雇员作项目的时候,无需关心每一个人怎样完成本身的工做,只须要指示其开工便可。
首先咱们定义一个基类:
typedef NS_ENUM(NSUinteger, PPSEmployeeType){
PPSEmployeeTypeDeveloper,
PPSEmployeeTypeDesigner,
PPSEmployeeTypeFinance,
}
@interface PPSEmployee : NSObject
@property (nonatomic, copy) NSStirng *name;
@property (nonamotic, assign) NSUInteger salary;
//建立方法
+ (PPSEmployee *)employeeWithType:(PPSEmployeeType) type;
//指示开工
- (void)doADaysWork;
@end复制代码
@implementation PPSEmployee
+ (PPSEmployee *)employeeWithType:(PPSEmployeeType) type{
switch (type){
case PPSEmployeeTypeDeveloper:
return [PPSEmployeeDeveloper new];
break;
case PPSEmployeeTypeDesigner:
return [PPSEmployeeDesigner new];
break;
case PPSEmployeeTypeFinance:
return [PPSEmployeeFinance new];
break;
}
}
- (void)doADaysWork{
// 子类覆写
}
@end复制代码
每一个实体子类都是从基类继承而来
@interface PPSEmployeeDeveloper : PPSEmployee
@end复制代码
@implementation PPSEmployeeDeveloper
- (void)doADaysWork{
[self coding];
}
@end复制代码
咱们上面实现的方式,是根据雇员的类别,建立雇员类的实例,这实际上是一种工厂模式。在Java中,咱们知道这种方式通常是经过抽象类来实现,可是OC中没有抽象类这一说,因而开发者一般会在文档中写明使用方法。
在Cocoa中 有不少的类族 大部分的集合类型都是类族 咱们在一个对象是不是属于某一个类时,若是咱们采用下面的方式,每每得不到咱们想要的效果:
id myArr = @[@"a",@"b"];
if([myArr class] == [NSArray class]){
//这里永远不会跑到 由于 咱们知道这里[myArr class]返回的永远是NSArr的一个子类,NSArray只是一个基类
}复制代码
固然,咱们要作这个判断的时候,应该都知道是使用isKindOfClass 这个方法,这个方法实际上是用来判断是不是同一类族,而不是某个类
有时候咱们须要在对象中存放相关的信息,这时候咱们能想到的方法就是,建立一个子类,而后咱们使用的时候 直接使用子类。 可是并非全部状况都可以这样作,有时候类的实例多是因为某种机制所创建的,咱们开发者是没有办法建立出本身创建的子类的实例。幸亏,咱们能够经过OC的一项强大的特性"关联对象"来解决这个问题。
那么什么事关联对象呢?
咱们能够给某个对象关联许多其余对象,这些对象经过"键"来区分。存储值的时候,能够经过指明存储策略来维护内存,存储策略就是你存储的是一个NSString啊,那你就该把从存储策略改成copy,相似于这种
下面是对象关联类型:
关联类型 | 等效的@property属性 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic,copy |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic,retain |
OBJC_ASSOCIATION_COPY | copy |
OBJC_ASSOCIATION_RETAIN | retain |
管理关联对象的相关方法:
使用这种方法时,咱们能够把某对象想象成一个NSDictionary,把关联到该对象的值理解为字典中的条目,那么这些关联对象,就至关于设置字典里的值和获取字典里的值了
在iOS中,咱们若是想要使用UIAlertView 咱们须要这样定义
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"你肯定吗?"
message:@"可能没那么肯定吧"
delegate:self
cancelButtonTitle:@"取消"
otherButtonTitles:@"继续", nil];
[alert show];复制代码
而后咱们须要实现UIAlertView的代理,来进行操做的识别判断
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
[self doCancle];
}else{
[self doContinue];
}
}复制代码
这样写,如今看来是没有什么问题的,可是若是咱们须要在当前的一个类中,处理多个警告信息,那么代码将会变得复杂,咱们须要在delegate中判断当前的UIAlertView的信息,根据不一样的信息实行不一样的操做。
若是 咱们能在建立UIAlertView的时候 就将这些操做作好,那么,在delegate中咱们就不须要判断UIAlertView了。事实上这种方法是可行的。
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"你肯定吗?"
message:@"可能没那么肯定吧"
delegate:self
cancelButtonTitle:@"取消"
otherButtonTitles:@"继续", nil];
void (^block) (NSInteger) = ^(NSInteger buttonIndex){
if (buttonIndex == 0) {
NSLog(@"cancle");
}else{
NSLog(@"continue");
}
};
//将block设置为UIAlertView的关联对象
objc_setAssociatedObject(alert, PPSMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
[alert show];复制代码
咱们只须要在delegate中拿到block就行
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, PPSMyAlertViewKey);
block(buttonIndex);复制代码