赞
踩
本节做为Objective-C的入门课程,笔者会从零基础开始介绍这种程序设计语言的各个方面。
首先,我们需要创建一个Command line tool工程项目(即不带图形化界面的项目)。
在上述工程创建界面上有很多模板项目,可以按需要选择相应的模板开发,这样省去了好多搭建框架的时间,但也可以选择从空项目开始。多数模板可以从字面意思就可以了解。
- 现在我们只需要知道Command Line Tool工程模板就足够了(一种无UI界面的可在命令行执行的脚本工程模板);
- 后续在涉及AppKit之前的所有代码我们全会以这类工程为载体演示代码,其它的工程模板在讲到其内容时再详细解释。
按照惯例,我们还是以一个Hello Word项目做为开始,了解一下ObjC(ObjectiveC简称)项目结构和基础语法。项目名称暂时称为helloWorld,项目设置采用默认即可,不需要做任何改变,项目结构如下:
打开main.m文件,我们所有的测试代码暂时全写在这里面,.m是ObjC代码文件的后缀(.c是C语言的源码文件),运行时会交由程序编译器LLVM处理和运行。
//
// main.m
// helloWorld
//
// Created by 刘东 on 2023/12/20.
//
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool { // 自动释放池,由系统来管理变量的内存
// insert code here...
NSLog(@"Hello, World!"); // 打包日志函数,用@修饰表示NSString对象
}
return 0; // 规定 0 表示程序正常结束,其它值都是非正常结束
}
除了上述.m文件外还可以兼容几下以类源码文件:
在ObjC中只有两种注释:
// Secondary text that may be displayed
/*
Secondary text that may be displayed adjacent to or below the primary title depending on the configuration of the window.
A value of empty string will remove the subtitle from the window layout.
*/
格式为:#import <Foundation/Foundation.h> 注意最后面没有;分号,表示为当前类的实现添加相关的模块依赖。如果导入的是自定义的实现,则需要用双引号(本地)替换<>(系统)。
#import <Foundation/Foundation.h>
#import "Fraction.h"
上述所谓的系统其实称为框架更合适,比如Foundation、AppKit框架,每个框架都有一个主头文件,它包含了框架内所有的头文件,只需导入一次就可以使用此框架内所有的功能,这样就省去了一个个导入的麻烦。
MAC OS所有框架的目录位于 /System/Library/Frameworks 目录下。
程序运行的主入口函数,格式为:int main(int argc, const char * argv[]),程序的执行入口和java的main函数功能相同,每个App最多只能允许存在一个main函数。一般练习时用main函数调用就行,如果是大型项目ObjC也提供了专门的单元测试框架,后续会讲到,main.m语法格式如下:
static void method(){
}
/*
argc:命令行输入的参数个数
argcv:字符指针数组,即参数值
*/
int main(int argc, const char * argv[]) {
@autoreleasepool {
//代码位置
}
return 0;
}
函数说明:int main(int argc, const char * argv[])
int main(int argc, const char * argv[]) {
@autoreleasepool {
struct entry dict[100] = {
{"abyss", "a bottomless pit"},
{"addle", "to become confused"}
};
int entries = 10;
int entryNumber;
int lookup(struct entry dict[], char search[], int entries);
if (argc!=2){
NSLog(@"no word typed on the command line.");
return 1;
}
entryNumber = lookup(dict, argv[1], entries);
if(entryNumber!=-1){
NSLog(@"%s", dict[entryNumber].definition);
}
}
return 0;
}
调用方法如下,可从命令行,也可从Xcode中执行
clang -fobjc -arc main.m lookup abyss -
上述程序会调用函数lookup,然后在dict字典中查找argv[1]中的单词,如果找到就返回详细的解释。
在main函数所在的类中也可以添加自定义的方法,但方法的命名方式和ObjC的语法有很大不同,这一点需要额外注意。在main中定义的方法是C语言的语法。
#import <Foundation/Foundation.h>
//无参方法,在方法前面也可以添加static关键字
void nsRangeTest(){
NSRange range1 = {17, 4};
}
//有参方法
NSComparisonResult *compareArray(id element, id compareEle){
return [[compareEle name] compare: [element name]];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
nsRangeTest();
}
return 0;
}
打印函数,相当于C语言或JAVA语言的print()函数,这里需要注意写法,一定是以@开头,这也说明了NSLog函数的入参是一个NSString类型的对象(在ObjC语言中,字符串用@""表示),另外所有的Cocoa函数和对象全部以NS做为命名前缀(也被称命名空间),也有一些老的API是采用CF开头的但不建议使用了。
NSLog(@"Hello, World!");
int sum = 20+25;
NSLog(@"The sum is %i", sum); //NSLog函数如果发现%,则视为占位符,这样的占位符有很多,后续会讲到
也可以用printf()函数来代码,但不是太建议,因为NSLog添加了很多格式化的信息,注意看下面代码的输出
NSLog(@"Hello, World!\n");
printf("Hello, World!\n");
~~~~
2024-03-26 19:31:52.091265+0800 helloWorld[46546:5251675] Hello, World!
Hello, World!
scanf()函数也可以使用占位符,因为键盘接收的原始数据全是字符串,在程序中需要做一些类型转换工作。下在程序运行后在scanf处会卡住,然后在控制台输入相应字符后就会往下执行了。
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age;
scanf("%i", &age); //注意&的用法,它表示一个指针引用,
NSLog(@"age is %i", age);
}
return 0;
}
/*~~
aaa
2024-03-27 14:39:56.103878+0800 objcBase[59017:6048518] age is 32759
Program ended with exit code: 0
*/
除了使用Xcode工具运行,还可以使用命令行工具执行。其命令如下,其中prog1为重命名的项目名称。但这种方式并友好,因为还在设置PATH等资源目录。
(base) MacBook:~ liudong$ clang -fobjc-arc main.m -o prog1
(base) MacBook:~ liudong$ clang -fobjc-arc -framework Foundation main.m -o prog1
输入类似下面的界面:
用NSLog输出时,上面的4596表示当前应用的进程ID号。另外编译好的文件一般会存放在以下目录中,其中helloWorld-ghwqfuwptpnvhtfpzqoxxzcbnzow是一串随机值。不同版本的OS操作系统存放的位置有可能不太一样。
~/Library/Developer/Xcode/DerivedData/helloWorld-ghwqfuwptpnvhtfpzqoxxzcbnzow/Build/Products/Debug
进入这个目录下可以执行./helloWorld就可以直接运行应用了。
有两种方式一种是在main函数中中止,一种是在方法中强制中止,在main函数中直接使用retrun 0即可,则在方法中需要使用以下代码。
exit(0);
首先来讲,苹果公司通常喜欢在不同版本的XCode中增加或移动一些功能,而且这些功能对开发代码的效率是非常高的。下面就以XCode V13版本为例来说明下这些设置如何操作。
在Preferences偏好设置中可以下载不同的运行环境:
XCode有代码提示功能,输入一个字符会有默认提示,通过ESC键来打开或关闭提示框,然后通过Control+/-可实现快速翻页。
可以通过在源码中设置特殊的标识来把需要关心的代码加入到代码面包屑工具栏中,这些标识在编译时会被编译器忽略掉。
这些特殊标记通常用:
鼠标左键+option,然后点击某个类型,在弹出窗口点击相应的类型名可直接跳转到document;
control+i,格式化代码;
command+d,删除行(需要在keymap中搜索delete line自行设置);
command+左/右箭头,快速移到行首和行尾
command+shift+o,快速查找;
command+option+左/右箭头:展开和折叠代码,功能们于Edit-Code Folding下面
command+option+shift+左/右箭头:展开和折叠所有方法
command+r,运行程序
command+u,测试程序
另外可供编辑使用的快捷键盘就是电脑上的触摸屏,可通过Edit-Customizer Touch bar 来设置,如下:
主要使用以下几个工具,依次是:跳到下一个断点、下一行、进入被调用的方法、跳出被调用的方法。分别对应快捷键F5~F8。
另一个调试窗口在导航区上,与调试区联动,主要是下图中这两个标签页,一个是性能查看,另一个是断点浏览
鼠标悬浮到某个程序变量上也会显示相应的信息
还有一些更高级的功能可以在控制台输入相应的指令,比如:
这个功能是不是一个新的功能,很多IDE都有此种能力,有些还会以插件的形式存在,比如sonna, understand或是idea中的各种分析插件。
静态检查器的功能就是不运行代码来分析代码中可能存在的一些问题,在xcode中其功能集中在菜单"project-Analyze"中,它可以检查代码中的:
//类似这样的标签还有很多,可以按需选择
static void dataFun (void) NS_RETURNS_RETAINED { }
关键字BOOL,其值默认只有YES或NO,在Objc中只可与1和0相互转换,占8位存储空间,在写程序时也可以用#define把TRUE和FALSE定义为1 和 0,示例如下:
BOOL areIn(int thing, int ti){
if (thing == ti){
return (YES);
}
return NO;
//return thing = ti; 这行代码有问题,因为ObjC中只有0和1来平替YES和NO
}
NSString *bool2Str(BOOL y){
if (y == YES){
return @"yes";
}else{
return @"no";
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL *boolV = areIn(1, 2);
NSLog(@"%d = %@", boolV, bool2Str(boolV)); //0 = no
NSLog(@"%d = %@", 1, bool2Str(1)); //1 = yes
NSLog(@"%d = %@", YES, bool2Str(YES)); //1 = yes
}
return 0;
}
单个字符,用’'单引号表示
char c = 'a';
int i = 1000;
带小数点的变量,比如
float f = 123.95;
双精度
double d = 8.44e+11;
字符不是一个基本类型,它是一个对象,在使用时除了用对象实例化后,简单的可以直接用@“”,来表示:
NSString *str = @"korgs";
创建一个可变字符串
NSMutableString *stringM1 = [[NSMutableString alloc] initWithString:@"字符串"];
可用cString打印字符串内容,它返回的是一个char *指针地址。
NSString *string = @"abdc";
NSLog(@"%@, %s", string, [string cString]);//abc, abc
NSLog(@"lenght = %i", [string length]);//4
ObjC中的数据类型定义非常有意思,支持组合定义,这些限定词主要包括:long, long long, short, unsigned和signed这几个,它的作用是扩充原有数字的表述范围,具体的范围会根据系统决定,比如
long int factorial; //声明为long的整形变量
long, long long, short, unsigned和signed
主要是数值上面:
以上类型如果需要用NSLog等函数打印时,其占位符表示都不太一样,大体如下。当用%@时表示可以打印任何内容。看似很复杂,其实就四个特殊的,float, long, unisigned, long long,分别用f, l(L), u(U), ll(LL)来表示,其它的不是太常用
类型 | 实例示例 | NSLog字符 |
---|---|---|
char | ‘a’ ‘\n’ | %c |
short int | 123 | %hi, %hx, ho |
unsigned short int | 123 | %hu, %hx, %ho,%hx |
int | 12, -97, 0177(8进制)0xFEE0(16进制) | %i, %x, %o |
unsigned int | 12u, 100U, 0xFFU | %x, %0, %u |
long int | 12L, -2001, 0xFFFFL | %li, %lx, %lo |
unsigned long int | 12UL, 100ul, 0xFFFFUL | %lu, %lx, %lo |
long long int | 500ll, 0xe5e5e5LL | %llu, %llx, %llo |
float | 12.32f, 3.1e-5f | %f, %e, %,g, %a |
double | 12.32, 3.1e-5 | %f, %e, %g, %a |
long double | 12.34L, 3.1e-5l | %Lf, %Le, %Lg |
id | nil | %p |
* | *p(指针) | %@ |
- %@:是一个通用字符可表示任何数据,可归类为打印对象,它会调用类的description:方法;
- %s:打印字符串
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。