iOS学习--类与对象(上)

本文介绍了OC编程语言的基本概念,包括NSString和NSLog的使用,类和对象的定义、对象的产生与使用,self关键词,id类型,以及方法的属性和可变参数。同时概述了单例模式的实现方法。
摘要由CSDN通过智能技术生成

学习属性大爆发!


前言

终于从算法进入到oc的学习了,以此为始,希望学有所获。


前置内容

在进入类和对象神圣的殿堂,先对oc的基础语法进行初步学习把。这是我学习oc的最大困惑之一了…

#include <Foundation/Foundation.h>

int main(int argc, const char * argv[]) { 
@autoreleasepool { 
	NSString* str = [[NSString alloc]init]; 
	NSLog(@"%@",str); } 
	}

先看这段代码。根据c语言中的基础,可以知道NSString应该是一个字符串指针类型,而NSLog则类似于printf函数一样。那么@又是什么呢?现在,我们来进入oc的语言。

@的使用

类似与上文,我们通常在字符串,数据输出时,看到@,但是@到底有什么用呢?

  1. 绝大部分关键词由@开头,例如:@interface @implementation @end
    @public @protected @private 。这些关键词的用法以后再详细说明!
  2. 可以将一个c的字符串转化为oc中的字符串对象NSString。这里,NSString是oc中专用的类表示字符串,可以直接实现储存c语言中""双引号括起来的字符。
//在c语言中,字符串是这样的
"this"
//在oc中,字符串是这样的
@"this"

NSLog输出函数。

包罗万象的NSLog输出函数!相较于c语言中的printf函数,具有更强大的包容性和便利性。比如自动换行

#include <Foundation/Foundation.h>

int main(int argc, const char * argv[]) { 
@autoreleasepool { 
	NSString* str = [[NSString alloc]init]; 
	int a = 6;
	NSLog(@"%@",str); //%@表示输出oc的对象
	NSLog(@"%d",a);
	} 
	}

回到上面的代码,不难发现NSLog的输出必须跟着@,这是OC中的语法规定。除此之外,语法上是跟c语言是一样的。但内容中多了一个%@的输出方式。具体作用呢我只知道用来输出OC中专门的对象,往后再继续补充吧。

往后再继续补充该前置内容把,目前还没遇到需要填补的内容。


一 类和对象

我们都知道,c语言是面向过程的语言,OC则是面向对象的语言。那这两者到底是什么意思呢?
我们以洗衣服举例:
如果是c语言,则关注过程 :拿盆,放衣服,放水,放洗衣粉,手搓,倒水,拧干,晾干。通过函数解决每一个过程的问题。
但如果是OC语言,则关注的是对象:人,衣服,洗衣粉,洗衣。通过这四个对象的交互,来完成这一问题。具体的体会,再深入OC的学习再做分享。而要实现四个对象的交互,就需要类和对象的使用了。

1.定义类

不被定义的人生,定义不出来的类。
类是某一批对象的抽象,可以把类理解成某成概念。比如说人类,球。那对象(也称为实例)则是具体存在的实体,比如说男人,女人,篮球,足球。
如何定义一个类?
OC中定义类分成两个步骤:

  1. 接口部分:定义该类包含的成员变量和方法。
  2. 实现部分:为该类的方法提供实现。
    简而言之呢,就是在.h文件中说明有哪些变量和方法,在.m文件中将方法具体实现。相较于c语言, .h文件就像是声明函数,变量。.m文件就像是函数的实现。然后主函数直接调用这些函数。
@interface Myclass : NSObject
{
    int 		_count;
    id			_data;
    NSString*	_name;
}
- (id)initWithString:(NSString*)aName;//方法声明,即函数,分号结束
@end

上面的代码,是.h文件中的接口部分,@interface声明定义类的接口部分,@end表示定义结束。定义了3个变量,一个方法。变量的定义与c语言中结构体相似。
值得我们注意的是,方法的定义。

 - (id)initWithString:(NSString*)aName;//方法声明,即函数,分号结束
  • 方法标识符:即前面的+ - 。+标识该方法是类方法,直接用类名调用,-代表该方法是实例方法,必须用对象才能调用。
  • 方法返回值类型:前面的(id)即是方法返回值类型,此处返回id类型,如果一个方法没有返回值,必须用(void)来声明。
  • 方法签名关键词:由方法名、形参标签和冒号组成。除了第一个形参之外,往后的每个形参都应该指定一个形参标签。此处则为(initWithString)。冒号后即应该传入的参数值。

完成了接口部分,下面来看看实现部分的代码:

@implementation Myclass 
{
    int 		_count;
    id			_data;
    NSString*	_name;
}
 - (id)initWithString:(NSString*)aName
{
  //方法体 
}
@end

实现部分的格式以@implementation开始,@end结束。完成函数内容代码。不过需要注意:

  • 类实现部分的类名必须与类接口部分的类名相同,用于表示这是同一个类的接口部分和实现部分。
  • 类实现部分也可以声明自己的成员变量,但这些成员变量只能在当前类内访问,因此,在类的实现部分声明的成员变量相当于定义隐藏的成员变量。
  • 类实现部分必须为类声明部分中的每一个方法提供方法定义。

此处,有个对隐藏的成员变量理解。在类的实现部分定义的成员变量,对于外界而言是隐藏的,是不能够被外界调用的,只有在实现部分内的函数可以调用。

2.对象的产生与使用

定义了类,接着就该使用类了。类的使用即需要对象。可以暂时简单将类理解成一个结构体,现在定义了一个结构体变量,则是对象。当然这个比喻非常不恰当。

Myclass* person = [[Myclass alloc] init];

其中alloc是OC中的一个关键字,该关键字负责分配内存空间,创建对象。此外,还需要init将该对象初始化,关于初始化往后会有更详细的解释,故此不再描述。因此创建对象的语法即为:

类名* 变量名 = [[类名* alloc] init ]

然后是对对象的使用。

//这是.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
    NSString* _name;
    int _age;
}
- (void) setName : (NSString*) name andAge: (int) age;
- (void) say: (NSString *) content;
@end

//这是.m
#import "Person.h"
@implementation Person
{
    int _testAttr;
}
- (void) setName : (NSString*) n andAge: (int) a
{
    _name = n;
    _age = a;
}
- (void) say: (NSString *) content
{
    NSLog(@"%@" , content);
}
@end

//这是主函数
#import <Foundation/Foundation.h>
#import "Preson.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];//对象的初始化
        [p say:@"hello, I love iOS"];//对象的使用
    }
}

上述代码就创建并调用了一个类。其中需要注意的点有

  • 对建立的类的引用,需要用“”而不是<>
  • 类的实现接口和主函数需要引用头文件。
  • 对对象的使用需要用[中括号]扩起来。

上述代码中,_testAttr就是在实现部分定义了一个隐藏的成员变量,该变量是外部无法调用的,只有在该类函数内部可用。


3.self关键词

self关键词总是指向该方法的调用者,当self出现在实例方法中,self代表调用该方法的对象。
如何理解这句话呢?我觉得跟复用这个概念很像。self的使用就是对传入这个函数的参数再次使用。即再次指向该函数的参数。下面我们以一段代码来加深理解:

#import <Foundation/Foundation.h>
@interface ReturnSelf : NSObject
{
    @public
    int _age;
}
- (ReturnSelf*) grow;
@end

@implementation ReturnSelf
- (ReturnSelf*) grow
{
    _age++;
    return self;
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ReturnSelf* rt = [[ReturnSelf alloc] init];
        [[[rt grow] grow] grow];
        NSLog(@"rt的_age成员变量的值是:%d",rt->_age);
    }
}

在该类中,我们定义了一个grow方法和age变量。在实现部分通过return self使每次返回的都是参数本身,从而实现了对参数的自加。
而self关键词的一大作用就是如此,返回传进来的参数本身。
而另一大作用,便是当局部变量隐藏了成员变量是,强制指定访问成员变量。

//.h
@interface Person : NSObject
{
    NSString* _name;
    int _age;
}
- (void) setName : (NSString*) _name andAge: (int) _age;
- (void) info;
@end

//.m
#import "Person"
@implementation Person
- (void) setName : (NSString*) _name andAge: (int) _age
{
    self->_name = _name;
    self->_age  = _age;
}
- (void) info
{
	NSLog(@"我的名字是%@,我的年龄是%d岁", _name, _age)
}

在这个类的实现接口中,将局部变量跟成员变量同名,此时局部变量隐藏了成员变量,用self即可强制指向成员变量。即指向.h中的变量。根据上方的代码仿写一下主函数体验一下对象的使用吧。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        [p setname:@"栗子" andAge"18"];
        [p info ];
    }
}

4.id类型

id 不仅仅是名字,在OC中专门提供了一个id类型。该类型可以代表所有对象的类型,也就是说,任意类的对象都可以赋值给id类型的变量。
如何实现的呢?通过id类型的变量调用方法时,并不是一开始就对该变量赋值,而是在运行时判断该对象所属的类,并在运行时确定需要动态调用对象的类。这就是动态绑定。云里雾里?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        id *p = [[Person alloc] init];//对象的初始化
        [p say:@"hello, I love iOS"];//对象的使用
    }
}

通过上面代码和对象的产生与使用中的代码对比,我们知道使用id类型变量,其实就是在运行时判断出id变量所指的对象实际类型为Perosn类型,因此将会 动态绑定到执行person对象的say方法。

二 方法详解

1.方法的所属性

体现:

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

这里要注意,如果方法不在类中定义,独立定义方法只能是函数。而标识符是区别该方法是类方法还是实例方法。

2.形参个数可变的方法

首先需要认识四个关键词

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

函数可以传入任意多个参数,这就是形参个数可变的方法。在定义方法时要增加逗号和三点
(, …)

//这是.h
@interface VarArge : NSObject
- (void)test:(NSString *) name , ...;
@end

//这是.m
#import "VarArgs,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

在.m函数中,实现形参个数可变有点像指针的用法。要指向第一个参数,然后处理第一个参数,再不断移动参数指向下一个参数。当参数为nil时,结束输入。

三 单例模式

如果程序多次创建该类的对象没有任何意义,还可能造成系统性能下降,此时程序仅需要保证该类只有一个实例。即单例模式。
单例类需要通过static全局变量来实现,程序需考虑定义一个static全局变量,用来保存已创建的Singleton,每次需要获取实例时,程序先判断该static全局变量是否为nil,如果该全局变量不为nil,则初始化一个实例并赋值给static全局变量。
简单理解就是加入一个变量,来指示当前实例是否为空。

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

//这是.m
#import "FKSingleton.h" 
static id instance = nil;
@implementation FKSingleton
+ (id) instance
{
	if (!instance)
	{
		instance =[[super alloc] init];
	{
	return instance;
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
		NSLog(@"%d",
			[FKSingleton instance] == [FKSingleton instance]);
    }
}

上述代码中,类实现接口的if语句通过控制instance的方法来获取FKSingleton实例,上述程序最多会产生一个SIngleton实例,从而实现了单例模式。

总结

本篇为iOS学习的开头,接下来会不断深入学习iOS,随着理解的深入不断修改本篇博客,并增加内容。
初步体验到了OC与c语言的不同,在往后的学习中,期待体会面向对象的编程语言的奇妙之处。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值