文章目录
前言
1.类与对象
2.方法
3.成员变量
4.隐藏和封装
一、类与对象
首先,类是什么?
类是面向对象的重要内容,可以把类当作一种自定义的数据类型,用类来定义的变量相当于指针类型的变量。
1.面向对象和面向过程
2.类和对象
类是某一批对象的抽象概念。对象是一个个实体。(对象类似于C中的结构体)
接口部分:
@interface用与于声明定义类的接口部分,@end表示结束。
成员变量,用于描述该类的对象的数据状态,方法用来描述类的行为。
我们一般将类的定义的接口声明放在头文件中
实现部分:
1.类名与接口部分的类名相同(函数的定义于函数的使用的函数名相同)
2.可以在类名后使用:父亲表示继承了某个父类
3.必须为类的声明部分的每个方法提供定义(函数声明后需要定义)
C中的类似定义
类似于C中我们将部分函数及结构体放在一个.h文件中。然后在main函数所在的文件中包含这个.h文件。
这里我们需要注意:
OC中建议成员变量名以下划线_开始。并且第一个单词的首字母小写,其余单词的首字母大写,剩余字母小写,单词与单词之间不需要任何分割符。以这样的方式来定义成员变量名。
3.对象的产生和使用
1.对象的定义:
类型 *变量名
2.对象的创建:
[ [ 类名 alloc ] 初始化方法 ]
alloc 为关键字,作用相当于C中的malloc,负责为对象开辟空间。
所有的类都有一个默认的初始化方法init,因此上面的写法等同于[ [类名 alloc ]init ]
4.对象和指针
本质来说,类是一种指针类型的变量。因此,程序中定义的类型只是存放一个地址,它被保存在main()函数的动态存储区,指向实际的对象,而真正的对象存放在堆内。
如果堆内存中的对象没有任何变量指向该对象,那么程序将无法访问该对象。
5.id类型
OC提供了一个id类型,可以代表所有对象的类型。也就是说,任意类型的对象都可以赋值给id类型。
当使用id类型时,OC会在运行时判断该对象的类,并在运行时确定需要动态调用的方法
程序将会在运行时自动检测变量所指的实际的类型。
二、方法(类似C中的函数)
1.定义
方法是类或对象的额行为特征的抽象,也是类或对象最重要的组成部分。
方法在逻辑上要么属于类,要么属于对象。
方法不能独立存在,必须属于类或对象,方法要么属于这个类,要么属于该类的一个对象
方法使用+标识符,这个方法就属于这个类,使用-标识符,就属于这个类的实例。
注意虽然传参传的是副本,但也是地址传递
2.形参个数可变的方法(NSLog()函数)
本质上,可变参数也是一个类似于数组的结构
如果在定义方法时,在最后一个形参名后增加逗号和三点(,…)就表明该形参可以接受多个参数值。
例如:
个数可变的形参只能处于形参列表的最后,一个方法中最多只能有一个个数可变的形参。
🌰
类的接口
#import<Foundation/Foundation.h>
@interface VarArgs:NSObject
-(void)test:(NSString*)name,...;
@end;
类的实现:
#import "0605.h"
@implementation VarArgs
-(void)test:(NSString *)name, ...{
va_list argList;
if(name){
NSLog(@"%@",name);
va_start(argList,name);
NSString* arg=va_arg(argList,id);
while(arg){
NSLog(@"%@",arg);
arg=va_arg(argList,id);
}
va_end(argList);
}
}
@end
测试:
#import"0605.h"
#import<Foundation/Foundation.h>
int main(){
@autoreleasepool {
VarArgs* va = [[VarArgs alloc]init];
[va test:@"heihei",@"nuonuo",@"nana",nil];
}
}
运行结果如下:
3.方法的声明
返回值类型:可以是oc允许的任何类型。如果声明了返回值类型,则该函数内必须有一个有效的return语句,来返回和声明的返回类型一致的数据。
类似于C中的函数,必须返回和函数类型相同的值。(此处函数类型为结构体,返回值类型也应该为结构体)
struct Node* CreatNode(int data) {
struct Node* NewNode = (struct Node*)malloc(sizeof(struct Node));
NewNode->data = data;
NewNode->next = NULL;
return NewNode;
}
在oc的方法声明中,所有的类型(包括void)都应该用圆括号括起来。
4.方法签名
oc中方法签名后边带冒号和不带冒号是完全不同的意思
不带冒号表示他是一个不带形参声明的方法,而带一个冒号表示它是带一个形参声明的方法。
5.方法与函数的不同
1.函数可以传值也可以传址,但方法都是传址
2.函数可以在任意位置定义,方法只能在类中定义
3.方法有(+)(-)标识符的区分,函数没有
4.函数可以独立执行,但方法执行时必须使用类或对象作为调用者
6.🌰
#import<Foundation/Foundation.h>
@interface Person:NSObject{
NSString* _name;
int _age;
}
-(void) steName:(NSString*)name andAge: (int)age;
-(void)say:(NSString*)content;
-(NSString*)info;
+(void)foo;
@end;
#import "0605.h"
@implementation Person{
int _testAttr;
}
-(void)setName:(NSString*)n andAge:(int) a{
_name = n;
_age = a;
}
-(void)say:(NSString*)content{
NSLog(@"%@",content);
}
-(NSString*)info{
[self test];
return [NSString stringWithFormat:@"我是一个人,我叫%@,年龄是%d",_name,_age];
}
-(void)test{
NSLog(@"heihei");
}
+(void)foo{
NSLog(@"person");
}
@end
类的接口:
#import<Foundation/Foundation.h>
@interface Dog:NSObject
-(void)jump;
-(void)run;
@end;
类的实现:
#import "0605.h"
@implementation Dog
-(void)jump{
NSLog(@"start jump");
}
-(void)run{
Dog* d=[[Dog alloc]init];
[d jump];
//将以上两行代码换位下边一行也可
//[self jump];
NSLog(@"start run");
}
@end
测试:
#import"0605.h"
#import<Foundation/Foundation.h>
int main(){
@autoreleasepool {
Dog* dog=[[Dog alloc]init];
[dog run];
}
}
代码运行结果:
三.成员变量
1.定义
只要实例存在,程序就可以访问该实例的变量。方法如下:
OC的成员变量都是实例变量,OC并不支持真正的类变量。
实例—>实例变量
//类的接口
#import<Foundation/Foundation.h>
@interface Person:NSObject{
@public
NSString* _name;
int _age;
}
@end;
//类的实现
@implementation Person
@end;
//测试
int main(){
@autoreleasepool {
Person* p=[[Person alloc]init];
NSLog(@"_name=%@,_age=%d",p->_name,p->_age);
p->_name=@"nuonuo";
p->_age=29;
NSLog(@"_name=%@,_age=%d",p->_name,p->_age);
}
}
代码结果如下:
2.模拟类变量
static不能用于修饰成员变量
3.单例模式
什么是单例类?
如果一个类始终只能创建一个实例,这个类就被称为单例类。 单例类可以通过static全局变量来实现,每次程序需要获取该实例时,先判断该static全局变量是否为nil,如果该全局变量为nil,则初始化一个值并付给该全局变量;如果不为nil,直接返回该全局变量指向的实例即可。
什么时候需要用到单例类?
在某些时候,程序多次创建的某个类的对象没有任何意义,由于不停的创建对象回收对象,还可能造成系统性能下降。此时程序需要保证该类只有一个实例,
类的接口如下:
#import<Foundation/Foundation.h>
@interface Singleton:NSObject
+(id)instance;
@end;
类的实现:
#import "0605.h"
@implementation Singleton
static id instance= nil;
+(id)instance{
if(!instance){
instance=[[super alloc]init];
}
return instance;
}
@end;
测试:
#import"0605.h"
int main(){
@autoreleasepool {
NSLog(@"%d",[Singleton instance]==[Singleton instance]);
}
}
代码结果:
四.隐藏和封装
1.定义和优缺点
封装是指面向对象的三大特征之一,它是指将对象的信息隐藏在对象内部,不允许外部程序直接访问内部信息。而是通过该类所提供的方法来实现对内部信息的访问和操作。
封装的优点:
1.隐藏类的实现细节
2.便于修改,提高代码的可维护性
3.使试用制只能通过事先预定的方法来访问数据
如何实现良好的封装?
1.将对象的成员变量和实现细节隐藏起来
2.把方法暴露出来,
2.使用访问控制符
@private(类似局部变量出作用范围后就不能)
彻底隐藏成员变量,使这个成员变量只能在该类的内部进行访问。
在类的实现部分定义的成员变量相当于默认使用这种访问权限
@package
部分隐藏成员变量,可以在当前类及当前类的同一映像的人易部分进行访问。
@protected
在当前类,当前类的子类任意访问。
在类的接口部分定义的成员变量相当于默认使用这种访问权限。
@public
公共访问权限,可以任意的进行访问。
类的接口:
#import<Foundation/Foundation.h>
@interface Person :NSObject{
@private
NSString* _name;
int _age;
}
-(void)setName:(NSString*)name;
-(NSString*)name;
-(void)setAge:(int)age;
-(int)age;
@end;
类的实现:
#import "0605.h"
@implementation Person
-(void)setName:(NSString*)name{
if([name length]>6||[name length]<2){
NSLog(@"error");
return;
}else{
_name=name;
}
}
-(NSString*)name{
return _name;
}
-(void)setAge:(int)age{
if(_age!=age){
if(age>100||age<0){
NSLog(@"error");
return;
}else{
_age = age;
}
}
}
-(int)age{
return _age;
}
@end;
测试:
#import"0605.h"
int main(){
@autoreleasepool {
Person* p= [[Person alloc]init];
[p setAge:1000];
NSLog(@"error:%d",[p age]);
[p setAge:30];
[p setName:@"nana"];
NSLog(@"_name=%@",[p name]);
}
}
代码结果 ;
类的接口
#import <Foundation/Foundation.h>
@interface User : NSObject
@property(nonatomic)NSString* name;
@property NSString* pass;
@property NSDate* birth;
@end
类的实现:
#import "0607.h"
@implementation User
@synthesize name=_name;
@synthesize pass;
@synthesize birth;
-(void)setName:(NSString*)name{
self->_name=[NSString stringWithFormat:@"+++%@",name];
}
@end
测试:
#import"0607.h"
int main(){
@autoreleasepool {
User* user=[[User alloc]init];
[user setName:@"nuonuo"];
[user setPass:@"12334"];
[user setBirth:[NSDate date]];
NSLog(@"%@,%@,%@",[user name],[user pass],[user birth]);
}
}
测试结果 :
五、简单的举例
这里我们通过一个简单的小例子,来认识一下OC和C的相似和不同
1.OC的实现
#import <Foundation/Foundation.h>
//#import就相当于C中的#include
//<Foundation/Foundation.h>也就类似于C中的<stdio.h>
int main(int argc,char* argv[]){
//主函数依旧是main函数
@autoreleasepool {
NSLog(@"Hello Object-C");
//NSLog函数就类似于C中的printf
}
return 0;
//返回方式也相同
}
第一行的2022-5-22 22:34:35.824506+0800是执行这段代码的时间
第一行的gy-c是 程序的名称
2569是 进程 的编号
91057是 线程 的编号
Hello Object-C 即为输出的内容
2.C的实现
#include <stdio.h>
int main() {
printf("Hello, Object-C\n");
return 0;
}
C这里只输出了需要输出的内容,不像OC输出了许多东西
我们也可以发现一些东西
1.输出的东西不太一样
2.OC会自动换行
3.OC和C的注释方法一样
总结
只是刚刚开始学,以后会继续补充。