网站开发客户来源,phpcms 网站栏目,wordpress 文章分享代码,哈尔滨站建筑文章目录 一、Objective-C的起源1.OC和其它面向对象语言2.OC和C语言3.要点 二、在类的头文件中尽量少引用其他头文件1.OC的文件2.向前声明的好处3.如何正确引入头文件4.要点 三、多用字面量语法#xff0c;少用与之等价的方法1.何为字面量语法2.字面数值3.字面量数组4.字面量字… 文章目录 一、Objective-C的起源1.OC和其它面向对象语言2.OC和C语言3.要点 二、在类的头文件中尽量少引用其他头文件1.OC的文件2.向前声明的好处3.如何正确引入头文件4.要点 三、多用字面量语法少用与之等价的方法1.何为字面量语法2.字面数值3.字面量数组4.字面量字典5.可变数组和字典6.局限性7.要点 四、多用类型常量少用#define预处理指令1.类型常量与#define2.static和const3.外界可见的常值变量4.要点 五、用枚举表示状态、选项、状态码1.枚举表示状态2.枚举表示选项3.辅助宏4.枚举表示状态码5.要点 一、Objective-C的起源
1.OC和其它面向对象语言
Objective-C和Java、C都是面向对象语言但是语法上有些许不同。OC使用“消息结构”而不是“函数调用”这二者的区别主要体现在使用消息结构的语言其运行所应执行的代码由运行环境来决定使用函数调用的语言则由编译器决定。OC的重要工作都是由运行期组件来完成而不是编译器。
消息与函数调用之间的区别
//MessagingObjevtive-C
Object *obj [Object new];
[obj performWith:parameter1 and:parameter2];//Function callingC
Object *obj new Object;
obj-perform(parameter1, parameter2); 运行期组件包含OC面向对象特性的全部数据结构和函数它的本质就是一种动态库并且能够桥梁一样把我们所编写的代码连接起来。 2.OC和C语言
OC是C语言的超集因此C语言中所有的功能在OC中依然可以使用并且理解OC中的内存模型和引用计数的一个前提就是理解C语言的内存模型。OC的指针是指向对象的想要声明一个变量并且指向某个对象可以使用如下语法 NSString *someString The string;
OC中所有的对象都必须这么声明因为OC中的对象所占的内存是在堆区中而不会分配到栈区中这样的好处有
1.在堆区分配对象意味着对象的生命周期可以超出当前函数的作用域。如果对象被分配在栈区当函数返回时栈上的局部变量会被自动销毁这将导致对象在栈上被销毁进而引发悬垂指针的问题。 2.指针变量通常存储在栈中对象在堆区分配可以被多个指针同时所指。 3.堆区可以容纳任意大小的对象而栈区的大小通常是有限的。由于对象的大小不确定将其分配到堆区可以更好地适应各种大小的对象需求。 3.要点
1.Objective-C为C语言添加了面向对象特性是其超集。Objective-C使用动态绑定的消息结构也就是说在运行时才会检查对象类型。接收一条消息之后究竟应执行何种代码由运行期环境而非编译器来决定。
2.理解C语言的核心概念有助于写好Objective-C程序。尤其是要掌握内存模型和指针。
二、在类的头文件中尽量少引用其他头文件
1.OC的文件
与C和C一样Objective-C也使用“头文件”header file与“实现文件”implementation file来区隔代码。用Objective-C编写“类”class的标准方式以类名做文件名分别创建两个文件头文件后缀用.h实现文件后缀用.m。
//EOCPerson.h
#import Foundation/Foundation.h
interface EOCPerson : NSObject
end//EOCPerson.m
#import EOCPerson.h
implementation EOCPerson
//Implementation of methods
end
如果创建一个新类例如EOCEmployer类然后将其设置为EOCPerson类的属性就需要在ECOPerson类的接口部分添加#import EOCEmployer.h。有时候我们使用EOCPerson类时不需要知道ECOEmployer类的全部细节只需要知道有这个类就行此时就可以使用向前声明forward declaring 需要修改的就是在接口部分将#import EOCEmployer.h替换成class EOCEmployer;
但是在ECOPerson类的实现部分要使用ECOEmpoyer类就必须知道它的全部细节因此就需要添加#import EOCEmployer.h。需要注意的是将引入头文件的时机尽量延后只在需要的时候才引入这样就可以减少类的使用者所需引入头文件的数量。
2.向前声明的好处
向前声明解决了两个类相互引用的问题。例如要编译EOCEmployer类则编译器必须知道EOCPerson类要编译EOCPerson类则编译器必须知道EOCEmployer类所以需要在两个头文件中互相引用对方的头文件但是这样就会导致循环引用从而引发两个类中有一个不能被正常编译。使用向前声明则不需要引入头文件也就不会通过头文件的循环引用导致出错。
3.如何正确引入头文件
1.如果你写的类继承于某个超类则必须引入定义那个超类的头文件。
2.如果声明某个类遵循某个协议那么这个协议就必须有完整的定义也就是要知道这个协议的全部细节因此就不能使用向前声明。所以要实现属性、实例变量或者遵循协议而必须引入头文件的话则应该将其移至“class-continuation分类中”。
4.要点
1.除非确有必要否则不要引入头文件。一般来说应在某个类的头文件中使用向前声明来提及别的类并在实现文件中引入那些类的头文件。这样做可以尽量降低类之间的耦合coupling。
2.有时无法使用向前声明比如要声明某个类遵循一项协议。这种情况下尽量把“该类遵循的协议”的这条声明移至“class-continuation分类”中。如果不行的话就把协议单独放在一个头文件中然后将其引入。 三、多用字面量语法少用与之等价的方法
1.何为字面量语法
在OC中初始化NSString对象一般用alloc及init方法这种方法有点繁杂因此从Objective-C 1.0的时候出现了非常简单的方式来创建NSString对象就是“字符串字面量”语法如下
NSString *someString Effective Objective-C 2.0;这个格式跟C语言中字符串的初始化如出一辙。使用字面量语法可以缩减源代码长度使其更为易读。
2.字面数值
传统的将数值包装成OC的NSNumber对象使用如下方法
NSNumber *someNumber [NSNumber numberWithInt:1];使用字面量语法则是
NSNumber *someNumber 1;并且能够以NSNumber实例表示的所有数据类型都可以使用字面量
NSNumber *intNumber 1;
NSNumber *floatNumber 2.5f;
NSNumber *boolNumber YES;
NSNumber *charNumber a;
//也适用下面的的表达式
int x 5;
float y 6.32f;
NSNumber *expressionNumber (x * y);
3.字面量数组
传统的创建数组和操作数组元素的方法如下
NSArray *animals [NSArray arrayWithObjects:cat, dog, mouse, badger, nil];NSString *dog [animals objectAtIndex:1];
而使用字面量语法来创建和操作数组元素的方式如下
NSArray *animals [cat, dog, mouse, badger];NSString *dog animals[1];
可以看到使用字面量语法更加简单并且可读性更强。还有一点当数组中某个元素为nil时使用传统方法创建的数组会依次处理各个参数直到遇到nil才停止也就是不会报错但是数组个数为nil元素之前的个数。使用字面量语法则会抛出异常并且程序终止这样程序的健壮性会更好。
4.字面量字典
传统的创建字典和根据键来取值的方法如下
NSDictionary *personData [NSDictionary dictionaryWithObjectivesAndKeys:Mett, firstName, Galloway, lastName, [NSNumber numberWithInt:28], age, nil];NSString *lastName [personData objectForKey::lastName];
而使用字面量语法创建字典和根据键来取值的方法如下
NSDictionary *personData {firstName: Matt, lastName: Galloway, age: 28};NSString *lastName personData[lastName];
可以看到使用字面量语法更加简单并且更符合阅读习惯。和字面量创建数组一样使用字面量语法创建字典遇到nil则会抛出异常并且程序终止。
5.可变数组和字典
通过取下标操作可以访问数组中的某个元素或者字典中的某个键对应的元素如果数组和字典是可变的mutable那么也能通过下标修改其中的元素值例如
mutableArray[1] dog;
mutableDictionary[lastName] Galloway;
6.局限性
字面量语法有个小小的限制就是除了字符串以外所创建出来的对象必须属于Foundation框架才行。如果自定义了这些类的子类则无法用字面量语法创建其对象。并且使用字面量语法创建出来的字符串、数组、字典对象都是不可变的要变成可变对象则需要拷贝一份
NSMutableArray *mutable [[1, 2, 3, 4, 5] mutableCopy];7.要点
1.应该使用字面量语法来创建字符串、数值、字典。与创建此类对象的常规方法相比这么做更加简明扼要。
2.应该通过取下标操作来访问数组下标或者字典中的键对应的元素。
3.用字面量语法创建数组或者字典时若值中有nil则会抛出异常。因此务必确保值里不含nil。
四、多用类型常量少用#define预处理指令
1.类型常量与#define
通常在定义常量时会用到#define例如#define ANIMATION_DURATION 0.3 但是使用这种方法定义的常量不会包含类型信息并且还有可能在编译之后运行时被修改从而导致问题。因此还有更好的方法来进行替换例如下面这个方法
static const NSTimeInterval kAnimationDuration 0.3;
这种方式定义的常量包含类型信息其好处是清楚的描述了常量的含义。由此可知该常量类型为NSTimeInterval这有助于其编写开发文档。 常量命名规则 如果常量局限在实现文件中则在常量前加k如果常量在类之外可见则通常将类名作为该常量的前缀。 常量定义的位置也非常重要我们最好不要将常量定义在头文件中若你定义在头文件中又被其他的文件引用了那么该这个文件中的这个常量都会被其替换掉所以最好不要在头文件中定义常量。
2.static和const
变量一定要同时使用static和const来声明如果试图修改由const修饰符所声明的变量那么编译器就会报错。static修饰符则意味着该变量仅在定义此变量的编译单元中可见。假如声明此变量时不加static则编译器会为它创建一个“外部符号”。此时若是另一个编译单元中也声明了同名变量那么编译器就会抛出错误信息。
3.外界可见的常值变量
有时候我们需要对外公开某个常量比如说用一个对象派发通知让其他想要接收通知的对象向该对象注册派发通知的时候需要字符串表示通知名称我们定义一个常量外界就可以直接使用这个常值变量来注册自己想要接收的通知即可而不用知道实际字符串的值。此类常量需放在“全局符号表”中以便可以在定义该常量的编译单元之外使用。方式如下
//In the header file
extern NSString *const EOCStringConstant;//In the implementation file
NSString *const EOCStringConstant VALUE;
这个常量在头文件中声明并且在实现文件中定义。 注意const在常量类型中的位置。因为常量定义应该从右向左解读所以它是一个不可变的指向NSString *类型的指针这个指针的指向不允许被改变。extern就是告诉编译器在全局符号表中将会有一个名叫EOCStringConstant的符号也就是说编译器无需查看其定义即允许代码使用此常量。因为符号要放在全局符号表里所以我们就得更加注意其命名为避免冲突最好是用与之相关的类名做前缀。
4.要点
1.不能用预处理指令定义常量。这样定义出来的常量不含类型信息编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值编译器也不会产生警告信息这将导致应用程序中的常量值不一致。
2.在实现文件中使用static const来定义“只在编译单元内可见的常量”。由于此常量不在全局符号表中所以无需为其名称加前缀。
3.在头文件中使用extern来声明全局变量并在相关实现文件中定义其值。这种常量要出现在全局符号表中所以其名称应该加以区隔通常用与之相关的类名做前缀。
五、用枚举表示状态、选项、状态码
1.枚举表示状态
枚举是一种常量命名方式。某个对象所经历的各种状态就可以定义为一个简单的枚举集。例如用下列枚举表示“嵌套字连接”的状态
enum EOCConnectionState {EOCConnectionStateDisConnected,EOCConnectionStateConnecting,EOCConnectionStateConnected,
};
编译器一般从0开始每个枚举加1来为枚举分配一个独有的编号并且实现枚举所用的数据类型取决于编译器。定义枚举变量的方式通常如下
enum EOCConnectionState EOCConnectionStateDisConnected;
可以看到不是很简洁因此为了操作起来更加方便可以使用typedef关键字重新定义枚举类型的方式
typedef enum EOCConnectionState EOCConnectionState;
C11标准修改后可以指明用何种底层数据类型来保存枚举类型的变量这样就可以向前声明枚举变量了。其语法如下
enum EOCConnectionState : NSInteger {/*...*/};
上面这个枚举类型数据的底层数据类型就是NSInteger了我们也可以使用向前声明的方法
enum EOCConnectionState : NSInteger;
还可以收订指定某个枚举成员的所对应的值那么在它之后的枚举类型的值就是在该值的基础上依次加一。
enum EOCConnectionState {EOCConnectionStateDisConnected 1,EOCConnectionStateConnecting,EOCConnectionStateConnected,
};
2.枚举表示选项
使用枚举可以定义选项并且用“按位或操作符”可以组合多个选项用“按位与操作符”可以判断是否启用某个选项。例如iOS UI框架中有如下枚举类型用来表示某个试图应该如何在水平或者垂直方向上调整方向
enum UIViewAutoresizing {UIViewAutoresizingNone 0,UIViewAutoresizingFlexibleLeftMargin 1 0,UIViewAutoresizingFlexibleWidth 1 1,UIViewAutoresizingFlexibleRightMargin 1 2,UIViewAutoresizingFlexibleTopMargin 1 3,UIViewAutoresizingFlexibleHeight 1 4,UIViewAutoresizingFlexibleBottomMargin 1 5,
};
上面操作代码如下
enum UIViewAutoresizing resizing UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;if (resizing UIViewAutoresizingFlexibleWidth) {//UIViewAutoresizingFlexibleWidth is set
} 3.辅助宏
Foundation框架下定义了一些辅助宏来定义枚举类型从而指定用于保存枚举值的底层数据类型。这些宏在支持新语法的编译器上使用新式语法否则是旧式语法。
4.枚举表示状态码
可以把逻辑含义相似的一组状态码放入一个枚举集中而不需要用#define来定义。比如在创建UI元素的时候使用不同的样式可以把这些样式声明为枚举类型。在switch语句里枚举类型可以这样定义
typedef NS_ENUM (NSUInteger, EOCConnectionState) {EOCConnectionStateDisConnected,EOCConnectionStateConnecting,EOCConnectionStateConnected,
};switch (_currentState) {EOCConnectionStateDisConnected://Handle disconnected statebreak;EOCConnectionStateConnecting://Handle connecting statebreak;EOCConnectionStateConnected://Handle connected statebreak;
}
通常我们喜欢在switch语句的最后加上default分支但是定义状态码的话最好不要用。因为你本就是枚举变量使用default分支如果用户没有使用枚举中的类型那么这个switch语句也会进入default分支会导致程序出现问题。
5.要点
1.应该用枚举来表示状态机的状态、传递给方法的选项以及状态码等值给这些值起个易懂的名字。
2.如果把传递给某个方法的选项表示为枚举类型而多个选项又可同时使用那么就将各选项值定义为2的幂以便通过按位或操作将其组合起来。
3.用NS_ENUM与NS_OPTIONS宏来定义枚举类型并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的而不会采用编译器所选的类型。
4.在处理枚举类型的switch语句中不要实现default分支。这样的话加入新枚举之后编译器就会提示开发者switch语句并未处理所有枚举。