【OC】OC初体验——类和对象、方法详解、成员变量

前言:

该博客概念理论较多,编写了OC面向对象中的部分内容,具体的代码实现在后续会陆续添加~

一、类和对象

  类是面向对象的重要内容,可以把类当成一种自定义数据类型,可以使用类来定义变量,这种类型的变量相当于指针类型的变量。也就是说,所有的类都是指针类型的变量。
面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也称为实例,
instance),其中类是某一批对象的抽象,可以把类理解成某种概念;对象才是一个具体存在的实体。例如,日常所说的人其实都是人的实例,而不是人类。
定义接口部分的语法

1.定义类

Objective-C 中定义类需要分为两个步骤:

  • 定义接口部分:定义该类包含的成员变量和方法。
  • 定义实现部分:为该类的方法提供实现。

定义接口部分

定义接口部分的语法如图:
在这里插入图片描述

  在上面的语法格式中,@interface 用于声明定义类的接口部分,@end 表明定义结束。其中紧跟该类的一对花括号用于声明该类的成员变量;花括号后面的部分用于声明该类的方法
  其中,成员变量,用于描述该类的对象的状态数据;方法,用于描述该类的行为。我们一般将定义类的接口声明部分放在头文件中,所以定义类接口部分的源代码应该命名为*.h 文件
  其中,类型,可以是 Objective-C 语言允许的任何数据类型,包括基本类型、构造类型和各种指针类型。成员变量名,成员变量名只要是一个合法的标识符即可。我们一般默认成员变量名应该由一个或多个有意义的单词连缀而成,第一个单词首字母小写,后面每个单词首字母大写,其他字母全部小写,单词与单词之间不需要使用任何分隔符,建议并习惯成员变量名以下画线(_)开头。

  下面对定义接口的方法声明语法做详细解释。
在这里插入图片描述

  方法类型标识符:该标识符要么是+,要么是-,其中,+代表该方法是类方法,直接用类名即可调用;-代表该方法是实例方法,必须用对象才能调用。
  方法返回值类型:返回值类型可以是 Objective-C 允许的任何数据类型,包括基本类型,构造类型和各种指针类型。如果声明了方法返回值类型,则方法体内必须有一个有效
的 return 语句,该语句返回一个变量或一个表达式,这个变量或者表达式的类型必须与此处声明的类型匹配。除此之外,如果一个方法没有返回值,则必须使用 void 来声明没有返回值。
  方法签名关键字:由方法名、形参标签和冒号组成。方法命名规则与成员变量命名规则基本相同,但不需要以下画线开头,通常建议方法名以英文动词开头。除第一个形参外,后面的每个形参都指定一个“形参标签”(用于说明该形参的作用)。一旦在定义方法时指定了形参列表,调用该方法时就必须传入对应的参数值——谁调用方法,谁负责为形参赋值。
  在 Objective-C 的方法声明中,所有的类型(包括 void)都应该使用圆括号括起来

注:类的接口部分只是声明方法,并没有为方法提供方法体,因此需要在方法声明后添加一个分号,用于表示方法声明结束。

定义实现部分

定义实现部分的语法格式如图:
在这里插入图片描述
  类实现部分的类名必须与类接口部分的类名相同,用于表示这是同一个类的接口部分和实现部分。类实现部分也可以在类名后使用“:父类”来表示继承了某个父类。
  类实现部分也可声明自己的成员变量,但这些成员变量只能在当前类内访问。因此,在类实现部分声明成员变量相当于定义隐藏的成员变量。
  类实现部分通常命名为*.m文件)必须为类声明部分的每个方法提供方法定义。方法定义由方法签名(不要在后面用分号)和方法体组成;实现部分除了实现类接口部分定义的方法之外,也可提供额外的方法定义——这些没有在接口部分定义,只是在实现部分定义的方法,将只能在类实现部分使用。

程序示例:

//该代码块定义了一个start类的接口部分
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface start : NSObject
{
    //下面定义了两个成员变量
    NSString* _name;
    int _age;
}
//定义了一个setName:andAge:方法
- (void) setName:(NSString*) name andAge : (int) age;
//定义了一个say:方法,并不提供实现
- (void) say: (NSString *) content;
//定义了一个不带行参的info方法
- (NSString*) info;
//定义了一个类方法
+ (void) foo;
@end

NS_ASSUME_NONNULL_END
//该代码块定义了start的实现部分
#import "start.h"

@implementation start
{
    //定义了一个只能在实现部分使用的成员变量(被隐藏的成员变量,无法从外部访问)
    int _testAttr;
}
//定义了一个setName:andAge:方法
- (void) setName: (NSString*) n andAge:(int)a
{
    _name = n;
    _age = a;
}
//定义了一个say:方法
- (void) say: (NSString *) content
{
    NSLog(@"%@", content);
}
//定义了一个不带行参的info方法
- (NSString*) info
{
    [self test];
    return [NSString stringWithFormat:@"我是一个人,名字为%@,年龄为%d。" , _name , _age];
}
- (void) test
{
    NSLog(@"——只在实现部分定义的test方法——");
}
//定义了一个类方法
+ (void) foo
{
    NSLog(@"start类的类方法,通过类名调用");
}

@end

2.对象的产生和使用

  定义类之后,接下来就可以使用该类了,可以从如下三方面来使用类:定义变量,创建对象,调用类方法
  定义变量的语法格式为:

类名*变量名;

  创建对象的语法格式为:

[[类名 alloc]初始化方法]

其中,alloc 是关键字,该关键字负责为该类分配内存空间、创建对象。除此之外,还需要调用初始化方法对该实例执行初始化。由于所有的对象都继承了NSObject 类,因此所有的类都有一个默认的初始化方法:init。
  无论是调用类方法,还是调用实例方法,都可用“猪八戒吃西瓜”这样的语句进行类比,这个句子可分为主语:猪八戒;谓语:吃;宾语:西瓜。如果把该语句转换为Objective-C 的写法,则可写成:

[猪八戒 吃:西瓜]

其中,猪八戒作为主语,也是方法调用者,有时本书会将它简称为“主调” (主语调用者的简称);吃作为谓语,也就是面向对象中的方法;西瓜作为宾语,则是面向对象中调用方法时传入的参数
  由此可总结出Objective-C 调用方法的语法格式为:

[调用者方法名:参数形参标签:参数值…]

如果方法声明中声明了多个形参,那么调用该方法时需要为每个形参传入相应的参数值。

3.对象和指针

  从 FKPerson 类定义来看,FKPerson 对象应包含三个成员变量(两个可以暴露的成员变量和一个被隐藏的成员变量),而成员变量是需要内存来存储的。因此,当创建 FKPerson 对象时,必须要有对应的内存来存储 FKPerson 对象的成员变量。
FKPerson 对象在内存中的存储示意图:
在这里插入图片描述
  由图可知,FKPerson 对象由多块内存组成,不同的内存块分别存储了 FKPerson对象不同的成员变量。由于 FKPerson类型的变量本质上就是一个指针变量,person 变量仅仅保存了 FKPerson 对象在内存中的首地址。形象地说,可认为FKPerson类型的变量指向实际的对象。从本质上说,类也是一种指针类型的变量,因此,程序中定义的 FKPerson*类型只是存放一个地址值,它被保存在该 main()函数的动态存储区,它指向实际的 FKPerson 对象,而真正的 FKPerson 对象则存放在堆(heap)内存中。
  将 FKPerson 对象赋给指针变量的示意图:在这里插入图片描述

  main()函数的动态存储区保存的指针变量并未真正存储对象里的成员变量数据,而指针变量仅仅是指向该对象所有的对象都只能通过指针变量来访问它们
  person 指针变量本身只存储了一个地址值,并未包含任何实际的数据,但它指向实际的 FKPerson 对象,当调用 person 指针变量的成员变量和方法时,实际上是访问person 所指向对象的成员变量和方法。
  堆内存里的对象可以有多个指针,即多个指针变量指向同一个对象,如果堆内存里的对象没有任何变量指向该对象,那么程序将无法再访问该对象。使用完后应释放该对象所占用的内存,否则就会适成内存泄漏

4.self关键字

  self 关键字总是指向该方法的调用者(对象或类),当 self 出现在实例方法中时,self 代表调用该方法的对象;当 self 出现在类方法中时,self 代表调用该方法的类。
  self 关键字最大的作用是让类中的一个方法访问该类的另一个方法或成员变量
  self 总是代表当前方法的调用者,当这个方法被调用时,它所代表的对象才被确定下来:谁在调用该方法,self 就代表谁。如果 self 出现在实例方法中,self 就代表调用该方法的对象;如果 self 出现在类方法中,self 就代表调用该方法的类本身。

5.id类型

  OC提供了一个id类型,这个id类型可以代表所有对象的类型。也就是说所有实例都可以赋给id类型的变量。
  当通过id类型的变量来调用方法时,OC将会执行动态绑定。即OC将会在运行时判断该对象所属的类以及确定需要动态调用的方法,而非在编译时确定要调用的方法。

二、方法详解

  方法是类或对象的行为特征的抽象,也是类或对象最重要的组成部分。OC 的方法不能独立存在,所有的方法都必须定义在类里。方法在逻辑上要么属于类,要么属于对象。

1.方法的所属性

OC中方法的所属性主要体现在以下几方面:

  • 方法不能独立定义,只能在类体里定义。
  • 从逻辑意义上看,方法要么属于该类本身,要么属于该类的一个对象。
  • 不能独立调用方法,调用方法需要使用类或对象作为调用者。

使用+标识符的方法属于这个类本身,因此只能用类作为调用者来调用;使用-标识符的方
法属于该类的实例,因此必须用实例作为调用者来调用。

2.形参个数可变的方法

  如果在定义方法时,在最后一个形参名后增加逗号和三点(,…),则表明该形参可以接受多个参数值。下面程序定义了一个形参个数可变的方法。

#import <Foundation/Foundation.h>

@interface VarArgs : NSObject
//定义行参个数可变的方法
- (void)test:(NSString *) name, ...;
@end

  在上面程序中,test:方法声明了一个 NSString类型的形参,这个形参除了指定 name 参数之外,还带,….,这表明该方法还可接受个数可变的 NSString参数。
  为了在程序中获取个数可变的形参,需要使用如下关键字。

  • va_list:这是一个类型,用于定义指向可变参数列表的指针变量。
  • va_start:这是一个函数,该函数指定开始处理可变形参的列表,并让指针变量指向可变形参列表的第一个参数。
  • va_end:结束处理可变形参,释放指针变量。
  • va arg:该函数返回获取指针当前指向的参数的值,并将指针移动到指向下一个参数。

三、成员变量

OC中根据定义变量的位置不同,将变量分为三大类:成员变量,局部变量,全局变量。

1.成员变量

  成员变量是指在类接口部分或类实现部分定义的变量。OC的成员变量都是实例变量,并不支持真正的类变量。
  实例变量从实例被创建开始存在,直至系统完全销毁这个实例。实例变量可理解为实例成员变量,它作为实例的一个成员,与实例共存亡。
  只要实例存在,程序就可以访问该实例的实例变量,在程序中访问实例变量使用如下语法:

实例->实例变量

2.模拟类变量

  通过内部全局变量来模拟类变量static修饰局部变量,表示将该局部变量存储到静态存储区,修饰全局变量用于限制全局变量只能在当前源文件访问。
  为了模拟类变量,我们在实现部分定义一个static修饰的全局变量,并提供一个类方法来暴露全局变量。

3.单例(Singleton)模式

  如果一个类始终只能创建一个实例,则这个类被称为单例子类。单例类可通过static全局变量来实现,程序考虑定义一个static全局变量,该变量用于保存已创建的Singleton对象每次程序获取该实例时,程序会先判断static全局变量是否为nil,如果为nil则初始化一个实例并赋值给全局变量
  下面是FKSingleton接口部分:

#import <Foundation/Foundation.h>
@interface FKSingleton : NSObject
+(id) instance;
@end

  在上面程序中,FKSingleton 类的接口部分声明了一个 instance 类方法,允许程序通过该类方法来获取该类的唯一实例。在 FKSingleton 类的实现部分将会定义一个 static 全局变量,并通过该全局变量来缓存已有的实例,然后实现 instance 类方法,在该类方法中控制 FKSingleton 类最多只会产生一个实例。
  下面是 FKSingleton 类的实现部分。

#import "FKSingleton.h"

@implementation FKSingleton
static id instance = nil;
+(id) instance
{
   // 如果 instance 全局变量为nil
   if(!instance)
   {
       //创建一个 Singleton 实例,并将该实例赋给 instance 全局变量
       instance =[[super alloc] init];
   }
   return instance;
}
@end
int main(int argc, char * argv[])
{
    @autoreleasepool{
        //判断两次获取的实例是否相等,程序将会返回 1(代表真)
        NSLog(@"*d" , [FKsingleton instance] =- [FKSingleton instance));
        }
}

  上面程序中粗体字代码用于控制通过 instance 类方法来获取 FKSingleton 实例时,程序最多只会产生一个 Singleton 实例。接下来在 main()函数中测试这个 FKSingleton 类,将可以看到两次产生的 FKSingleton 对象实际上是同一个对象。

总结

1.面向对象设计的三大特征:封装性、多态性、继承性
2.类和对象的声明和实现:类是指相同事物的一个抽象(相同事物的属性\行为);对象是满足类特征的一个具体的个体。
3.类的声明:类的声明使用了关键字 @interface 和 @end
@interface 类名 : NSObject ——声明实例变量、成员变量、属性,注意:实例变量名一般以下划线开头(数据类型 _实例变量名;)。
4.对象的创建:语法:[类名 new]此对象的创建执行了三个操作:在内存的堆区开辟了存储空间;对实例变量完成了初始化工作;返回开辟的堆存储空间的首地址。
5.方法声明实现及调用:OC中的方法分为:对象方法和类方法。
对象方法以-开头,如 -(void)xx,只能由对象来调用,对象方法中可以访问当前对象的成员变量,其调用格式 [对象名 对象方法名]。
类方法以+开头,如+(void)xx,只能由类来调用,类方法中不能访问实例(成员)变量,因为类方法由类来调用,并没有创建存储空间来存储类中的成员变量,其调用格式:[类名 类方法名]。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
OC(Objective-C)是一种面向对象的编程语言,它支持在类中使用方法和block。类方法是在类中定义的方法,可以直接通过类名来调用,而不需要实例化对象。而block是一种闭包,可以在代码中定义并传递给方法或函数,用于延迟执行特定的逻辑。 在OC中,可以使用类方法来创建和操作类的实例,例如通过一个工厂方法创建对象,或者在类方法中实现一些与类相关的逻辑。类方法通常使用“+”符号进行声明和实现。 而block可以在方法中作为参数传递,也可以在方法中定义和使用。block可以捕获其所在作用域的变量,可以在方法内部延迟执行一段代码,也可以用于实现回调等功能。block的定义和使用使用“^(){}”语法。 类方法和block可以结合使用,例如可以在类方法中接受一个block作为参数,并在合适的时机调用该block,以实现一些灵活的逻辑。通过类方法和block的组合,可以在OC中实现更加灵活和强大的功能,例如在异步操作中使用block来回调结果,或者在工厂方法中使用block来定制对象始化逻辑等。 总而言之,类方法和block是OC中的两个重要特性,它们可以分别用于类的操作和延迟执行逻辑,也可以结合使用以实现更加灵活的功能。在实际的OC开发中,类方法和block通常会被广泛使用,可以帮助开发者更加简洁和灵活地实现代码逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值