Objective-C知识点杂集二

2018-09-06  本文已影响0人  拧发条鸟xds

第三部分


向对象发送消息时,如何选择正确的方法。

首先,检查对象所属的类,查看在类中是否明确定义了一个具有指定名称的方法。如果有的话,就使用这个方法;如果没有,就寻找它的父类是否有这个方法,如果还没有,就一直寻找其父类。


#import与@class指令的区别

iOS---Objective-C中@class与#import的区别

iOS @class 和#import“”引用

#import <Foundation/Foundation.h>

@class XYPoint;

@interface Kaobei : NSObject

@property (strong,nonatomic) XYPoint *origin;

- (XYPoint *)origin;
- (void)setOrigin:(XYPoint *)pt;

@end

多态

多态的定义:不同的类共享相同方法名称的能力成为多态。就是虽然不同的类里面的方法名是相同的,但是系统在运行时,会先确定对象是哪一个类,再去调用类的方法。

id是一个通用的对象类型。也就是说,id可以用来存储属于任何类的对象。id后面的类没有星号。OC系统总是跟踪对象所属的类。动态类型和动态绑定:就是说,先判定对象所属的类,然后在运行时确定需要动态调用的方法,而不是在编译的时候。

id a;//动态类型
Fraction *a;//静态类型

@try处理异常

@try{
    
}
@catch(NSException *e){
    
}
@finally{
    
}

重载类的初始化方法

如果希望类对象在初始化的时候做一些事情,比如给实例变量赋值。我们可以重写init方法。也可以另写一个以init开头的初始化方法。

- (instancetype)init{
    
    self = [super init];
    if(self){
        //初始化代码
    }
    return self;
}

初始化代码解释:

  1. 这个方法首先会调用父类的初始化方法。执行父类的初始化方法,可以使继承的实例变量正常初始化

  2. 必须将父类init方法的执行结果赋值给self(self = [super init];),因为初始化过程改变了对象在内存中的位置(意味着引用将要改变)。

  3. 如果父类的初始化成功,那么返回的值是非空的,通过if语句可以验证。注释说明可以在这个代码块的位置放入自定义的初始化代码。通常可以在这个位置初始化实例变量。


关于属性、存取方法和实例变量以及@synthesize

(《OC程序设计》P203)

目前的趋势是使用下划线(_)作为实例变量的起始字符。

@synthesize window = _window;

@synthesize window = _window;表明合成(synthesize)属性window的取值方法和设置方法,并将属性与实例变量(_window)关联起来。这对区分属性和实例变量是有区别的。鼓励通过摄者方法来设置实例变量的值,通过取值方法来获取实例变量。

[window makeKeyAndVisible];//无法运行

无法运行,因为没有实例变量叫做window。

[_window makeKeyAndVisible];//可以运行

正常运行,或者使用点方法:

[self.window makeKeyAndVisible];//可以运行

在实现部分(@implementation)显式的声明实例变量(或者使用@synthesize指令隐形声明的实例变量)是私有的,这就意味着并不能在子类中通过名字直接获取到实例变量。在子类中,只能使用继承的存取方法获取实例变量的值。

基于它们的属性(我理解为基于属性的修饰符),合成方法(@synthesize的方法?)还能做其它的事情,例如,管理内存、复制值等,给实例变量赋值或从实例变量取值就不会做这些事情。这是属性和实例变量的另一个抽象,这种抽象使得当存取实例变量时系统有机会做其它工作。


全局变量、外部全局变量和静态变量

全局变量、外部全局变量
int gGlobalVar = 0;

上面定义了一个全局变量,可以在这个文件的任何位置使用它。

其实这样定义一个全局变量,在其它文件中也可以使用它。所以,上面实际上定义的是一个外部全局变量

想要在其它文件中是有上面的全局变量,需要在其它文件中这样定义:

extern int gGlobalVar;

这样就能够在其它文件中使用最上面定义的全局变量了。

注意,不能写作 extern int gGlobalVar = 0; 因为使用关键字extern表明这条语句是变量的声明,而不是定义。记住,声明不会引起变量存储空间的分配,二定义会引起变量存储空间的分配。

static关键字

当我们不希望其它文件使用全局变量时,要使用static关键字。

static int gGlobalVar;

这样,在这条语句之后的方法或函数都能够使用这个变量,而其他文件是不能够使用这个变量的。

我们知道,类方法是不能够访问实例变量的,但有时候又希望类方法可以设定和访问一些变量。所以我们可以声明一些静态变量(外界无法访问,保证了封闭性)。


第四部分


枚举数据类型

枚举类型的定义规则:以enmu开头;之后是枚举类型数据的名称;然后是标识符序列,定义了所有允许给这个类型指派的值(包含在一对花括号内)。

理论上,枚举类型只能赋的值只能为花括号内的标识符序列,但就算违背了这个规则,OC编译器也不会发出警告消息。

  1. 定义一个枚举类型flag:
enmu flag { false , true };
  1. 声明两个enum flag类型的变量:
enmu flag end , start;
  1. 给声明的变量赋值或判断变量的值:
end = true;

if(start ** true)

  1. 如果希望一个枚举标识符对应一个特定的整数值,那么可以在定义数据类型时给这个标识符指定整数值。列表中随后出现的枚举标识符会被依次赋以整数值,从指定的整数值加1开始
enmu direction {up,down,left = 10,right};

上面定义了一个枚举数据类型direction。因为up位于序列首位,所以编译器给它赋值为0;down接着up,因此,赋给它的值为1;left指定了值10;所以,right的值为11。

  1. 枚举标识符可以共享相同的整数值。
enmu boolean {no = 0, false = 0, yes = 1 , true = 1};
  1. OC编译器实际上将枚举标识符作为整型常量来处理
enmu boolean {no = 0, false = 0, yes = 1 , true = 1};

enmu boolean flag;

flag = yes;

在这里,枚举类型变量flag实际上被是赋值为1,而不是yes这个名字。

  1. 如果要给枚举类型变量赋予一个整数值,应该使用类型转换运算符。但是就算不使用,编译器也不会有异议。
enmu boolean {no = 0, false = 0, yes = 1 , true = 1};

enmu boolean flag;

flag = (enmu boolean)1;

flag = (enmu boolean)(yes - 1);

  1. 枚举类型提供了一种方法,是你能把整数值和有象征意义的名称对应起来。尽量不要依赖枚举值被作为整数这个事实。

  2. 定义枚举类型时,可以将一个整数类型和枚举名称对应起来

enmu direction:unsinged short int {up,down,left = 10,right};
  1. 定义枚举类型时,可以将数据类型的名称省略。
enmu {up,down,left = 10,right} direction;

注意,这里定义的是一个未命名的枚举类型,direction是该枚举类型的变量,而不是数据类型名称。


typedef语句

  1. 像声明所需类型的变量那样编写一条语句。 int a;
  2. 通常应该出现声明变量名的地方,将其替换为新的类型名;int Counter;
  3. 在语句前面加上关键字typedef。

typedef int Counter;

typedef NSNumber *NumberObject;//注意星号

type enum Direction {up,down,left,right} Direction;//为enum Direction指定名称Direction

一次求反运算符

一次求反运算符:~。作用为对数字进行位反转。

例子:如果想对一个数的二进制的最低位设置为0,可以使用求反运算符。

w &= ~1;

这样可以不用保证int类型在不同系统上的不同位数。


第五部分


扩展类定义的几种方式

子类、分类、协议


category(分类)

#import "Fraction.h"

@interface Fraction (test)

- (void)add:(int)a;
- (void)mul:(int)a;

@end

@interface Fraction (test):告诉编译器你正在为Fraction定义新的分类,而且它的名称为test。注意,此处没有列出Fraction的父类,因为编译器已经从Fraction.h知道了这个内容。并且,你没有向编译器告知实例变量,因为以前定义的接口部分已经这样做了。实际上,如果尝试列出父类或实例变量,将会受到编译器发出的语法错误

分类的注意事项

协议和代理

协议
  1. 协议是多个类共享的一个方法列表
  2. 协议中列出的方法没有相应的实现,计划有其他人来实现。@protocol、@required
  3. 协议列出了一组方法,有些是可以选择实现的,有些是必须实现的。
  4. 注意,协议是无类的,它是无类的。和类名一样,协议名也必须是唯一的。
@protocol 协议名称

//声明一些协议方法

@required //在这之后都是必须实现的方法
@optional //在这之后都是可以选择实现的方法

@end
//在NSObject.h中

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end

遵守NSCopying协议

//.h文件中
@interface test : NSObject <NSCopying>

//.m文件中
@interface test () <NSCopying>
if([currentObject conformsToProtocol:@protocol(NSCopying)] ** YES)

可以通过 respondsToSelector: 判断一个对象是否实现了某个方法。

if([currentObject conformsToProtocol:@selector(copyWithZone:)] ** YES)

@protocol test <test11> //说明test协议也遵守了test11协议

代理

定义了协议的类可以看作是将协议定义的方法代理给了实现它们的类。具体的动作由代理类来承担,响应某些事件或者定义某些参数。

上一篇 下一篇

猜你喜欢

热点阅读