【书籍篇】《Objective-C程序设计》语法相关

来源:互联网 发布:数据质量整改报告 编辑:程序博客网 时间:2024/05/22 01:35
编译时和运行时检查
自定义类MyClass main中作如下处理

MyClass * cls = [[MyClass alloc]init];

        [cls setRel];

在执行时 编译器会显示 NoVisible @interface for ‘MyClass’ declares the selector ’setRel

如果 MyClass类中含有当前的方法 但是传入参数不一样时

在现有的版本Xcode中不会报错 但是运行时 会出现程序崩溃的情况 

分析:运行时系统检查系统类是否存在—》检查是否有该方法—》当执行时 传入的参数无法处理 确定当前方法不是所调用方法 系统崩溃。


id数据类型和静态类型

id类型可以用来存储任意类型的对象 但是我门在使用过程中并没有大范围使用id 为什么?

将一个变量声明为特定类对象时 使用静态形态 (即当前变量总是用于存储特定类的对象)。

使用静态类型时 编译器尽可能确保变量用法在程序中始终保持一致 编译器能通过检查来确定应用于对象的方法是由该类定义的或者由该类继承了,否则警告。


如果检查是在运行时执行为什么关心静态类型?

因为能更好在便一阶段而非运行时指出错误 如果留到运行时  可能在运行程序时不能发现错误。

原因2 能够提高程序的可读性。


动态类型参数和返回类型

如果使用一个动态类型来调用一个方法 需要注意以下规则 

如果在多各类中实现名称相同方法 每个方法都必须符合各个参数类型和返回值类型 这样编译器才能为消息表达式生成正确代码。


编译器会对它所遇到的每个类声明执行一致性检查 如果有一个或者多个方法在参数或者返回类型存在冲突 编译器显示警告信息。


在运行时 OC运行时系统仍然检查存储在变量中对象所属的确切类 并选择响应方法来执行。然而编译器可能生成不正确的代码来向方法传递参数或处理返回值 当一个方法选取对象作为他的参数 而另外一个是浮点数时 就可能发生。

如果这两个方法的不一致仅仅在于对象类型的不同 如  A 的add:方法使用 B为返回值 而B的add方法使用C为返回值 那么会生成正确代码 因为传递给对象的引用是内存地址(指针) 这也就解决了程序中会遇到传入对象不一样导致的返回值在以下的使用中出现问题的问题。


开始使用可以包含来自不同类的对象的变量时 可能会遇到以下问题

 这个对象是xx吗?

 这个对象支持xx方法吗?

 这个对象是xx类或者xx类的子类吗?


NSObject类支持一些基本方法 用于提出这些问题。

处理动态类型的方法:

-(BOOL)isKindOf:A                 对象是否为A或其子类的成员?

-(BOOL)isMemberOfClass:A  对象是否是A的成员

-(BOOL)respondsToSelector:selector 对象能否响应selector指定的方法?

+(BOOL)isSubClassOfClass:A   该类是指定类的子类吗?

+(BOOL)instancesRespondToSelector:selector   

+(BOOL)instanceMethodForSelector:<#(SEL)#>该方法返回selector的实现地址 即IMP地址


-(id)performSelector:selector      执行方法

-(id)performSelector:selector withObject:object执行方法

-(id)performSelector:selector withObject:object withObject:object2执行方法


以上方法只是一部分。

其余部分中包括:

1.允许你提出关于是否遵守了协议的问题(协议)+ (BOOL)conformsToProtocol:(Protocol *)aProtocol

2.提出有关动态解析方法的问题  





5.使用@try处理异常

好的编程实践能够预测程序中可能出现的问题。为此 可以测试 使得程序异常终止的条件并处理这些情况。可能需要记录一条消息并完全终止程序 或者采取其他正确措施。

以下列程序为例

 id cls = cls1;


    @try {

        [cls cancel];

    }

    @catch (NSException *exception) {

        NSLog(@"%@",exception.description);

    }

    @finally {

        NSLog(@"%@",@"该处有问题");

    }

该处id指向一个cls1对象,然后调用一个方法cancel,因为当前的cls是一个id类型 所以编译器并不知道在cls中有什么样的实现。

所以编译时不会有问题,在运行时用@try @catch来捕获这个异常,然后针对异常做出相应的处理。

如果直接调用这个方法 在执行时会出错误。因此try catch语法是有必要的。



oc中的作用域

该作用域和Java类似 但是是在类扩展中使用的,且需要用@来修饰。

@protected 修饰的实例变量可以被该类及任何子类中定义的方法直接访问 默认

@private 修饰的实例变量可被该类方法访问,但是不能被子类 其他类访问

@package 对于64位图像,可以在实现该类的图像的任何地方访问这个实例变量。


关于分类的注意事项:

1.尽管分类可以访问原始类的实例变量但是不能添加自身的任何变量。如果想添加,可以考虑创建子类 或者用runtime来添加

2.分类可以重载该类的方法,但是这样的设计习惯很不好 首先 重载了这个方法之后 就不能访问原先的方法了。如果确实需要重载 可以子类化这个类 然后在类内重载 调用super类的方法来发送信息。

3.与接口不同的一点是 分类不必实现类中所有的方法,这对于程序扩展很有用。因为可以在该分类中声明所有方法,然后在一段时间之后才实现它。

4.分类实现重载,子类也会调用该方法。这存在潜在的危险性。



关于预处理程序

预处理程序是oc编译过程的一部分。顾名思义 实际是在oc程序分析之前处理。

1.#define语句 给符号指定程序常量 定义的符号不是变量 不可修改

2.高级定义

名称定义可以包括表达式和其他东西

如 #define Two_P 3.1415926

为什么不加分号?

预处理程序在分析程序之前被替换,分号存在会引起语法错误。

预处理程序定义右侧不必是合法oc表达式 只要使用的时候 结果表达式正确就可以了。例如 可以如下定义:

#define AND &&

#define OR ||

if(x>0 AND x<10){}

if(x>10 || OR x<0){}

3.预定义的值可以引用其他预定义的值

在iOS中,两个已经经过定义的顺序颠倒也是可以的。

#define Two_PI 2*PI

#define Pi 3.1415926

规则是:只要在程序中使用预定义名称时,所有的符号都是定义过的。那么就可以在定义中引入其他预定义的值。

#define IS_LEAP_YEAR  year % 4 ==0 || year % 400 == 0 \

                                                |year % 400 == 0


通常与处理程序假设定义包含在程序的一行中,如果需要第二行,那么上一行的最后一个字符必须是反斜线符号,这个字符告诉与处理程序这里存在一个后续,否则将被忽略。

可以将IS_LEAP_YEAR定义为含有参数y

#define ISLEAP_YEAR(y) y % 4 == 0 || y %100 ==0 || y % 400 ==0

预定义通常被称之为『宏』。这个术语经常用于带有一个或者多个参数的定义。

#define SQUARE(x) x*x

为了防止出现问题,必须给x加上括号




条件编译

条件编译通常用于创建可以在不同的计算机系统上编译运行的程序,还经常用来开关程序中的各种语句,例如用来输出变量值或跟踪程序执行流程的调试程序。

#ifdef #endif

#else #ifndef

#undef

0 0