Objective-C 基础

环境与工具:MacOSx + Xcode

前言

  1. Objective-C 完全兼容 C 语言,其源文件后缀为.m,是 message 的意思,代表其消息机制;
  2. main.m 中的 main 函数仍然是程序的入口;
  3. 框架:是一个功能集合,官方或第三方开发好的程序功能的集合,把这些功能封装在类或函数中,这些类和函数的集合就叫框架
    • Foundation 框架:Objective-C 中最基本的框架,提供了一些最基础的功能和数据类型;
  4. OC 中大量出的NS表示乔布斯当初建立的NeXTSTEP公司,后来被引用到 OC 中;
  5. OC 中大量出现的@符号表示:
    • 将 C 字符串转换为 OC 字符串;
    • 也应用于 OC 中绝大部分的关键字中;
  6. OC 中的注释方式与 C 相同;
  7. OC 中函数的定义与调用方式与 C 相同;
  8. OC 支持 C 中所有的运算符;
  9. OC 支持 C 中所有的控制语句语法;
  10. OC 支持 C 中所有的关键字;

💦开始


Hello world!

main.m

 // #import 与 C 的 #include 一样,是预处理指令,引入头文件,在程序编译时执行,前者是后者的增强版
 // Foundation.h 的位置:Finder - 应用层序 - 右击 Xcode 显示包内容 - Contents - Developer - Platforms - MacOSx.platform - Developer - SDKs - MacOSx10.xx.sdk - System - Library - Frameworks - Foundation.framework - Headers - Foundation.h,打开该文件可见,该文件包含了 Foundation 框架中的所有头文件
 // 下句的意思是把 Foundation 框架中的 Foundation.h 头文件包含进来
#import <Foundation/Foundation.h> 

// main 函数返回值依然是整型 int,代表程序的结束状态,main 函数的输入参数用于接收传递给该程序的参数,与C的main函数传入参数一样,可以为空
int main(int argc, char *argv[]) {  
    @autoreleasepool {  // @autoreleasepool - 自动释放池
        NSLog(@"Hello World!");  // NSLog() 是个函数,于 Foundation.h 中,是 C 中 printf() 的增强版
        // 语法:
        //		NSLog(@"内容");
        //		NSLog(@"格式控制字符串",变量列表);
        // 该函数打印的格式为:当前代码执行时间 + 当前程序工程名称 + 进程编号 + 内容 + 自动换行
    }
   return 0;
}

编译、链接

见教程第 13 集:

iOS开发基础班+就业班(100天完整版)之基础班2:Objective-C学习(10天)

面向过程与面向对象

它们是解决同一个问题的两种不同思路:

  1. 面向过程:解决一件事通过自己逐步实现;
  2. 面向对象:解决一件事找一个专业的方法或人帮忙实现;

面向对象程序设计思想:

  1. 遇到需求先找有没有专门解决这件事的方法或人,如果有就直接用;
  2. 如果没有,直接造出拥有这种功能的对象;

类与对象

  • 类是具有相同特征的事物的统称,是抽象的,不可直接使用;
  • 对象是具体的客观存在的一个实体;
  • 类与对象之间的关系:
    1. 类是对象的模板,根据这类实例化的对象,类有什么特征对象就有什么特征,不可多也不可少;
    2. 类的本质是程序员自定义的一个数据类型,既然类是数据类型,那么他跟其他数据类型一样,也可以作为方法的传入参数

main.m

// 声明一个类
@interface Person : NSObject{
	
	@public // 允许实例化的类访问类属性
	// 类的属性代表这类对象具有的共同特征
	NSString *_name;
	int _age;
	float _height;
	float _weight;
}
// 声明类方法
- (void)run;  // 无参数方法
- (void)eatWithFood:(NSString *)foodName;  // 带单个输入参数无返回值的方法规范
- (int)sumWithNum1:(int)num1 andNum2:(int)num2;  // 带多个输入参数有返回值的方法
- 
@end

// 实现一个类
@implementation Person
// 实现类方法
- (void)run{
	NSLog(@"i am running.");
	// 在类方法的实现中可以访问类属性
	_name = @"Yuen"; 
	// 方法中直接访问的属性是属于实例化对象的
	NSLog(@"my height is %@",_height)
	_weight -= 0.5f;
	NSLog(@"after running, my weight is %@",_weight )
}

- (void)eatWithFood:(NSString *)foodName{
	NSLog(@"i am eatting %@",foodName);
}

- (int)sumWithNum1:(int)num1 andNum2:(int)num2{
	int num3 = num1 andNum2 + num2;
	return num3;
}
@end
// 注意:类的声明和实现必须都存在

int main(int argc, char *argv[]) {  
	// 实例化一个类:类名 *对象名 = [类名 new]
	Person *p1 = [Person new];
	// 访问对象的属性
	p1->_name = @"Yuen"; 
	p1->_age = 18;
	p1->_height= 2.26f;
	p1->_weight = 200.8f;
	(*p1)._name = @"Yuen";
	NSLog(@"your name is %@",p1->_name);
	 // 调用类方法
	[p1 run];
	[p1 eatWithFood:@"apple"];
	int sum = [p1 sumWithNum1:10 andNum2:20];
	NSLog(@"the sum is %d",sum );
	
    return 0;
     }

😏以上是为了方便阅读把类的声明实现调用都放到了main.m文件中,编程中为了方便代码管理,应该分发到不同的文件中;

【例程】类作为方法的传入参数

Person.h

#import <Foundation/Foundation.h>
#import "Dog.h"

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
}
-(void)pet:(Dog *)dog;  // 把类作为方法的传入参数

@end

Person.m

#import "Person.h"

@implementation Person
- (void)pet:(Dog *)dog{
	[dog shout];
}

@end

Dog.h

#import <Foundation/Foundation.h>

@interface Dog:NSobject{
	@public
	NSString *_name;
	int _age;
}
-(void)pet:(Dog *)dog;  // 把类作为方法的传入参数

@end

Dog.m

#import "Dog.h"

@implementation Dog
- (void)shout{
	NSLog(@"wowowowowowwo...");
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	Person *p1 = [Person new];
	Dog *d1 = [Dog new];
	[p1 pet:d1]
	
    return 0;
     }

【例程】对象作为类的传入参数

Person.h

#import <Foundation/Foundation.h>

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
}
- (BOOL)compareAgeWithOtherPerson:(Person *)otherPerson;  // 对象作为类的传入参数

@end

Person.m

#import "Person.h"

@implementation Person
- (BOOL)compareAgeWithOtherPerson:(Person *)otherPerson{
	return (_age > otherPerson->_age);
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	Person *p1 = [Person new];
	Person *p2 = [Person new];

	BOOL res = [p1 compareAgeWithOtherPerson:p2];
	NSLog(@"res = %d",res);
	
    return 0;
     }

【例程】对象作为方法的返回值

Person.h

#import <Foundation/Foundation.h>
#import "Dog.h"

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
}
- (Dog *)pet;  // 对象作为方法返回值

@end

Person.m

#import "Person.h"

@implementation Person
- (Dog *)pet{
	Dog *d1 = [Dog new];
	return d1;  // 返回对象的指针
}

@end

Dog.h

#import <Foundation/Foundation.h>

@interface Dog:NSobject{
	@public
	NSString *_name;
	int _age;
}
- (void)shout;

@end

Dog.m

#import "Dog.h"

@implementation Dog
- (void)shout{
	NSLog(@"wowowowowowwo...");
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	Person *p1 = [Person new];
	Dog *d1 = [p1 pet];
	
    return 0;
     }

【例程】对象作为类的属性

Person.h

#import <Foundation/Foundation.h>
#import "Dog.h"

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
	Dog *_dog;  // 对象作为类的属性
}
- (Dog *)pet;  // 对象作为方法返回值

@end

Person.m

#import "Person.h"

@implementation Person
- (Dog *)pet{
	Dog *d1 = [Dog new];
	return d1;  // 返回对象的指针
}

@end

Dog.h

#import <Foundation/Foundation.h>
#import "Necklace.h"

@interface Dog:NSobject{
	@public
	NSString *_name;
	int _age;
	Necklace *_necklace;  // 对象作为类的属性
}
- (void)shout;

@end

Dog.m

#import "Dog.h"

@implementation Dog
- (void)shout{
	NSLog(@"wowowowowowwo...");
}

@end

Necklace.h

#import <Foundation/Foundation.h>

@interface Necklace:NSobject{
	@public
	NSString *_colour;
}
- (void)shinning;

@end

Necklace.m

#import "Necklace.h"

@implementation Necklace
- (void)shinning{
	NSLog(@"the dog's necklace is %@ and it's shinning...",_colour);
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	Person *p1 = [Person new];
	Dog *d1 = [Dog new];
	d1->_name = @"旺财";
	p1->*_dog = d1;  // 对象作为类的属性
	p1->_dog->_name = @"大黄";  //修改对象的属性
	p1->_dog->_necklace = [Necklace new];  // 对象作为类的属性
	p1->_dog->_necklace->_colour = "golden";
	[p1->_dog->_nacklace shinning];
	
    return 0;
     }

类方法与对象方法

  • 类方法:类方法的调用不依赖于对象,即可在不创建对象下调用类方法,直接通过类名来调用;

😀类方法的优点

  1. 节约内存空间:因为不用额外创建对象;
  2. 提高效率:调用类方法时指针直接指向类所在内存地址,而不是间接通过对象来指向内存地址;

😮 注意

  1. 类方法中不能直接访问类属性,因为类属性只在对象被创建时才随着在对象中被创建,而类方法在执行时如果没有对象,那么就不能访问类属性;
    虽然不能直接访问类属性,但仍可在类方法中创建对象,通过这个对象来访问类属性;
  2. 在类方法中不能通过self直接调用当前类的其他对象方法,因为对象方法只能通过对象来调用;
    当然也可以跟上面一样,在类方法中创建对象,通过这个对象来访问当前类的其他对象方法;
  3. 综上,如果方法不需要直接访问属性,也不需要直接调用其他同类对象方法,则应该使用类方法,因为他可以降低内存占用和提高效率;
  4. 类方法的规范:每创建一个类,都应该为这个类提供一个和该类名同名的类方法,并提供一个对象返回值,其作用是在可用一句话创建一个完整的对象,提高代码可读性(你会发现,苹果在底层封装了很多个这样的类方法);
  • 对象方法/实例方法:对象方法的调用必须先创建对象,通过对象名来调用;

Person.h

#import <Foundation/Foundation.h>

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
}
- (void)sayHi;  // 对象方法声明
+ (void)saySorry;  // 类方法声明
+ (Person *)PersonWithName:(NSString *)name andAge:(int)age;  // 提供一个和该类名同名的类方法,并提供一个对象返回值

@end

Person.m

#import "Person.h"

@implementation Person
- (void)sayHi{  // 对象方法实现
	_name = @"Yuen";
	NSLog(@"hello,i'm %@",_name);
	}

+ (void)saySorry{  // 类方法实现
	NSLog(@"Sorry...");
	
	Person *p1 = [Person new];  // 类方法中创建对象 
	p1->_name = "Yuen";  // 类方法中通过对象访问类属性
	[p1 sayHi];  // 类方法中调用当前类的其他对象方法
	}

+ (Person *)PersonWithName:(NSString *)name andAge:(int)age{
	Person * p1 = [Person new];
	p1->_name = name;
	p1->_age = age;
	return p1;
	}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	Person *p1 = [Person new];
	[p1 sayHi];  // 对象方法调用
	[Person saySorry];  // 类方法调用
	
	Person *p2 = [Person PersonWithName:@"Yuen" andAge:18];  // 一句话创建一个完整的对象	
    return 0;
     }

类加载

内存中的五大区域:

  1. :存储局部变量;
  2. :程序员手动申请的字节空间,如 malloc、calloc、relaloc函数;
  3. BSS 段:存储未被初始化的全局变量、静态变量;
  4. 数据段:(常量区)存储已被初始化的全局静态变量、常量数据;
  5. 代码段:存储代码;

类加载:在程序运行期间,当某个类被第一次访问的时候,会将这个类存储到内存中的代码段区域,这个过程就叫类加载,直到程序结束时,代码区才会被释放;

💥注意

  1. 对象中只有属性,没有方法,因为对象中的方法都是从类而来,是一样的,在内存代码段中占用一小块内存空间即可,没必要浪费更多的内存空间,而属性却可以是自定义的;(见下图)
  2. 访问对象的属性:指针名->属性名;,根据指针找到其指向的对象,再找到对象中的属性来访问;
  3. 调用方法:[指针名 方法名];,先根据指针名找到其指向的对象,再根据对象的 isa 指针找到类,然后在类中找到方法;

main.m

@interface Person : NSObject{
	@public 
	NSString *_name;
	int _age;
	float _height;
	float _weight;
}

- (void)sayHi;  

@end


@implementation Person

- (void)sayHi{
	NSLog(@"hello,my name is %@,i am %d years old.",_name,_age);
}

@end


int main(int argc, char *argv[]) {  
	// 这种类的实例化方式是合法的,他的意义是在栈内存中声明一个 Person 类型的指针变量 p1,但这个指针变量没有指向任何地址值,这就是**类加载**
	Person *p1;  
	// 当然最标准的类实例化方式还是下面这个,上面那个是合法但没意义的,真正创建对象的是 [Person new]
	Person *p1 = [Person new];
	/*
	 new 的作用:
	 	1. 在堆内存中申请一块合适大小的空间;
	 	2. 在这个空间中根据类的模板创建对象,类模板中定义了什么属性,就把这些属性依次声明在对象中;
	 		> 注意:在每一个对象中都有一个隐藏的属性: isa 指针,指向对象所属的类在代码段中的地址值,也即说,不管有多少个对象,如果他们所属的类是同一个,那么他们的 isa 指针中存储的地址值的一样的;
	 	3. 初始化对象的属性;
	 		> 1. 如果属性的类型是基本数据类型,则赋值为 0;
	 		> 2. 如果属性的类型是 C 语言的指针类型,则赋值为 NULL;
	 		> 3. 如果属性的类型是 OC 的类指针类型,则赋值为 nil;
	 		> 其实 NULL 和 nil 都是一个宏,值都是 0,表示指针不指向任何内存空间;
		4. 返回对象的地址;
	 		
	*/
    return 0;
}

在这里插入图片描述
在这里插入图片描述

分组导航标记

  • 分组导航标记:方便快速找到对应类;
    1. #pragma mark 分组名,在导航条的位置显示一个自定义标题;
    2. #prama mark -,在导航条对应位置显示一个水平分隔线;
    3. #pragma mark - 分组名,以上两者的结合;
#pragma mark 人类
@interface Person : NSObject{
}
@end

@implementation Person

@end

#pragma mark - 狗类
@interface Dog : NSObject{
}
@end

@implementation Dog

@end

在这里插入图片描述

函数与方法的同异

  • 函数:实现某种功能的代码封装,如 C 中的void test(){}
  • 方法:在 OC 类中写的函数,就叫方法,如其中的- (void)sayHi;
  • 同异本质是一样的
    1. 定义的语法不同;
    2. 定义的位置不同:
      • OC 方法的声明只能在@interface花括号外边和@end之间,实现只能在@implementation@end之间;
      • 函数除了不能在@interface花括号外边和@end之间和函数里面,其他地方都可以(当然为了代码规范,函数就不要写在类里面);
    3. 调用方法不同:
      • 函数可以直接调用;
      • 方法必须先创建对象,通过对象来调用;

参考:方法的定义与调用

main.m

#import <Foundation/Foundation.h> 

void test();  // 函数声明
void test(){  // 函数实现
	NSLog(@"2333")
}

@interface Person : NSObject{
}
- (void)hello;  // 方法声明
@end

@implementation Person

- (void)hello{  //方法实现
	NSLog(@"hello");	
}

@end

int main(int argc, char *argv[]) {  
	 test();  // 调用函数
	 
	Person *p1 = [Person new];
	[p1 hello];  // 调用方法 
	
    return 0;
}

bug 与 NSException 及其处理

  • bug:程序可以编译、链接、执行,但程序执行的结果不是预期效果,这种情况称为 bug;
  • Exception:程序可以编译、链接、执行,当程序执行到某一步时,程序执行发生崩溃,程序执行终止,这种情况称为 Exception;

异常处理
Person.h

#import <Foundation/Foundation.h>

@interface Person:NSobject{
	@public
	NSString *_name;
	int _age;
}
- (void)sayHi; 

@end

Person.m

#import "Person.h"

@implementation Person
- (void)sayHi{
	NSLog(@"hello.");
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, char *argv[]) {  
	/*
		将有可能发生异常的代码放在 @try 中;
		当 @try 中的代码执行时发生异常,程序不会崩溃,而是跳转到 @catch 中执行里面的代码,然后结束 @try @catch 继续往下执行;
		如果 @try 中代码没有发生异常,就略 @catch 往下执行;
		@try @catch 后还可以跟一个 @finally,里面的代码无论 @try 中是否发生异常都会被执行;
	*/
	/*
		值得注意的是, @try @catch 并不是所有的异常都是可以处理的,C 语言中大部分的异常它都无法处理;
	*/
	@try{
		Person *p1 = [Person new];
		[p1 sayHi];
	}
	@catch(NSException *ex){
		NSLog(@"throw exception:%@",ex);  // 查看异常发生原因
	}
	@finally{
		NSLog(@"coding...")
	}
	
    return 0;
     }

代码规范

  1. 在头文件中声明类,在源文件中实现类;

📏数据类型


  1. OC 支持 C 中的所有数据类型:
    • 基本数据类型:int double float char
    • 构造类型:数组 结构体 枚举
    • 指针类型:int *p1
    • 空类型:void
    • typedef 自定义类型;
  2. BOOL 类型:存储 YESNO,BOOL 类型变量一般用于存储条件表达式的结果;
  3. Boolean类型:存储turefalse,Boolean 类型变量一般用于存储条件表达式的结果;
  4. class类型;
  5. id类型:万能指针;
  6. nil类型:与 NULL 差不多;
  7. SEL:方法选择器;
  8. block:代码块;

数据类型是在内存中开辟空间的一个模板;

NSString 字符串

  1. NSString是OC 中专门用来存储OC 字符串地址数据类型
  2. 其实NSString 是Foundation 框架中的一个类,作用是存储OC 字符串,本质上是用了NSString 对象来存储;
  3. NSSting 中最常用的方法
    1. + (instancetype)stringWithUTF8String:(const char *)nullTerminatedCString;
      1. 该类方法的作用是将C 的字符串转换为OC 字符串对象;
      2. 其中instancetype 为返回值返回这个类的对象;
    2. + (instancetype)stringWithFormat:(NSString *)format;
      1. 该类方法的作用是拼接一个字符串对象,实现把不同数据类型的变量拼接成一个OC 字符串;
    3. length 方法没返回值为NSUInteger,即unsigned long,得到字符串的个数,支持处理中文;
    4. - (unichar)characterAtIndex:(NSUInteger)index;
      1. 返回值是unichar,即unsigned short,占用内存 2字节;
    5. isEqualToString:(NSString *)判断两个字符串的内容是否相同;
    6. - (NSComparisonResult)compare:(NSString *)string;
      1. 作用:比较两个字符串大小;
      2. 返回值(int):
        1. 前者比后者小: -1;
        2. 前者与后者相等:0;
        3. 前者比后者大: 1;

main.m

int main() {  
	NSString *str = @"Yuen"; // 定义一个 OC 字符串
	// 打印字符串的两种形式
	NSLog(@str);
	NSLog(@"%@",str);
	
	// 知道NSString 是一个类之后,我们也可以用类实例化的方式来创建字符串,但不推荐
	NSString *str1 = [NSString new];  // 创建空字符串@""
	NSString *str2 = [NSString string];  // 创建空字符串@""
	NSString *str3 = [NSString stringWithFormat:@"Yuen"];  // @"Yuen" 本质上就是NSString 类的对象,*str3 是这个对象的地址
	NSLog(@"string is %@,and its memory address is %p.",str3,str3)  // 查看字符串内容及其内存地址值

	// 将C 字符串转换为OC 字符串对象
	char *str4 = "Yuen";
	NSString *str5 = [NSString stringWithUTF8String:str4];
	NSLog(@"NSString is %@.",str5)
	
	// 拼接OC 字符串
	int age = 18;
	NSString *str6 = @"Yuen";
	NSSting *str8 = NSString *str7 = [NSString stringWithFormat:@"hello,i'm %@ and i'm %d years old.",str6,age];  // 拼接字符串
	
	// 查看字符串长度
	NSString *str9 = @"Yuenjunnin";
	NSUInteger len = [str9 lenth];
	NSLog(@"string lenth is %lu",len);
	
	// 得到字符串中指定下标的字符
	unichar ch = [str9 characterAtIndex:2];
	NSLog(@"found the specified character:%C",ch);  // 查看字符串中指定下表的字符,用%C(大写C),因为%c 对应的是char,而unichar 对应的是%C

	// 判断两个字符串的内容是否相同
	NSString *str10 = @"dog";
	NSString *str11 = @"cat";
	BOOL yesOrNo = [str10 isEqualToString:str11];
	NSLog(@"Are they two equal? %d",yesOrNo);  

	// 比较两个字符串大小
	NSString *str12 = @"dog";
	NSString *str13 = @"cat";
	NSComparisonResult res = [str12 compare:str13];  // 该方法的返回值本质是一个枚举,其包含int 类型的-1、0、1,所以也可以定义一个int 类型来接收方法返回值,即 int res = [str12 compare:str13]; 
	NSLog(@"which is bigger of them? %d",res); 
	
	return 0;
   }
   

BOOL & Boolean

  1. 查看源码可见,BOOL 实际上是一个有符号的 char 变量,目的是提高代码可读性;

typedef signed char BOOL

YESNO 实际上是10

#define YES((BOOL)1)
#define NO((BOOL)0)

  1. 同理,Boolean实际上是一个无符号的 char 变量,目的也是提高代码可读性;

typedef unsigned char Boolean

truefalse 实际上是10

#define true 1
#define false 0

  1. 同时使用 BOOLBoolean,是为了提高多种语言的兼容性;

main.m

int main() {  
	int num1 = 10;
	int num2 = 20;
	BOOL re1 = num1 > num2
	NSLog(@"%@",re1)
	return 0;
   }
   

💻Xcode

  1. xcode 中新建文件时,选cocoa class,可自动生成同名的头文件和源文件;

新建工程

Xcode 新建一个新工程 - 选 OSX - Application - Command line Tool - Language 选 Objective-c- 生成main.m源代码文件;

Source Control不勾选,不用Git管理代码

从 finder 中添加代码文件

从 finder 中直接拉取代码文件到工程中,会弹出以下窗,记得勾选Copy items if neededCreate groups
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Truffle7电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值