• Objective-c学习笔记02——类(面向对象)01


    本文首发在:http://www.simman.cc/article/2107,转载请注明出处!

    Objective-C可以开发apple家族系列产品的软件,这里记录成笔记方便日后复习:

    一、面向对象简介:

    面向对象程序设计(英语:Object-oriented programming,缩写:OOP),指一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的集合。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。[1]

    二、面向对象与面向过程

    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
    面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

    三、对象、类、方法

    请查阅:面向对象程序设计
    也可以看下图简单理解一下:

    四、Objective-c中的类

    1 OC类的结构

    扩展名 源类型
    .h 头文件。头文件包含类、类型、函数和常量声明,类的声明使用关键字@interface@end
    .m 实现文件。具有此扩展名的文件可以同时包含 Objective-C 代码和 C 代码。有时也称为源文件,类的实现使用关键字@implementation@end
    .mm 实现文件。具有此扩展名的实现文件,除了包含 Objective-C 代码和 C 代码以外,还可以包含 C++ 代码。仅当您实际引用您的 Objective-C 代码中的 C++ 类或功能时,才使用此扩展名。

    注意:.h中的方法只是做一个声明,并不对方法进行实现。也就是说,只是说明一下方法名、方法的返回值类型、方法接收的参数类型而已,并不会编写方法内部的代码。

    2 方法

    方法的声明和实现,都必须以 + 或者 – 开头

    * + 表示类方法(静态方法)
    * – 表示对象方法(动态方法)
    在.h中声明的所有方法作用域都是public类型,不能更改。

    类方法和实例方法的区别在于,类方法不能使用实例变量

    使用类方法主要原因有:

    1.类方法的使用不依赖于实例化一个对象,也就是说如果一个功能的实现不需要实例化对象,就可以用类方法来实现。
    2.类方法可以隐藏单例,将类方法和单例结合,可以在应用程序的任何地方访问静态实例,而无需使用指向对象的指针或保存他的实例变量。
    3.类方法和内存管理相关,分配一个NSArray,可以【NSArray alloc】init,也可以【NSArray array】,但是前者必须释放,而后者返回一个随时准备好自动释放的数组对象,并不需要你进行release操作

    四、创建一个类

    1 声明
    下图中的语法声明名为 MyClass 的类,它是从基础类(或根类)NSObject 继承而来的。(根类是供其他类直接或间接继承的类。)类声明以编译器指令 @interface 开始,以 @end 指令结束。类名称后面(以冒号分隔),是父类的名称。在 Objective-C 中,一个类只能有一个父类。

    在 @interface 指令和 @end 指令之间,编写属性和方法的声明。这些声明组成了类的公共接口。分号标记每个属性和方法声明的结尾。如果类具有与其公共接口相关的自定函数、常量或数据类型,请将它们的声明放在@interface …@end 块之外。

    注意:上面的成员变量命名有所不规范,我们通常会在前面加上 _ (下划线)以和其他变量或者方法区分,这里的成员变量作用域有三种类型,分别是@public,@protected,@private,如果没有指定类型,则默认为@protected.

    方法声明包含方法类型标识符、返回类型、一个或多个签名关键词,以及参数类型和名称信息。以下是 insertObject:atIndex: 实例方法的声明。

    一个方法的实际名称 (insertObject:atIndex:) 是包括冒号字符在内的所有签名关键词的串联。冒号字符表明有参数存在。在上述示例中,该方法采用两个参数。如果方法没有参数,则省略第一个(也是仅有的一个)签名关键词后面的冒号。
    声明如下的方法:

    1. - (void)setName:(NSString *)name :( int)age width:(int)width;

    则方法名为:setName::
    2 实现
    类实现的语法与类接口文件类似。它以 @implementation 编译器指令开始(接着是该类的名称),以 @end 指令结束。中间是方法实现。(函数实现应在 @implementation …@end 块之外。)一个实现应该总是将导入它的接口文件作为代码的第一行。

    1. #import "MyClass.h"
    2. @implementation MyClass
    3. - (id)initWithString:(NSString *)aName
    4. {
    5. // code goes here
    6. }
    7. + (MyClass *)myClassWithString:(NSString *)aName{
    8. // code goes here
    9. }
    10. @end

    对于包含对象的变量,Objective-C 既支持动态类型化,也支持静态类型化。静态类型化的变量,要在变量类型声明中包括类名称。动态类型化的变量,则要给对象使用类型 id。您会发现在某些情况下,会需要使用动态类型化的变量。例如,集 (collection) 对象,如数组,在它包含对象的类型未知的情况下,可能会使用动态类型化的变量。此类变量提供了极大的灵活性,也让 Objective-C 程序拥有了更强大的活力。

    下面的例子,展示了静态类型化和动态类型化的变量声明:

    1. MyClass *myObject1; // Static typing
    2. id myObject2; // Dynamic typing
    3. NSString *userName; // From Your First iOS App (static typing)

    请注意第一个声明中的星号 (*)。在 Objective-C 中,执行对象引用的只能是指针。如果您还不能完全理解这个要求,不用担心。并非一定要成为指针专家才能开始 Objective-C 编程。只需要记住,在静态类型化的对象的声明中,变量的名称前面应放置一个星号。id 类型意味着一个指针。

    四、实例化对象

    1. #import <Foundation/Foundation.h>
    2. #import "MyClass.h"
    3. int main(int argc, const char * argv[])
    4. {
    5. @autoreleasepool {
    6. MyClass * class = [MyClass alloc]; //调用Myclass类的静态方法alloc分配存储空间
    7. class = [class init]; //调用对象class的动态方法init进行初始化
    8. }
    9. return 0;
    10. }

    alloc:会返回分配好内存的Myclass对象,在等号左边用了一个指向Myclass类型的指针变量class来接收这个对象,注意class左边的*号。所有OC对象都是用指针变量来接收的,如果你不了解指针,你记住下面这点就行了:利用类名定义一个变量时,类名后面一定要带个*号。
    init:由于init是动态方法,所以这里使用stu变量来调用,并不是使用类名来调用。init会返回已经初始化完毕的对象,再次赋值给了stu变量。这时候的Student对象stu才能正常使用。
    当然你也可以写成如下两种方式:

    1. 1 Myclass * class = [[Myclass alloc] init];
    2. 2 Myclass * class = [Myclass new];

    还有一点需要明确一下,如果你创建一个新对象(入用alloc),就会在内存中为它保留足够的空间用于存储对象数据,这包括它的实例变量的空间,另外再多一些记录其他信息,还有一点就是使用init后示例变量会进行初始化,通常都会为0.

    五、释放对象(销毁对象)

    在Xcode4.2之前需要程序员进行手动释放对象,那么就要使用如下的代码进行:
    [class release] ,class是我们上面实例化的对象。

    当然现在Xcode已经更新到了5.0版本,则默认为启用ARC(全称:Automatic Reference Counting)机制,此时释放对象的任务就交由系统进行处理,如果你使用的是5.0版本,并且想手动释放对象的话,那么可以在Build Settings 里面找到Apple LLVM 5.0 – Language – Objective C的下面把Objective-C Automatic ReferenceCounting 的值设置为NO。

    否则你使用 release 的时候Xcode就会报如下错误:
    ‘release’ is unavailable: not available in automatic reference counting mode ARC forbids explicit message send of ‘release’

    四、”.”语法

    在说.语法之前,让我们来看看getter和setter方法,下面我们给MyClass类添加两个动态方法:

    1. - (int)age;
    2. - (void)setAge:(int)age;

    1、上面的age为getter方法,oc习惯把getter方法命名和实例变量名一样。
    2、上面的setAge为setter方法,同样习惯于使用set开头,并且后面也与实例变量名相同,而且首字母要大写。

    除了上面的方法,OC同时提供了使用.语法进行访问类方法,请看下面的代码

    1. //使用[]语法,访问age的getter方法、setter方法
    2. [class age];
    3. [class setAge:28];
    4. //使用.语法,访问age的getter方法
    5. class.age
    6. class.age = 28;

    这里要说明的是 class.age 等于 [class.age],class.age = 28 等于 [class setAge:28],.语法其实就是访问对象方法的getter和setter方法。至于到底使用哪个方法,取决于你怎么用。

    五、构造方法

    在Objective-c中,init就是默认的构造方法,它不接受任何参数。我们可以看到在Foundation框架里对init的如下声明:

    1. - (id)init;

    一般来说,我们编写的类可能会在初始化的时候进行对象变量的赋值,所以我们要重构构造方法,可以使用如下的方式:

    1. //声明
    2. - (id)initWithMyClass:(int)age;
    3. //实现
    4. - (id)initWithMyClass:(int)age {
    5. self = [super init];
    6. if(self){
    7. _age = age;
    8. }
    9. return self;
    10. }

    通过上面的代码我们可以看出,构造方法的返回值类型为id,现在可以把id理解为任何类型的类型。
    上面使用了 [super init],这是调用父类的init方法进行初始化,并返回赋值给当前对象self,接下来判断是否为真,如果是则对_age赋值,否则不做任何操作,最后返回当前对象self;

    如果我们重构了构造方法,那我们就可以使用如下方式进行创建对象,并初始化了。

    1. Myclass *class = [Myclass initWithMyClass:28];

    六、Self关键字

    Objective-c中的self代表当前方法的调用者,并且可以用在动态方法静态方法中。

    1. - (void)test1(){
    2. [self test2];
    3. }
    4. + (void)test2(){
    5. [Myclass test1];
    6. [self test1];
    7. }

    通过上面的代码我们可以看到不管是在动态方法和静态方法中,都可以使用self关键字调用其他方法,并且等于 [Myclass test1]这种方式。

    如果细分一下的话,可以理解为:
    静态方法:self代表这个类。
    动态方法:self代表这个对象。

    六、空指针与野指针

    1、空指针
    没有存储任何内存地址的指针就称为空指针(NULL指针)
    空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。
    下面两个都是空指针:

    1. Student *s1 = NULL;
    2. Student *s2 = nil;

    2、野指针
    “野指针”不是NULL指针,是指向”垃圾”内存(不可用内存)的指针
    3、示例

    1. Myclass *class = [[Myclass alloc] init];
    2. class.age = 28;
    3. class.release;
    4. class.age = 20;

    如果我们run如上代码会报 class.age = 20;这行的错误,因为上一行已经relase了,这是对象指向的内存地址不可用,也就成了野指针。
    当然,如果我们给空指针发消息,则不会报错。

    七、@property和@synthesize

    在说@property和@synthesize之前我们先写一段代码:

    1. - (int)age;
    2. - (void)setAge:(int)age;

    上面的代码是age的setter和getter方法声明的简单访问器,其中的setter中还涉及到一定的内存管理,既然这个技术这么重要,那么有没有一种更方便的方法去做呢?答案就是@property和@synthesize。它们是Objective-C 2.0加入的指令,前者用于声明,后者用于合成访问器,结合使用就可以自动生成访问器了。

    下面我们使用@property和@synthesize来新建一个访问器:

    1. //声明方法
    2. @property (nonatomic, copy) NSString *name;
    3. //实现方法
    4. @synthesize stuName = _stuName;

    使用@property和@synthesize很方便,但又给我们带来了很多疑问比如在上面的代码中又出现了nonatomic和copy,是什么意 思?在@property中还有其他几个关键字,它们都是有特殊作用的,我把它们分为三类分别是:原子性,访问器控制,内存管理。

    原子性
    atomic(默认):atomic意为操作是原子的,意味着只有一个线程访问实例变量。atomic是线程安全的至少在当前的访器上我是安全的。它是一个默认的,但是很少使用。它的比较慢,这跟ARM平台和内部锁机制有关。
    nonatomic: nonatomic跟atomic刚好相反。表示非原子的,可以被多个线程访问。它的速度比atomic快。但不能保证在多线程环境下的安全性,在单线程和明确只有一个线程访问的情况下广泛使用。
    访问器控制
    readwrite(默认):readwrite是默认的,表示同时拥有setter和getter。
    readonly:readonly 表示只有getter没有setter。
    有时候为了语意更明确可能需要自定义访问器的名字:

    1. @property (nonatomic, setter = mySetter:,getter = myGetter ) NSString *name;

    最常见的是BOOL类型,比如标识View是否隐藏的属性hidden。可以这样声明

    1. @property (nonatomic,getter = isHidden ) BOOL hidden;

    要注意修改setter或者getter的名字是存在副作用的,可能会使KVC和KVO无法正常工作。
    内存管理
    retain:使用了retain意味着实例变量要获取传入参数的所有权。具体表现在setter中对实例变量先release然后将参数 retain之后传给它。下面这段代码展示了retain类似的行为:

    1. -(void)setStuName:(NSString *)stuName{
    2. if (_stuName != stuName)
    3. {
    4. [_stuName release];
    5. _stuName = [stuName retain];
    6. }
    7. }

    assign(默认):用于值类型,如int、float、double和NSInteger,CGFloat等表示单纯的复制。还包括不存在所有权关系的对象,比如常见的delegate。
    strong:是在ARC伴随IOS引入的时候引入的关键字是retain的一个可选的替代。表示实例变量对传入的参数要有所有权关系即强引用。strong跟retain的意思相同并产生相同的代码,但是语意上更好更能体现对象的关系。
    weak: weak跟assign的效果相似,不同的是weak在对象被回收之后自动设置为nil。而且weak智能用在iOS 5或以后的版本,对于之前的版本,使用unsafe_unretained。
    unsafe_unretained:weak的低版本替代。
    copy:copy是为是实例变量保留一个自己的副本。
    现在明白了@property是怎么回事了,但是@synthesize是怎么回事,看看之前的第一段代码:

    1. @synthesize stuName = _stuName;

    这里的stuName = _stuName是什么意思?stuName是propertyName跟@property声明的名字一样。而后面的_stuName 是实例变量名。生成的访问器就是来访问的 _stuName的。代码的样子就和最开始那setter和getter代码所描述的一样。

    注意一个问题,我们并没有声明_stuName这个变量,这是编译器自动帮我们创建的。 如果这段指令我换个写法:@synthesize stuName = a; 并且我们没有在interface里面声明这个变量,那么会自动创建一个变量a。

    如果这里写成这样:

    1. @synthesize stuName;
    2. //等同于
    3. @synthesize stuName = stuName;

    在Xcode4.4中,Xcode添加的一些新的编译特性。其中一个就是默认合成(Default Synthesis)。默认合成就不再需要显示的使用@synthesize指令了,这很方便但是要注意的是,默认合成遵守的约定,这里的也就是命名规则是propertyName = _propertyName。

    1. /对于下面的@propety
    2. @property (nonatomic, copy) NSString *stuName;
    3. //默认合成的规则是这样:
    4. @synthesize stuName = _stuName;

    因为@synthesize是默认合成,所以 @synthesize stuName = _stuName;则可以省略。

    此条目发表在objective-c分类目录,贴了标签。将固定链接加入收藏夹
  • 相关阅读:
    html例题——简历
    求值
    c#语句实例(排大小)
    3.6语言基础笔记
    2016.3.5进制间的转换
    3.26-1
    3.23(网页)
    3.23
    3.22
    3.20
  • 原文地址:https://www.cnblogs.com/simman/p/3377968.html
Copyright © 2020-2023  润新知