参考资料:
(1)类与对象
Objective-C 的类: 类包含了两个部分:定义(interface)与实现(implementation)。定义(interface)部分包含了类声明和实例变量的定义,以及类相关的方法。实现(implementation)部分包含了类方法的实际代码。

objectivec
#import <Foundation/Foundation.h>
//类接口声明
@interface Animal : NSObject
@property (nonatomic, strong) NSString *name; //属性(类的特征)
- (instancetype)initWithName:(NSString *)name; //单个参数的方法声明
- (NSString *)introduce:(NSString *)name :(int)age :(NSString *)gender; //多个参数的方法声明
- (void)makeSound; //实例方法(无返回值无传参的方法声明) (类的功能)
+ (void)wolfing; //类方法
@end
arduino
//预处理指令
#import <Foundation/Foundation.h>
#import:OC特有的头文件引入指令,eg:#import <Foundation/Foundation.h>
<Foundation/Foundation.h>: Cocoa 和 Cocoa Touch框架的核心组件,有点类似于C语言的studio.h,包含NSObject、NSString等基础类,为IOS开发提供运行环境,以及需要遵从的协议;
less
//类接口声明
@interface Animal : NSObject
@interface :类/接口的声明符,结尾需使用 @end;(类名遵循大驼峰命名规范)
objectivec
//属性声明
@property (nonatomic, strong) NSString *name;
@property:属性声明方法,可以自动生成getter和setter
nonatomic:OC在定义时有原子属性(atomic)和非原子属性(nonatomic)两种属性、atomic是线程安全,需要消耗大量的资源;nonatomic是非线程安全,适合内存较小的移动设备;因此在IOS APP开发时,建议所有的属性都适用nonatomic,避免多线程抢占资源,将加锁、资源抢夺的逻辑交给服务端处理,减轻客户端的压力;
strong :一种属性声明,表示指向并拥有该对象,表示浅拷贝,多个指针指向同一个地址;
与之对应的常用属性声明是copy,表示深拷贝,copy的属性会在内存中拷贝一份新的对象,指向新的地址;
objectivec
//方法声明
- (instancetype)initWithName:(NSString *)name;
- (void)makeSound;
--- :实例方法标识符,类方法用 + 标识
instancetype:返回类型声明,表示不确定类型,运行时才能确定,即调用该方法的类实例对象指针;因为不确定性,因此不能作为对象的定义类型,只能用户成员方法返回类型;与之相关的是id类型,也是未知类型,但是是确定的(确定未知),因此可以用作定义对象类型,eg:id Animal;
initWithName: 方法名(遵循小驼峰命名规范)
**(NSString )* :参数类型声明(中间有一个空格)、明确参数为字符串指针,NSString表示字符串类型;
(void) :无返回值方法声明
@end:协议与内存管理
objectivec
//类接口实现
@implementation Animal;
- (instancetype)initWithName:(NSString *)name{
self = [super init];
if(self){
_name = name;
}
return self;
}
- (void)makeSound{
NSLog(@"I am %@,I can wolf",slef.name)
}
+ (void)wolfing{
NSLog(@"I can wolfing");
}
@end //必须以end闭合
objectivec
//实现实例方法
- (instancetype)initWithName:(NSString *)name{
self = [super init]; //确保父类初始化完毕后再初始化子类
if(self){ // 确认self初始化成功,不为nil
_name = name //使用_name的方式访问变量来设置属性值,避免不必要的setter;
}
return self
}
//多传参方法实现
- (NSString *)introduce:(NSString *)name :(int)age :(NSString *)gender{
NSString *result = [NSString stringWithFormat:@"my name is %@,i am %d years old, i am %@",name,age,gender];
NSLog(@"%@",result);
return result;
}
- (void)makeSound{
NSLog(@"I am %@,I can wolf",slef.name)
}
//实现类方法
+ (void)wolfing{
NSLog(@"I can wolfing");
}
self = [super init] :确保父类初始化完毕后再初始化子类
已经声明和实现了一个接口,那么应该如何使用它?
csharp
int main(int argc, const char * argv[]) {
@autoreleasepool {
Animal *animal = [[Animal alloc] initWithName:@"gggg"];
NSLog(@"init name is %@",animal.name); //因为你初始化的时候给了name属性一个默认值,因此这个地方打印出来是gggg
animal.name = @"Dog"; //你也可以重写这个属性的值,打印出来就是你重写的值
[animal makeSound]; //实例化方法需要实例化对象进行调用
[Animal wolfing]; //类方法,可以不经过实例化,直接被类调用;
[animal introduce:@"小花" :9 :@"男"];
}
return 0;
}
@autoreleasepool:自动释放池,在该释放池内创建的对象在被销毁时会被自动释放;
ini
Animal *animal = [[Animal alloc] initWithName:@"gggg"];
上述代码是为Animal类创建了一个实例,名称叫做animal,[Animal alloc]的作用时调用 alloc 方法为 Animal 类分配内存。alloc 方法会返回一个指向 Animal 对象的指针,但此时对象还未初始化。因此后续需要init类进行初始化; 一般情况下,如果不传参数的话,我们用init方法这设置对象的初始状态即可,例如将属性设置为默认值。不会为属性提供一个初始值;
ini
Animal *animal = [[Animal alloc] init]
//在oc2.0中,如果不需要传参,可以直接使用new 方法
Animal *animal = [Animal new]
但,我们在类声明时声明了一个initWithName方法,即
objectivec
- (instancetype)initWithName:(NSString *)name;
这个方法就要求你实现的时候,必须也实现这个方法,初始化一个name 因此,Animal *animal = [[Animal alloc] initWithName:@"gggg"]的作用就是在初始化时为animal实例添加了一个name属性,值为gggg
(2)继承
我们可以把Animal当成父类,然后我们继续创建一个Dog的子类,并继承父类,则代码变成了:
objectivec
#import <Foundation/Foundation.h>
//创建父类接口
@interface Animal : NSObject; //继承自根Object
@property (nonatomic, strong) NSString *name; //属性声明
- (instancetype)initWithName:(NSString *)name; //方法声明
- (void)makeSound; //实例方法声明,用-修饰
+ (void)wolfing; //类方法声明,用+修饰
@end
//父类接口实现
@implementation Animal;
// 实例化方法
- (instancetype)initWithName:(NSString *)name{
self = [super init]; //防御性措施,确保父类初始化成功后再初始化子类
//检查self是否为nil
if(self){
_name = name;
// 通过在方法内部使用下划线前缀(如_name = name;),直接访问实例变量来设置属性值。
// 这是因为在Objective-C中,直接使用属性名进行赋值会自动调用属性的setter方法,
// 这在某些情况下(如懒加载属性)是有用的,但在初始化时直接使用实例变量可以避免不必要的setter调用开销。
}
return self;
}
- (void)makeSound{
NSLog(@"I am %@,I can wolf",self.name);
}
+ (void)wolfing{
NSLog(@"I can wolfing");
}
@end
//声明子类接口
@interface Dog : Animal;
@property (nonatomic,strong)NSString *leg;
-(void)makeRun;
@end
//子类接口实现
@implementation Dog;
- (void)makeRun{
NSLog(@"I can RUN");
}
@end
//程序入口
int main(int argc, const char * argv[]) {
//自动释放池
@autoreleasepool {
Animal *animal = [[Animal alloc] initWithName:@"gggg"]; //创建一个类的实例
NSLog(@"init name is %@",animal.name);
animal.name = @"Dog"; //添加属性,字符串前
[animal makeSound]; //执行实例方法
[Animal wolfing]; //类的方法不用实例化,可被类直接调用
// 子类可直接调用父类的属性和方法,包括实例方法和类方法
Dog *dog = [[Dog alloc] init];
dog.name= @"petty";
dog
[dog makeRun]; //调用自己的实例方法
[dog makeSound]; //调用父类的实例方法
[Dog wolfing]; //调用父类的类方法
}
return 0;
}
(3)对象的属性
对象的属性:
objectivec
@interface Dog : NSObject {
NSString *age;
int _a;
float _b;
}
@end
一般情况下,对象的属性是不是允许被外部访问的,即:
csharp
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog2 = [[Poodle alloc] init];
dog2->_a = 2; //当前情况下是无法给对象的属性进行赋值的
}
return 0;
}
若要在外部访问对象中的属性,则需要@public关键字进行修饰;
objectivec
@interface Dog : NSObject {
@public
NSString *age;
int _a;
float _b;
}
@end
访问的语法:
ini
Dog *dog1 = [[Dog alloc] init] //类的实例化 oc 1.0写法
Dog *dog1 = [Dog new]; //类的实例化 oc 2.0写法(常用)
dog1->_a = 2; //访问并修改类的属性值,写法一(常用)
(*dog1)._a = 4; //访问并修改类的属性值,写法二
(4)方法的实现可以直接访问对象所声明的属性
例如,我们有如下的Person类的声明,其中包含了一些方法:
objectivec
@interface Person : NSObject{
NSString *_name;
int _age;
NSString *_gender;
}
- (void)run:(NSString *)name;
@end
@implementation Person;
- (void)run:(NSString *)name{
_age = 10;
NSLog(@"i am %@,i am runing,my age is %d",name,_age);
}
@end
在上述代码中,可以在方法的实现中,直接访问对象的属性,不用实例化,而且不要求对象的属性使用@public进行修饰;
(5)分组导航标记
当一个文件中有很多类和类的实现时,不方便查找;可以为类和类的实现加上中文导航,方便快速查找;
1、第一种导航方法:在导航条中添加一个标题
arduino
#pragma mark 分组名

2、第二种导航方法:在导航条中添加一条横线
arduino
#pragma mark -

3、第三种导航方法,可直接结合水平线和标题
arduino
#pragma mark - 猫猫类

(6)类指针作为方法的参数
类,本质上来讲是我们自定义的一个数据类型,因此是可以作为方法的参数的;
首先声明一个Dog类:
objectivec
@interface Dog : NSObject{
NSString *_name;
}
-(void)bark;
@end
@implementation Dog
- (void)bark{
NSLog(@"狗会叫");
}
@end
然后声明一个Person类:
less
@interface Person : NSObject{
@public
NSString *_name;
int age;
}
-(void) eat;
-(void) testDog:(Dog *)dog1;
@end
@implementation Person
-(void)eat{
NSLog(@"我是人,我会吃饭");
}
-(void)testDog:(Dog *)dog1{
[dog1 bark];
}
@end
在Person类中,我们声明了一个方法testDog,这个方法需要传入一个Dog类指针作为参数,然后这个testDog的实现是调用了Dog类指针中的bark方法;
因此在使用时,我们就可以通过实例话Person指针,并调用testDog方法,同时传入Dog类指针作为参数;且只能传入Dog类,因为声明的是Dog类;
csharp
int main(int argc, const char * argv[]){
Person *p1 = [Person new];
Dog *dog1 = [Dog new];
[p1 testDog:dog1]; //传入一个类指针作为方法的参数
}
而且当对象作为方法的参数时,是地址传递;
如:我们在Person类的实现中修改了狗的名称:
scss
@implementation Person
-(void)eat{
NSLog(@"我是人,我会吃饭");
}
-(void)testDog:(Dog *)dog1{
dog1->_name = @"大黄";
[dog1 bark];
}
@end
然后在main中调用的时候也修改了狗的名称;
ini
int main(int argc, const char * argv[]){
Person *p1 = [Person new];
Dog *dog1 = [Dog new];
dog1->_name = @"旺财";
[p1 testDog:dog1]; //传入一个类指针作为方法的参数
NSLog(@"狗的名字是:%@",dog1->_name);
}
那么此时狗的名称是:大黄;因为虽然我们先将dog的_name属性修改为了旺财,但是,我们又执行了p1的testDog方法,在这个方法中,又将 _name属性改为了大黄;这就是因为传入的Dog *指针是地址,大家都指向同一个对象,因此改的都是同一个属性;
(7)上帝杀人示例(对象作为方法的参数)
Gog类:
objectivec
@interface God : NSObject{
@public
NSString *_name;
int _age;
int _leftlife;
Gender _gender;
}
-(void)killWithPerson:(Person *)person;
@end
@implementation God
-(void)killWithPerson:(Person *)person{
NSLog(@"我是%@,请喝下这杯毒药,凡人......",_name);
NSLog(@"要带走的人是:%@,他今年%d岁,性别为:%u,剩余生命为:%d",person->_name,person->_age,person->_gender,person->_leftLife);
person->_leftLife = 0;
NSLog(@"%@已经被带走了,他的剩余生命为%d",person->_name,person->_leftLife);
}
@end
Person类:
objectivec
@interface Person : NSObject{
@public
NSString *_name;
int _age;
int _leftLife;
Gender _gender;
}
@end
@implementation Person
@end
main函数:
ini
int main(int argc, const char * argv[]){
God *god = [God new];
Person *p1 = [Person new];
god->_name = @"耶稣";
god->_age = 9999;
god->_leftlife = 9999;
god->_gender = GenderMale;
p1->_name = @"小东";
p1->_age = 30;
p1->_gender = GenderFeMale;
p1->_leftLife=10;
[god killWithPerson:p1];
}
(8)对象作为方法的返回值
返回值是一个对象,那么返回值类型应该是类指针;
按照上帝的那个例子来讲:为上帝新增一个造人的方法(带参和不带参)
objectivec
@interface God : NSObject{
@public
NSString *_name;
int _age;
int _leftlife;
Gender _gender;
}
-(void)killWithPerson:(Person *)person;
-(Person *)makePerson; //每次都造一样的属性
-(Person *)makePersonWithName:(NSString *)name andGender:(Gender)gender andAge:(int)age andleftLife:(int)leftLife; //有多个参数传参,可以自定义属性
@end
ini
@implementation God
-(void)killWithPerson:(Person *)person{
NSLog(@"我是%@,请喝下这杯毒药,凡人......",_name);
NSLog(@"要带走的人是:%@,他今年%d岁,性别为:%u,剩余生命为:%d",person->_name,person->_age,person->_gender,person->_leftLife);
person->_leftLife = 0;
NSLog(@"%@已经被带走了,他的剩余生命为%d",person->_name,person->_leftLife);
}
-(Person *)makePerson{
Person *p1 = [Person new]; // 创建要返回的对象实例
p1->_name =@"亚当";
p1->_age = 20;
p1->_gender = GenderMale;
p1->_leftLife = 20
return p1;
}
-(Person *)makePersonWithName:(NSString *)name andGender:(Gender)gender andAge:(int)age andleftLife:(int)leftLife{
Person *p1 = [Person new];
p1->_name = name;
p1->_age = age;
p1->_gender = gender;
p1->_leftLife = leftLife;
return p1;
}
@end
在Person类中新增一个展示的方法:
objectivec
@interface Person : NSObject{
@public
NSString *_name;
int _age;
int _leftLife;
Gender _gender;
}
-(void)show;
@end
@implementation Person
-(void)show{
NSLog(@"造的人名字是:%@,年龄是:%d,性别是:%u,还能生存%d年",_name,_age,_gender,_leftLife);
}
@end
main方法调用:
ini
int main(int argc, const char * argv[]){
Person *p2 = [god makePerson];
Person *p3 = [god makePersonWithName:@"杏花" andGender:(GenderFeMale) andAge:(24) andleftLife:(70)];
[p2 show];
[p3 show];
}