oc语言学习之基础知识点介绍(二):类和对象的进一步介绍

时间:2023-11-11 08:32:38

一、类、对象在内存中的存储

/*
内存分区:
栈:局部变量
堆:程序员自己写代码申请开辟的 程序员自己维护,编译器现在帮我们自动优化了,它在合适的给我们加上了释放空间的语句,所以我们现在写的对象不会造成内存泄露 全局区:所有的全局变量和静态变量
常量区:所有的常量
代码区:程序编译后的指令集 类是模板,肯定需要存在内存里面,因为实例化对象的时候需要根据这个模板来创建,那么存在内存里面,存在哪呢?? 类模板存在:全局区!
存的是:类的描述,还有所有的方法实现 每个对象都会有一个系统给我们的isa指针,但是这个指针无法直接使用,指针指向的是它所属的类的地址。 对象和类的原则:对象一定属于类。
对象调用方法,步骤其实是 先从指针变量找到对象所在堆空间地址,然后再通过这个对象里存的isa指针找到类中存的方法,再执行
*/ #import <Foundation/Foundation.h> @interface Person : NSObject{ @public
NSString *_name;
int _age;
} -(void)sayHi; @end @implementation Person //当类被加载的时候会调用这个方法
+(void)load{
NSLog(@"Person类被加载了");
} -(void)sayHi{
NSLog(@"大家好,我叫%@,今年%d岁",_name,_age);
} @end int a = ; void test(){ } @interface Dog : NSObject @end @implementation Dog //当类被加载的时候会调用这个方法
+(void)load{ NSLog(@"Dog类被加载了");
} @end int main(int argc, const char * argv[]) {
@autoreleasepool { //验证类在全局区
/*
NSLog(@"全局a的地址:%p",&a);//0x100004700 NSLog(@"类地址:%p",[Person class]); //0x100004688 NSLog(@"%p","abc"); // 0x100003b6e NSLog(@"%p",test);//0x1000013d0
*/ Person *p1 = [Person new]; Person *p2 = [Person new]; Person *p3 = [Person new]; NSLog(@"类地址:%p",[Person class]);//0x1000046e0 //因为下面的都是同一个类型。所以打印的isa地址都一样
NSLog(@"p1->isa=%p", [p1 valueForKey:@"isa"] );//0x1000046e0 NSLog(@"p2->isa=%p", [p2 valueForKey:@"isa"] );//0x1000046e0 NSLog(@"p3->isa=%p", [p3 valueForKey:@"isa"] );//0x1000046e0
Dog *js = [Dog new];
//因为下面的和上面的那些对象不是同一个类型,所以isa地址肯定不一样
NSLog(@"js->isa=%p",[js valueForKey:@"isa"]);//0x1000047f0 }
return ;
}

二、pragma的使用

/*
方法和函数弹出式窗口,里面包含了所有的方法和函数的声明列表以及实现列表,通过点击它可以方便又快速的跳转到某一个方法或函数那。 #pragma mark 分组名
作用:是在方法和函数弹出式窗口中增加分组信息,方便你快速的找到某个方法或函数。 #pragma mark - 分组名:
作用:跟上面一样,但是会增加分隔横线,这样看起来更直观。 */
#import <Foundation/Foundation.h> @interface Person : NSObject #pragma mark - 运动相关
-(void)run;
-(void)swimming;
-(void)playBasktball;
@end #pragma mark - Person实现类 @implementation Person #pragma mark - 运动实现
-(void)run{} -(void)swimming{} -(void)playBasktball{}
@end

三、对象作为方法的参数和返回值

1、对象作为方法的参数

/*
有参数的方法:
定义语法: -(void)方法名:(参数类型)参数1 方法名2:(参数类型)参数2;
把对象作为方法的参数:
语法:--(void)方法名:(类名 *)参数1; 调用:
[对象 方法名:对象]
例: [d displayPerson:p1]; 注意:
C以前分值传递和引用传递,现在OC也是一样。
所以也就是说,你把int char float 结构体变量 等等传给方法都是值传递。
值传递:在方法内改变了形参的值,外面的实参不会改变。
传递对象:是引用传递。
引用传递:在方法内改变了形参的值,外面的实参也会发生改变。 */ #import <Foundation/Foundation.h> #pragma mark - Person类
@interface Person : NSObject{
@public
NSString *_name;
int _age;
} @end @implementation Person @end #pragma mark - Display类
@interface Display : NSObject -(void)displayPerson:(Person *)p; //相当于这个方法需要传入一个Person对象 -(void)changePerson:(Person *)p;//改变Person对象的属性 -(void)changeInt:(int)number; @end @implementation Display
-(void)displayPerson:(Person *)p{ NSLog(@"p->_name=%@ p->_age=%d",p->_name,p->_age);
} -(void)changePerson:(Person *)p{
p->_name = @"习大大";
p->_age = ;
} @end #pragma mark - Main函数
int main(int argc, const char * argv[]) {
@autoreleasepool { //实例化Display对象
Display *d = [Display new]; //把对象作为方法的参数进行输出
//实例化Person对象,名字叫p1
Person *p1 = [Person new]; p1->_name = @"周小帅";
p1->_age = ; //传进去的对象是什么,就会打印什么
[d displayPerson:p1];//周小帅 16
}
return ;
}

2、对象作为方法的返回值

/*
方法定义:
-(返回值类型)方法名:参数列表;
-(int)
-(char)
-(int *)
-(struct student)
-(Person *) //相当于要返回一个Person类的对象
语法:
-(类名 *)方法名:参数列表 -(Person *)creatPerson; //返回一个Person对象
*/
#import <Foundation/Foundation.h> #pragma mark - Person类
@interface Person : NSObject{ @public
NSString *_name;
int _age;
} -(void)sayHi; @end @implementation Person -(void)sayHi{ NSLog(@"大家好,我叫%@,今年%d",_name,_age);
} @end #pragma mark - PersonFactory类 @interface PersonFactory : NSObject /**
* 这个方法返回一个Person对象
*/
-(Person *)creatPerson; @end @implementation PersonFactory -(Person *)creatPerson{
//new关键字无论何时都会开辟新的堆空间(新的对象)
Person *p = [Person new];
p->_age = ;
p->_name = @"周帅";
return p; //你要Person对象,我的p刚好就是person对象,所以可以返回p
//所以每次返回的地址都不一样
} @end
int main(int argc, const char * argv[]) {
@autoreleasepool {
PersonFactory *pf = [PersonFactory new];
//creatPerson里面方法用了new,所以每次返回的都是新的对象
//zs是一个Person对象,地址比如说是:0x11
Person *zs = [pf creatPerson];
//然后打印的是0x11地址里面存的成员数据,因为没有改过,所以打印的还是原来方法内改的值
NSLog(@"zs->name=%@ zs->age=%d",zs->_name,zs->_age);//周帅 16
//ls也一个Person对象,地址比如说是:0x12
Person *ls = [pf creatPerson];
//然后打印的是0x12地址里面存的成员数据,因为没有改过,所以打印的还是原来方法内改的值
NSLog(@"ls->name=%@ ls->age=%d",ls->_name,ls->_age);//周帅 16
//改的是0x11里面的数据
zs->_name = @"苍苍苍";
zs->_age = ;
//因为ls指向的0x12没改,所以打印的还是原数据
NSLog(@"ls->name=%@ ls->age=%d",ls->_name,ls->_age);//周帅 16
//因为zs指向的0x11里面的改了,所以打印的是改后的数据
NSLog(@"zs->name=%@ zs->age=%d",zs->_name,zs->_age);//苍苍苍 15
}
return ;
}

四、结构体作为类的成员

typedef struct {
int year;
int month;
int day; }myDate; @interface Person : NSObject{
@public
NSString *_name;
int _age;
BOOL _gender;
//出生年月日
myDate _birthDay;
//人可以养一条宠物
Dog *_chongwu; //代表是Dog对象类型
} /**
* 说自己生日的
*/
-(void)sayMyBirthDay;
@end
@implementation Person -(void)sayMyBirthDay{
NSLog(@"我出生于%d年%d月%d日",_birthDay.year, _birthDay.month,_birthDay.day);
_chongwu->_color = @"黄色";
}
@end
/* 结构体也可以作为类的成员。 本身类的成员属性的定义语法就是 : 类型 成员变量名;
所以只要符合这个语法的都行!
所以换句话来说,所有只要是类型的东西,都可以作为成员属性 结构体作为对象的成员:
在对象内部:成员名.成员 例:_birthDay.year
在外部:对象名->成员名.成员 例: p->_birthDay.year */ #import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person new];
p->_age = ; // p->_birthDay.year = 1998;
// p->_birthDay.month = 2;
// p->_birthDay.day = 3; //一次性赋值,需要强转,右边的小括号里面必须加这个结构体的类型
p->_birthDay = (myDate){,,}; //实例化某个对象属性
p->_chongwu = [Dog new]; [p sayMyBirthDay];
NSLog(@"出生于哪年%d",p->_birthDay.year);
//外部访问某一个属性(这个属性又是一个对象)的时候的赋值
p->_chongwu->_color = @"红色";
NSLog(@"养了条宠物,毛色是%@",p->_chongwu->_color);
}
return ;
}

五、多文件开发

/*
c语言中有多文件开发,oc中肯定也有。
多文件开发的原因:
1.我们的类代码太多,写在一个文件里面不利于观察。
2.项目是多个人一起开发的,如果写在一个文件里面不利于同时开发。
注意: 写好模块后,一定只能导入.h文件,不能导入.m文件。 多文件开发添加文件方式:鼠标右键,选择New File ,然后在选择Cocoa Class ,第一栏填名称,第二栏是表示继承哪里,一般情况下不需要修改,会自动穿件oc的头文件和.m文件。
*/

六、结构体与类的区别、函数与方法的区别

/*
结构体与类的区别:
1.语法区别。
2.结构体里面不能包含行为(方法、函数),类可以。
3.结构体成员不能写访问修饰符,但是类可以。
4.结构体里面不能有类的对象作为成员,但是类可以有结构体作为成员。
5.结构体数据保存在栈,对象数据保存在堆。
6.结构体是面向过程的思维产物,类是面向对象的思维产物。 函数与方法的区别:
1.语法不同。
2.函数可以直接调用,方法必须通过对象。
3.函数的声明可以写在任意位置,不包括属性列表的大括号里面。方法的声明只能写在@interface和@end之间,不包括属性列表的大括号里面。
4.函数的实现可以写在除了函数内的其他所有位置(不要写在@interface和@end之间),不包括属性列表的大括号里面,方法的实现只能写在@implementation和@end之间。
5.函数是面向过程的思维,方法是面向对象的思维。
*/

七、NSString的略详细介绍

/*

 NSString 是OC中的字符串类型

    用法:
NSString *str = @"字符串"; 1.NSString也是一个对象,所以也可以通过new来创建 2.NSString按格式化组成一个字符串 [NSString stringWithFormat:@"格式化字符串",值列表]; 例: NSString *str = [NSString stringWithFormat:@"哈哈,今年%d岁",16]; 3.OC中比较两个字符串是否相等: C语言:strcmp(字符串1,字符串2) == 0 代表相等; OC:[ OC字符串1 isEqualToString:OC字符串2]; 这个方法有一个BOOL类型的返回值,如果相等返回YES,否则返回NO 例:[str1 isEqualToString:str2] 注意:也可以直接把这个方法调用写在if括号里面判断 4.OC字符串怎么计算实际个数??(重点)
C语言:strlen(字符串); 计算实际占用的字节数 OC语言:[OC字符串 length]; 返回这个OC字符串的实际个数 例:[str length]; */ #import <Foundation/Foundation.h> @interface Person : NSObject{ @public
int _age;
}
@end @implementation Person @end int main(int argc, const char * argv[]) {
@autoreleasepool { //不用这种写法,因为这种对象主要是用来表示一个字符串
// NSString *str = [NSString new];
// str = @"字符串"; //直接这么用就行
// NSString *str = @"字符串";
//
// NSLog(@"%@",str);
//格式化字符串// NSString *str = [NSString stringWithFormat:@"哈哈,今年%d岁",16];
//
// NSLog(@"%@",str); //判断两个字符串是否相等
/*
NSString *str1 = @"哈哈";
NSString *str2 = @"哈哈"; BOOL res = [str1 isEqualToString:str2]; if (res) { NSLog(@"相等"); }else{ NSLog(@"不相等");
}
*/ /*
BOOL res = [@"哈哈" isEqualToString:@"哈哈"]; if (res) {
NSLog(@"相等");
}else{
NSLog(@"不相等");
}
*/ //获得OC字符串的实际个数
NSString *str = @"abcd 哈^";
unsigned long length = [str length];
NSLog(@"%lu",length);//7个 空格、符号等等都算
unsigned long length2 = [@"呵呵呵" length];
NSLog(@"%lu",length2);//3个 }
return ;
}