当我们学OC的时候,我们在学什么

前言

  • 昨天给大一的同学分享了波OC基础知识,自己也是趁此机会将整个OC基础融会贯通地梳理了一遍,其实还挺爽的
  • OC是一门面向对象的语言,如果在此之前只学习过C语言的话,确实会比较困难,从面向过程转换到类与对象需要认真的思考学习,如果仅仅是照着网课以及书上copy代码可能不会有什么作用,重要的是转变你的思维
  • OC的基础一般就是类与对象以及Foundation框架的部分
  • 那么对于一个iOS工程师来说,是不是要能把所有方法背下来才算懂了呢?显然不是的,方法那些在遇到问题的时候会查,能解决就行
  • 因此这篇文章主要是主要讲类与对象
  • 关于Foundation框架的基础知识,我推荐OC 知识:Foundation 框架及相关类详尽总结
  • 本文比较适合学习OC有一段时间,想梳理一下脉络的同学,且我是根据目前已经学了一年多的iOS开发,回过来看前面基础知识写成的文章,讲的部分都是我觉得比较重要的,可能不够全面

类与对象

创建一个类

  • 我们在Xcode上创建名叫Person,继承于NSObject的类
  • 此时,会自动生成.h以及.m文件,我们看下这里面都有些什么东西
//.h文件
#import <Foundation/Foundation.h>	//引入头文件,与C语言类似

NS_ASSUME_NONNULL_BEGIN	//NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END。在这两个宏之间的代码,所有简单指针对象都被假定为nonnull

@interface Person : NSObject	//@inetrface 类名:父类名
/*
	这一块在@interface与@end之间的称之为接口部分
*/
@end

NS_ASSUME_NONNULL_END

//.m文件
#import "Person.h"	//引入头文件,与C语言类似

@implementation Person  
/*
	这一块在@implementation以及@end之间称之为实现部分
*/
@end

接口部分

什么是接口
  • 接口部分可以近似理解为C语言的声明部分,声明该类的成员变量,属性以及方法,在.m里的@implementation部分去实现它
  • 但首先注意到,我们在#import引入文件的时候只会引入.h文件,因此接口部分就是我们提供给外界使用这个类时提供的一个窗子
  • 什么是窗子?你可以看到里面有什么,但你不会知道(也没必要知道)它的具体实现过程是什么
我们在接口里放什么
  • 应该尽可能将成员变量隐藏,将方法暴露出去
  • 一个好的iOS工程师应该尽可能模仿Apple公司的风格
  • Apple给我们提供了大量的框架供我们调用,我们能看到的都是.h文件,是不可能让你知道某个方法的具体实现过程(部分开源的除外)
  • 此外其实在.m文件中也可以写接口,我们称之为扩展类(class extension)
#import "Person.h"

@interface Person ()

/*
	在这里同样可以写属性,但只有@implementation中可以调用
*/

@end

@implementation Person

@end
为什么这么做
  • 举一个很简单的例子,假如我们写的是一个学生管理系统,我们需要写一个学生类,给他加上三个属性(比如姓名,年纪,成绩)
  • 如果我们把所有属性暴露的话,我们将可以输入任意数据
  • 但如果是写一个规定好的录入方法,让我们输入不合法(比如输入成绩小于0)的时候,可以打印报错等等

实现部分

  • 在@implementation Person以及@end之间写的就是实现部分,实现接口部分的内容,完善丰富的具体实现等等
  • 实现部分是外界调用时无需知道的部分

成员变量&&属性

一句话总结

  • 属性就是实现了getter,setter方法,加上了属性关键字的成员变量

getter以及setter方法

  • 其实一切都是环环相扣的,为什么要有setter方法以及getter方法,也就是为了外部好调用
  • 假如在上面这个Person类里面我们写一个student成员变量,在@implementation Person实现setter以及getter方法
//.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

- (void)setNameStr:(NSString *)nameStr;	//setter方法应该是set+成员变量名(去掉下划线)
- (NSString *)nameStr;	//getter方法应该直接用成员变量名(去掉下划线)

@end

NS_ASSUME_NONNULL_END

//.m文件
#import "Person.h"

@interface Person () {
    @private
    NSString *_nameStr;	//成员变量应该以下划线开头
}



@end

@implementation Person


- (void)setNameStr:(NSString *)nameStr {
    _nameStr = nameStr;
}

- (NSString *)nameStr {
    return _nameStr;
}

@end
  • 这涉及到了OC的命名方法,驼峰命名法,具体可以看大驼峰命名法和小驼峰命名法
  • 现在我们将成员变量变为私有的,而将对应的setter,getter方法暴露出去供外界调用,达到封装的目的

点语法

  • 点语法本质上就是调用的setter,getter方法
一个错误的例子
  • 假设我设置nameStr时使用的属性,而我又想重写set方法
#import "Person.h"

@interface Person ()

@property (nonatomic, copy) NSString *nameStr;	//(nonatomic, copy)这里写的是属性关键字,也是属性的特殊点之一,对于萌新来说,理解该用什么关键字看呢比较困难,所以我建议萌新直接忽略这一部分,不写关键字,直接这么写@property NSString *nameStr;这样写不是说属性关键字可有可无,而是当你不写时会补上默认关键字,希望了解关键字,请自行百度

@end

@implementation Person

- (void)setNameStr:(NSString *)nameStr {
    self.nameStr = nameStr;	//这里的点语法本质上就调用了setter方法
}

@end
  • 上面这段话可以通过编译,但当你实际使用的时候,一旦你写了self.nameStr进行操作就会直接报错,因为这等于就是一个没有出口的递归
  • 同样,调用nameStr可以使用self.nameStr也可以使用下划线+nameStr,正如上面所说,属性只是自动实现了setter方法,getter方法的成员变量,而成员变量的命名依然是下划线+nameStr,这样你可能对于为什么命名时要加下划线有了新的理解,一切都是为了统一

init,self,super

init方法

  • 首先,虽然OC同样提供new方法,但我们依然应该使用[[Person alloc] init]方法去进行初始化,alloc进行分配空间,init进行初始化,非常合理
  • init方法是非常常用的方法,不管是什么类都必须要初始化才能使用,为了方便我们个性化的使用,就算是系统类,我们往往也希望能够定制化,因此重写init方法是非常常见的操作

重写init方法

  • 打开Xcode,到我们的Person类里的.m@implementation部分,打上init四个字母,Xcode就会有自动补全
#import "Person.h"

@interface Person ()

@property (nonatomic, copy) NSString *nameStr;

@end

@implementation Person

- (instancetype)init
{
    self = [super init];	//super关键字代表调用父类方法,在这里super就是我们创建Person时,选择的父类NSObject
  //也就是说NSObject有一个init方法,在父类里已经在.h里的接口部分申明了,因此我们直接重写就完事了,没必要再申明
    if (self) {	//self就是初始化的对象本身
		
    }
    return self;	//最后将其返回
}

@end
  • init方法的返回值是instancetype,它和id类似,表示任意,但是instancetype只能做返回值,具体差异请百度

改写init方法的例子

  • 给Person类写三个属性,nameStr, ageInt, scoreInt三个属性,我希望在我init初始化的时候,就把相应的值赋给对应的属性
  • 我们就可以自己写一个init方法
#import "Person.h"

@interface Person ()

@property (nonatomic, copy) NSString *nameStr;
@property (nonatomic, assign) NSInteger ageInt;
@property (nonatomic, assign) NSInteger scoreInt;

@end

@implementation Person

- (instancetype)initWithName:(NSString *)nameStr age:(NSInteger)ageInt score:(NSInteger)scoreInt
{
    self = [super init];
    if (self) {
        _nameStr = nameStr;
        _ageInt = ageInt;
        _scoreInt = scoreInt;
    }
    return self;
}

@end
  • 这样子写完之后,在把该方法申明在.h的接口部分,我们等同于写了一个新的方法,但它同样会返回self,完成我们的初始化操作
  • 我们在调用这个方法的时候可以直接给初始化对象把三个属性赋好了,easy+happy

扩展

  • 另一个对于萌新来说很重要,同时不太好理解的点就是OC的深拷贝,浅拷贝问题,但这个很重要,推荐看这篇文章详解iOS开发中复制对象
  • 此外,你可以已经看了很多OC的知识点了,但如果对于上文还是不理解,请再把类与对象,继承等等认真研究一遍
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值