Objective-C 通常写作ObjC或OC,是扩充C的面向对象编程语言。
1980初布莱德·考克斯(Brad Cox)发明Objective-C,1988年前CEO为乔布斯的Next公司获得OC授权,并开发next step开发环境,
1996苹果公司收购Next公司,发行cocoa开发环境,为了区分c,所以前缀为NS.
Objective-C 是一种简单的计算机语言,设计为可以支持真正的面向对象编程。
Objective-C 通过提供类定义,方法以及属性的语法,还有其他可以提高类的动态扩展能力的结构等,扩展了标准的 ANSI C 语言。类的语法和设计主要是基于 Smalltalk,最早的面向对象编程语言之一
Objective-C:C的超集
Objective-C是C语言的严格超集--任何C语言程序不经修改就可以直接通过Objective-C编译器,在Objective-C中使用C语言代码也是完全合法的。Objective-C被描述为盖在C语言上的薄薄一层,因为Objective-C的原意就是在C语言主体上加入面向对象的特性。
Objective-C为面向对象的编程Objective-C完全支持面向对象的编程,包括面向对象开发的四大特性
基本语法
Objective-C的面向对象语法源于Smalltalk消息传递风格。所有其他非面向对象的语法,包括变量类型,预处理器(preprocessing),流程控制,函数声明与调用皆与C语言完全一致。但有些C语言语法合法代码在objective-c中表达的意思不一定相同,比如某些布尔表达式,在C语言中返回值为true,但在Objective-C若与yes直接相比较,函数将会出错,因为在Objective-C中yes的值只表示为1。
示例代码
#import
int main(int argc, char *argv[]) {
@autoreleasepool {
NSLog(@"Hello World!");
}
return 0;
}
Objective-C程序基本上由以下部分组成
示例代码:
#import
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
@end
int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
return 0;
}
编译并运行结果:
2018-10-13 07:48:42.120 demo[25832] Hello, World!
示例解析
(1)#import
(2)@interface SampleClass:NSObject 用于创建接口。它继承了NSObject,此类是所有对象的基类。
(3)- (void)sampleMethod; 用于声明一个方法。
(4)@end标记了接口的结束。
(5)@implementation SampleClass用于指示它实现了接口SampleClass。
(6)- (void)sampleMethod{}用于指示实现sampleMethod方法。
(7)@end指示实现的结束。
(8)int main()是程序执行入口的主函数。
(9)/…/表示注释,它将被编译器忽略,并且已经在程序中添加了其他注释。 所以这些行在程序中称为注释。
(10)NSLog(…)是Objective-C中可用的另一个函数,它会生成消息“Hello,World!”并显示在屏幕上。
(11)return 0;,终止main()函数并返回值0。
Objective-C程序由各种令牌组成,令牌可以是关键字,标识符,常量,字符串文字或符号。
示例
NSLog(@"Hello, World! \n"); 六个令牌组成
解析
NSLog
@
(
"Hello, World! \n"
)
;
(1)分号;
分号是语句终止符。也就是说,每个单独的语句必须以分号结束。 它表示一个逻辑实体的结束。
(2)注释
以/*开头并以字符*/
结尾
(3)标识符
Objective-C标识符是用于标识变量,函数或其他用户定义项的名称。 标识符以字母A到Z或a到z或下划线_开头,后跟零个或多个字母,下划线和数字(0到9)。
注:Objective-C不允许标识符中的标点符号,如@
,$
和%
。 Objective-C是一种区分大小写的编程语言。
Objective-C空白格
只包含空格(可能带有注释)的行称为空行,而Objective-C编译器完全忽略它。
数据类型是指用于声明不同类型的变量或函数的扩展系统。 变量的类型决定了它在存储中占用的空间大小以及如何解释存储的位模式。
基本数据类型
(1) 整数类型
表达式sizeof(type) 计算产生对象或类型的存储大小(以字节为单位)。
如果需要定义一组相关常量,可以采用枚举类型,把这些常量定义成一个类型,
例如游戏在上、下、左、右方向,可以枚举类型:
enum direction{
up,
down,
left,
right
}.
其中,up从0开始,down是1,以此类推加1。如果不想从0开始,也可以指定初始值
enum direction{
up=1,
down,
left,
right
}.
%@ 对象
%d, %i 整数
%u 无符整形
%f 浮点/双字
%x, %X 二进制整数
%o 八进制整数
%zu size_t
%p 指针
%e 浮点/双字 (科学计算)
%g 浮点/双字
%s C 字符串
%.*s Pascal字符串
%c 字符
%C unichar
%lld 64位长整数(long long)
%llu 无符64位长整数
%Lf 64位双字
%e 实数,用科学计数法计
常量
常量指的是程序在执行期间不会改变的固定值。这些固定值也称为文字。
常量可以是任何基本数据类型,如整数常量,浮点常量,字符常量或字符串文字。还有枚举常量。
定义常量
Objetive-C中有两种简单的方法来定义常量
#define LENGTH 10
const int LENGTH = 10;
变量
变量是程序可以操作的存储区域的名称,它决定了变量内存的大小和布局
变量的名称可以由字母,数字和下划线(_)字符组成
Objective-C语言内置很多运算符,提供如下类型的运算符
决策
决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及在条件被确定为真时要执行的一个或多个语句,以及可选的,如果条件要执行的其他语句 被认定是假的。
Exp1 ? Exp2 : Exp3;
?表达式的确定方式如下:评估Exp1。 如果结果为true,那么Exp2会被评估并成为整个值?表达式的值。 如果Exp1评估为false,则计算Exp3,Exp3的结果值将成为表达式的值。
在Objective-C中,基本上会将函数称为方法。
Objective-C基础框架提供了程序可以调用的许多内置方法。 例如,appendString()方法将字符串附加到另一个字符串。
定义方法
- (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2{
body of the function
}
Objective-C编程语言中的方法定义由方法头和方法体组成。 以下是方法的所有部分
(1)返回类型 - 方法可以返回值。return_type是函数返回的值的数据类型。 某些方法执行所需的操作而不返回值。 在这种情况下,return_type是关键字void。
(2)方法名称 - 这是方法的实际名称。方法名称和参数列表一起构成方法签名。
(3)参数 - 参数就像一个占位符。调用函数时,将值传递给参数。该值称为实际参数或参数。参数列表指的是方法的参数的类型,顺序和数量。 参数是可选的; 也就是说,方法可能不包含任何参数。
(4)连接参数 - 一个连接的参数是让它更易于阅读并在调用时清楚地表达它。
(5)方法体 - 方法体包含一组语句,用于定义方法的作用。
方法声明
-(int) max:(int)num1 andNum2:(int)num2;
块是C,Objective-C和C++等编程语言中的高级功能,它允许创建不同的代码段,这些代码段可以传递给方法或函数,就像它们是值一样。 块是Objective-C对象,因此它们可以添加到NSArray或NSDictionary等集合中。 它们还能够从封闭范围中捕获值,使其类似于其他编程语言中的闭包或lambda。
简单块声明语法
returntype (^blockName)(argumentType);
简单的块实现
returntype (^blockName)(argumentType)= ^{
};
示例代码
void (^simpleBlock)(void) = ^{
NSLog(@"This is a block");
};
调用上面块的示例代码
simpleBlock();
块接受参数和返回值
块也可以像方法和函数一样获取参数和返回值。
使用类型定义块
#import
typedef void (^CompletionBlock)();
@interface SampleClass:NSObject
- (void)performActionWithCompletion:(CompletionBlock)completionBlock;
@end
@implementation SampleClass
- (void)performActionWithCompletion:(CompletionBlock)completionBlock {
NSLog(@"Action Performed");
completionBlock();
}
@end
int main() {
/* 第一个Objective-C程序 */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass performActionWithCompletion:^{
NSLog(@"Completion is called to intimate action is performed.");
}];
return 0;
}
执行上面示例代码,得到以下结果:
2019-05-10 08:14:57.105 demo[184:323] Action Performed
2019-05-10 08:14:57.108 demo[184:323] Completion is called to intimate action is performed.
NSNumber
NSString、NSMutableString
NSArray、NSMutableArray
NSDictionary、NSMutableDictionary
struct
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
} book;
Objective-C编程语言提供了一个名称为typedef的关键字,可以使用此关键字为类型指定新名称。
typedef unsigned char BYTE;
标识符BYTE可以用作unsigned char类型的缩写(或别名)
#import
typedef struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
} Book;
int main() {
Book book;
book.title = @"Objective-C编程";
book.author = @"Yiibai";
book.subject = @"编程教程";
book.book_id = 10010;
NSLog( @"Book title : %@\n", book.title);
NSLog( @"Book author : %@\n", book.author);
NSLog( @"Book subject : %@\n", book.subject);
NSLog( @"Book Id : %d\n", book.book_id);
return 0;
}
执行结果
2019-05-15 09:16:30.201 main[189222] Book title : Objective-C编程
2019-05-15 09:16:30.203 main[189222] Book author : Yiibai
2019-05-15 09:16:30.203 main[189222] Book subject : 编程教程
2019-05-15 09:16:30.203 main[189222] Book Id : 10010
注:typedef 与 #define 区别
#define是一个Objective-C指令,它也用于定义类似于typedef的各种数据类型的别名,但有以下区别
每个变量都是一个内存位置,每个内存位置都定义了它的地址,可以使用符号(&)运算符进行访问,该运算符表示内存中的地址。
#import
int main () {
int var1;
char var2[10];
NSLog(@"Address of var1 variable: %x\n", &var1 );
NSLog(@"Address of var2 variable: %x\n", &var2 );
return 0;
}
指针是一个变量,它的值是另一个变量的地址,即存储单元的直接地址。 与任何变量或常量一样,必须先声明指针,然后才能使用它来存储任何变量地址。
指针变量声明的一般形式是
type *var_name;
type是指针的基类型; 它必须是有效的Objective-C数据类型,var_name是指针变量的名称。
int *ip; /* 指向 int 类型的指针 */
double *dp; /* 指向 double 类型的指针 */
float *fp; /* 指向 float 类型的指针 */
char *ch /* 指向 char 类型的指针 */
所有指针的值是实际数据类型的地址值,无论是整数,浮点数,字符还是其他,都是相同的,是表示内存地址的长十六进制数。
使用指针
示例代码
#import
int main () {
int var = 20; /* 变量定义 */
int *ip; /* 指针变量声明 */
ip = &var; /* 在指针变量中存储 var 的地址*/
NSLog(@"Address of var variable: %x\n", &var );
/* 存储在指针变量中的地址 */
NSLog(@"Address stored in ip variable: %x\n", ip );
/* 使用指针访问该值 */
NSLog(@"Value of *ip variable: %d\n", *ip );
return 0;
}
执行结果
2019-05-15 04:05:36.179 main[80041] Address of var variable: 23bea2dc
2019-05-15 04:05:36.183 main[80041] Address stored in ip variable: 23bea2dc
2019-05-15 04:05:36.183 main[80041] Value of *ip variable: 20
Objective-C NULL指针
如果没有要分配的确切地址,最好将NULL值分配给指针变量。这是在变量声明时完成的。 指定为NULL的指针称为空指针。
NULL指针是一个常量,在几个标准库中定义了零值
#import
int main () {
int *ptr = NULL;
NSLog(@"The value of ptr is : %x\n", ptr );
return 0;
}
执行上面示例代码,得到以下结果:
2019-05-15 04:26:24.203 main[40259] The value of ptr is : 0
Objective-C预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。 简单来说,Objective-C预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。 我们将Objective-C预处理器称为OCPP。
所有预处理器命令都以井号(#)开头。它必须是第一个字符(前面不能有空格)
ANSI C定义了许多宏。虽然每个都可用于编程,但不应直接修改预定义的宏。
示例:
#import
int main() {
NSLog(@"File :%s\n", __FILE__ );
NSLog(@"Date :%s\n", __DATE__ );
NSLog(@"Time :%s\n", __TIME__ );
NSLog(@"Line :%d\n", __LINE__ );
NSLog(@"ANSI :%d\n", __STDC__ );
return 0;
}
执行结果
2019-11-15 08:44:54.041 main[50640] File :main.m
2019-11-15 08:44:54.042 main[50640] Date :Nov 15 2019
2019-11-15 08:44:54.042 main[50640] Time :08:44:52
2019-11-15 08:44:54.042 main[50640] Line :7
2019-11-15 08:44:54.043 main[50640] ANSI :1
(1)宏延续()
宏通常必须包含在一行中。宏延续运算符用于继续对于单行来说太长的宏
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
(2)字符串化(#)
字符串化或数字符号运算符(#)在宏定义中使用时,将宏参数转换为字符串常量。 此运算符只能在具有指定参数或参数列表的宏中使用。
#import
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
执行结果
2019-05-15 08:56:38.088 main[98681] Carole and Debra: We love you!
(3)令牌粘贴(##)
宏定义中的令牌粘贴运算符(##)组合了两个参数。 它允许将宏定义中的两个单独的标记连接到一个标记中。
#import
#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
执行结果
2019-05-15 08:58:04.872 main[138839] token34 = 40
(4)defined()运算符
预处理器定义的运算符用于常量表达式,以确定是否使用#define定义标识符。如果定义了指定的标识符,则该值为true(非零)。 如果未定义符号,则值为false(零)。
#import
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
NSLog(@"Here is the message: %s\n", MESSAGE);
return 0;
}
执行结果
2019-05-15 09:04:30.790 main[31654] Here is the message: You wish!
OCPP的一个强大功能是使用参数化宏模拟函数的能力。
#define square(x) ((x) * (x))
#import
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
执行结果
2019-05-15 09:08:15.586 main[64146] Max between 20 and 10 is 20
打印日志
NSLog
错误
NSError
Objective-C程序使用NSError对象来传达有关用户需要了解的运行时错误的信息。 在大多数情况下,程序会在对话框或工作表中显示此错误信息。 但它也可能会解释信息并要求用户尝试从错误中恢复或尝试自行更正错误
NSError对象包括
自定义错误
NSString *domain = @“com.yiibai.MyApplication.ErrorDomain”;
NSString *desc = NSLocalizedString(@“Unable to complete the process”, @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
命令行参数
执行时,可以将一些值从命令行传递给Objective-C程序。 这些值称为命令行参数
命令行参数使用main()函数参数处理,其中argc表示传递的参数数量,argv[]是指针数组,指向传递给程序的每个参数。
执行时,可以将一些值从命令行传递给Objective-C程序。 这些值称为命令行参数,很多时候它们对程序很重要,特别是当想要从外部控制程序而不是在代码中对这些值进行硬编码时就很有用了。
命令行参数使用main()函数参数处理,其中argc表示传递的参数数量,argv[]是指针数组,指向传递给程序的每个参数。 以下是一个简单的示例,它检查命令行是否提供了任何参数并采取相应的操作 -
#import
int main( int argc, char *argv[] ) {
if( argc == 2 ) {
NSLog(@"The argument supplied is %s\n", argv[1]);
} else if( argc > 2 ) {
NSLog(@"Too many arguments supplied.\n");
} else {
NSLog(@"One argument expected.\n");
}
}
应该注意的是,argv [0] 保存程序本身的名称,argv [1]是指向提供的第一个命令行参数的指针,而* argv [n]是最后一个参数。 如果没有提供参数,argc的值将为1,否则如果传递一个参数,则argc设置为2。
传递由空格分隔的所有命令行参数,但如果参数本身有空格,则可以通过将这些参数放在双引号("")或单引号(’’)中来传递这些参数。再次重写上面的例子,将打印程序名称,也通过放入双引号传递命令行参数