Objective-C基础学习笔记
day01-基础语法
NSString
OC字符串的占位符是%@
NS前缀
NextStep —> Cocoa —> Foundation框架中
如何定义类
1)类的三要素
名字,特征,行为
2)定义类的语法
a.位置:源文件中,不写在main中。
b.类的定义
//类的声明
@interface 类名 : NSObject {
具有的共同特征,定义为变量
}
功能是方法,方法声明写这里
@end
//类的实现
@implementation 类名
方法实现写这里
@end
3)注意
类中属性名必须以_开头,如 _name
创建类的对象
语法:
类名 *对象名 = [类名 new];
Person *p = [Person new];
使用对象
如何访问对象属性:
1)默认下,对象的属性不允许外界直接访问
若要访问,在声明属性的大括号内先加 @public 关键字
2)访问属性的方式
对象名->属性名 = 值;
对象名->属性名;
(*对象名).属性名;
方法的声明和调用
定义
//类的声明
@interface 类名 : NSObject {
具有的共同特征,定义为变量
}
功能是方法,方法声明写这里
@end
//类的实现
@implementation 类名
方法实现写这里
@end
无参数方法
1)声明
a.位置:在@interface大括号外
b.语法:- (返回值类型)方法名;
2)实现
a.位置:在@implementation中实现
b.语法:
(void)方法名{
实现的代码
}
3)调用
a.通过对象调用
b.语法:[对象名 方法名];
带1个参数的方法
1)声明
a.位置:在@interface大括号外
b.语法:
- (返回值类型)方法名:(参数类型)形参名;
- (void)eat:(NSString *)foodName;
方法名叫 eat:
c.函数的声明
void eat(NSString *foodName);
2)实现
a.位置:在@implementation中实现
b.语法:方法声明复制到@implementation中,去掉分号并追加大括号,方法的实现代码写在括号内。
3)调用
a.通过对象调用
b.语法:[对象名 方法名:实参];
带n个参数的方法
1)声明
a.位置:在@interface大括号外
b.语法:
- (返回值类型)方法名:(参数类型)形参名1 :(参数类型)形参名2 :(参数类型)形参名3;
- (int)sum:(int)num1 :(int)num2;
方法名叫sum: :
2)实现
a.位置:在@implementation中实现
b.语法:方法声明复制到@implementation中,去掉分号并追加大括号,方法的实现代码写在括号内。
3)调用
a.通过对象调用
b.语法:[对象名 方法名:实参1 :实参2 :实参3];
带参方法声明规范
1)带1个参数时,方法名起为 xxxWith:
2)带N个参数时,方法名起为 方法名With:(参数类型)形参名1 and:(参数类型)形参名2 and:(参数类型)形参名3
day02-类与对象
类加载
- 内存结构
栈 存储局部变量
堆 程序员手动申请的字节空间 malloc calloc realloc函数
BSS段 存储未初始化的全局变量,静态变量
数据段(常量区) 存储已被初始化的全局变量、静态变量、常量数据
代码段 存储代码,但是属性只有在创建对象后才会创建在对象中
-
类加载
1)在声明一个类的指针变量时会访问类;
2)创建对象时会访问类;
程序运行时,当某个类第一次被访问时,将这个类存储到内存中的代码段区域,这个过程叫类加载。
只有类在第一次被访问时才会做类加载,一旦类加载到代码段,直到程序结束才会被释放。
-
对象在内存中如何存储
-
假设下面这个写在函数之中.
Person *p1 =[Person new];
1).Person *p1;
会在栈内存中申请1块空间。在栈内存中声明1个Person类型的指针变量p1。
p1是1个指针变量。那么只能存储地址.
2).[Person new];
真正在内存中创建对象的其实是这句代码.
new
做的事情
a.在堆内存中申请1块合适大小的空间.
b.在这个空间中根据类的模板创建对象。
类模板中定义了什么属性,就把这些属性依次的声明在对象之中。
对象中还有另外1个属性叫做isa是1个指针。指向对象所属的类在代码段中的地址。
c.初始化对象的属性
如果属性的类型是基本数据类型那么就赋值为0
如果属性的类型是C语言的指针类型那么就赋值为NULL如果属性的类型是0C的类指针类型那么就赋值为nil
d.返回对象的地址。 -
注意
a.对象中只有属性,而没有方法,自己类的属性外加1个isa指针指向代码段中的类。b.如何访问对象的属性
指针名->属性名;
根据指针找到指针指向的对象再找到对象中的属性来访问。
c.如何调用方法
[指针名方法名];
先根据指针名找到对象,对象发现要调用方法再根据对象的isa指针找到类。然后调用类里的方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-htbZ0icn-1655305045670)(https://tva1.sinaimg.cn/large/e6c9d24egy1gznp6whg18j20t90ay754.jpg)]
- 为什么不把方法存储在堆内存的对象中,而是放在代码段中?
每个对象的方法实现的代码都是一样的,这样做可以减少冗余。多个对象的isa值是一致的。
- 对象属性的默认值
创建对象后即使不赋值,属性也是有值的。
基本类型默认是0,C指针类型默认是NULL,OC指针类型默认是nil。
nil与NULL
-
NULL与nil
只能做指针变量的值。表示该指针不指向内存中的任何一块空间。等价于0,是一个宏。
-
C指针用NULL,表示不指向任何空间。OC的类指针用nil,表示不指向任何对象。
-
如果一个类指针的值为nil,表示不指向任何对象。
Person *p = nil;
通过p指针访问对象属性时,运行报错。
通过p指针访问对象方法时,运行不报错,但方法不执行。
分组导航标记
-
#pragma mark 分组名
添加标题
-
#pragma mark -
添加水平线
-
#pragma mark - 分组名
先有水平线再有组名
方法与函数
-
我们之前在C中学习的函数,就叫做函数
void test(){}
-
在OC类中写时方法.就叫做方法。
- (void) sayHi;
- 相同点:
都是用来封装1段代码的。将1段代码封装在其中,表示1个相对独立的功能函数或者方法只要被调用.那么封装在其中的代码就会被自动执行. - 不同点:
- 语法不同,
- 定义的位置不一样.
0C方法的声明只能写在@interface的大括弧的外面,实现只能写在@implementation之中,函数除了在函数的内部和@interface的大括弧之中其他的地方都是可以写。
就算把函数写在类中这个函数仍然不属于类所以创建的对象中也没有这个函数,
注意;函数不要写到类中.虽然这样是可以的但是你千万不要这么做因为这么做是极度的不规范的. - 调用的方式也不一样.
函数可以直接调用.
但是方法必须要先创建对象通过对象来调用。 - 方法数是属于类的。有1个伐木累。有个归属感。
而函数是1个孤魂野鬼,就是1个独立的.
函数的声明,实现,调用
#import <Foundation/Foundation.h>
void test();
int main(int argc, const char * argv[]) {
test();
return 0;
}
//实现可以直接写在声明中,但一定要写在调用之前!
void test(){
NSLog(@"shuchu");
}
常见错误-九条
- @interface是类的声明。@implementation是类的实现他们之间不能相互嵌套.
- 类必须要先声明然后再实现
- 类的声明和实现必须都要有就栓没有方法类的实现也不必不可少的。
- 类的声明必须要放在使用类的前面。
实现可以放在使用类的后面 - 声明类的时候类的声明和实现必须要同时存在,
特殊情况下可以只有实现没社乡明、
虽然可以这样,但是我们平时在写类的时候千万不要这么写因为这么写是极度不规范的会被别人曹晓的。 - 属性名一定要以下划线开头这是规范。否则后面的知识点你就对不上号。
类名每1个单词的首字母大写。 - 属性不允许声明的时候初始化
在为类写1个属性的时候不允许在声明属性的时候为属性赋值。 - OC方法必须要创建对象通过对象名来调用
- 方法只有声明没有实现
a.如果方法只有声明没有实现编译器会给1个警告不会报错。
b.如果指针指向的对象有方法的声明而没有方法的实现那么这个时候通过指针来调用这个方法
在运行的时候就会报错。
多文件开发
-
所有的类都写在main.m这个源文件之中的.
后果:后期的维护就非常的不方便.也不利于团队开发 -
推荐的方式
把1个类写在1个模块之中,而1个模块至少包含两个文件。.h头文件
写的类声明因为要用到Foundation框架中的类 NSObject 所以在这个头文件中要引入Foundation框架的头文件。然后将类的声明的部分写在.h文件中
.m实现文件
先引入模块的头文件这样才会有类的声明再写上类的实现。如果要用到类,只需要引入这个了模块的头文件就可以直接使用了。
-
添加类模块的更简洁的方式
NewFile->Cocoa Class自动生成模块文件.h .m
自动的将类的声明和实现写好。填写的名称是决定模块文件的名称,类名是可以自己再改的
但是建议模块的文件名和模块中的类名保持一致,这样方便代码的管理. -
当我们要使用这个类的时候,需要先将这个类的头文件先引进来.才可以使用。
对象可以作为方法的参数,也可以作为方法的返回值。
参数类型是指针对象 类名 *
对象作为方法的参数时,传递的是地址值,方法内部修改指向对象时,会造成实际改变。
如何移动文件
- Show in finder
- 选中后拖到目标文件夹
- “Copy items if needed”&"Create groups"一定要勾选
定义枚举类型
//定义男女枚举类型
typedef enum {
//想定义的值
GenderMale,
GenderFemale
} Gender; //枚举类型名
若写在单独的.h文件中,需要用 #import “文件名” 也可以写在只用一次的文件里,一般也是.h文件。
对象作为类的属性
Person *p = [Person new];//人有一个 狗 属性
p->_dog = [Dog new];
p->_dog->_qq = [XiangQuan new];//狗有一个 项圈 属性
p->_dog->_qq->_color = @"blue";//给项圈设置颜色
[p->_dog->_qq bLingBLing];//调用项圈闪光方法
day03-封装
异常处理
分类
错误:源代码不符合规范,编译报错。
Bug:可以编译,链接,执行。但结果与预期不一致。
异常:可以编译,链接,执行。但在某种情况下,程序会终止运行,后续代码也不执行。程序崩溃。
处理语法
@try{
....
}
@catch(NSException *ex){
....
}
-
可能异常的代码放在try中。发生异常时,不崩溃而是跳转执行catch中代码,catch中代码执行完再往下执行。
-
catch中只有在异常发生时才执行。所以catch写异常处理代码。
-
@try…@catch后可跟@finally,这里代码无论是否有异常都执行。
-
不是所有异常都能捕获,C语言异常就是。【实际】用if来进行判断会出异常的地方。
02-类方法
- 声明
用+声明
- 调用
用类名直接调用 [类名 类方法名];
-
分析一下类方法和对象方法的调用过程.
类方法的特点
1).节约空间:因为调用类方法不需要创建对象,这样就节约了空间。
2).提高效率:因为调用类方法不需要拐弯直接找到类直接执行类中的类方法. -
在类方法中不能直接访问属性。
1).属性是在对象创建的时候,跟随着对象一起创建在对象之中.2).类第1次被访问的时候,会做类加载,是把类的代码存储在代码段。
因为属性只有在对象创建的时候才会创建在对象之中。
而类方法在执行的时候.有可能还没有对象。对象都没有你访问个毛的属性。
虽然不能直接访问属性。但是我们可以在类方法中创建1个对象访问这个对象的属性. -
在类方法中也不能通过self直接调用当前类的其他的对象f方法
因为对象方法只能通过对象来调用而这个时候没有对象。 -
实例方法中可以直接调用类方法。
-
关于类方法的规范.
1).如果我们写1个类,那么就要求为这个类提供1个和类名同名的类方法。
这个方法创建1个最纯洁的对象返回
因为苹果和高手写的类都遵守这个规范。2).如果你希望创建爱的对象的属性的值由调用者指定那么就为这个类方法带参数,
类名withXXX:
+ (Person*)person{ Person *p1 = [Person new]; return p1; } + (Person *)personwithName:(NSString *)name andAge:(int)age{ Person *p1 = [Person new]; p1->_name = name; p1->_age = age; return p1; }
03-NSString
一、NSString打印的格式说明
%@ 指针指向的对象
%p 指针的地址值
二、常用类方法
- +(nullable instancetype)stringWithUTF8String:(const char *)nullTerminatedCString;
instancetype 代表返回的是当前类的对象,即NSString对象
将C语言字符串转换为OC字符串。输入函数接收的是c字符串,用这个转化就行。
-
+ (instancetype)stringWithFormat:(NSString *)format, …
NSString *s = [NSString stringWithFormat:@"第一个参数就得是带格式符号的%@,%d",@"实力代理",12];
将多个相同或不同类型的变量拼接成一个新的字符串。
int age = 14; NSString *s = @"小王"; NSString *s = [NSString stringWithFormat:@"我是%@,今年%d岁了。", s, age];
三、常用对象方法
-
length方法 返回值为NSUInteger 就是unsigned long %lu
得到字符串个数,可以处理中文。
-
得到字符串中指定下标处字符
- (unichar)characterAtIndex:(NSUInteger)index;
unichar就是unsigned short %C 大写的
%c读的是变量中第一个字节的数据,%C读的是前两个字节的数据,%d读的是前四个字节的数据.
-
判断两个字符串内容是否相同
- (BOOL)isEqualToString:(NSString *)aString;
-
比较两个字符串的大小
- (NSComparisonResult)compare:(NSString *)string;
NSComparisonResult是枚举,-1 0 1
04-匿名对象
-
我们之前创建对象的做法.
Person *p1 = [Person new];
让1个指针指向1个对象这个指针就叫做这个对象的名字. -
匿名对象.
没有名字的对象,如果我们创建1个对象,没有用1个指针存储这个对象的地址。也就是没有任何指针指向这个对象那么这个对象就叫做匿名对象。 -
如何去使用1个匿名对象呢?
因为new实际上1个类方法。这个方法做的事情创建对象(4个步骤)。返回值是创建的对象的地址。[Person new]
这句代码的结果实际上就是创建的那个对象的指针。那我们可以直接使用.
[Person new]->_name = @"jack";
[[Person new] sayHi];
-
注意点.
-
匿名对象只能使用1次.
-
每次创建匿名对象都是不同的对象。
[Person new]->_name = "jack";
创建了1个对象
[Person new]->_age = 18;
又创建了1个对象
[[Person new] sayHi] ;
第3个对象.
-
-
有神马用?
1)。如果某个对象的成员只会被我们使用1次,用完之后这个对象再也不需要了那么就可以使用匿名对象.2)。如果方法的参数是1个对象,而调用者为这个参数赋值的对象就是专门来给这个方法传递的
并且这个对象调用者不会使用那么这个时候就可以直接为方法传递1个匿名对象。
setter & getter
-
属性上的@public去掉
-
setter
- (void)setAge:(int)age;
实现中写逻辑验证
-
getter
- (int)age; - (int)age{ return _age; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AR7JZlGO-1655305045671)(https://tva1.sinaimg.cn/large/e6c9d24egy1gznp8fbobkj20s60fe78e.jpg)]
-
只读只写封装
只读:只提供getter,不提供setter
只写:只提供setter,不提供getter
day04-继承
01-Xcode文档安装
Window - developer documentation
02-static关键字
-
OC中的static关键字用法
a.不能修饰属性和方法
b.可以修饰局部变量
如果局部变量被修饰就会变成静态变量,存储在常量区,方法执行完毕后不被回收,再次执行该方法时继续使用而不是重新声明。
-
当方法返回是该类的对象时,返回值类型可以写成
instancetype
03-self关键字
- 方法中可以声明与属性同名的变量。
方法中访问该同名的变量时,访问的是局部变量。 - self 与java的this仅有一点点像
可以在对象方法和类方法中使用。
self是1个指针。在对象方法中self指向当前对象。在类方法中self指向当前类.
- self用在对象方法中.
1). self在对象方法中,指向当前对象.
当前对象:谁调用方法谁就是当前对象。
2).在对象方法中,self指向当前对象,然后这又有神马用?
a. 可以使用self显示的访问当前对象的属性. self->属性.代表访问的是当前对象的这个属性.
b.可以使用self来调用当前对象的其他的对象方法.
3).对象方法中使用self的场景.
a.必须使用self的场景.
如果在方法中存在和属性同名的局部变量,
你如果想要访问同名的局部变量,直接写就可以了.你如果想要访问当前对象的同 名属性必须使用self
在对象方法中,如果要调用当前对象的其他的对象方法必须使用self.b.
b.选用self的场景.
在方法中不存在和属性同名的局部变量如果这个时候想要访问当前对象的属性 用不用self效果都是一样的.都是访问的当前对象的属性.
属性要求以下划线开头局部变量不要求以下划线开头按照这个规范来实际上是不会重名的.
- 把slef用在类方法中.
1).类加载。
当类第1次被访问的时候会将类的代码存储在代码区.
代码区中用来存储类的空间也有1个地址.
2).在类方法中 self也是1个指针。指向当前这