黑马程序员——OC核心语法

来源:互联网 发布:5s手机4g网络用不了 编辑:程序博客网 时间:2024/05/03 21:16

点语法

简单的一句话,点语法的本质就是方法调用。
set方法[p setAge:10]; 用点语法可以表达为:p.age = 10;;get方法int a = p.age; 用点语法可以表达为:[p age];
在使用点语法的时候我们需要注意逻辑关系,在方法的声明中引入不当的点语法会造成死循环:

- (void)setAge:(int)age{    // _age = age;    self.age = age; }- (int)age{    // return _age;    return self.age;}

在这个程序中,两个点语法都会造成死循环的产生。第一个点语法可以写成[self setAge:age];,第二个点语法可以写成[self age];

成员变量的作用域

成员变量的作用域有四种方式定义
1> @public : 在任何地方都能直接访问对象的成员变量;
2> @private : 只能在当前类的对象方法中直接访问,在@implementation中默认的就是@private;
3> @protected : 可以在当前类及其子类的对象方法中直接访问 在@interface中默认就是@protected;
4> 第四种是不常用的一种方式,@package : 只要处在同一个框架中,就能直接访问对象的成员变量。
除了作用域我们还需要注意的是在@implementation中不能定义和@interface中同名的成员变量;同时在main函数后面的成员变量都不能直接访问的。

@property和@synthesize

首先我们做一个练习:
设计一个类Circle,用来表示二维平面中的圆
1> 属性
* double _radius (半径)
* Point2D *_point (圆心)

2> 方法
* 属性相应的set和get方法
* 设计一个对象判断跟其他圆是否重叠(重叠返回YES,否则返回NO)
* 设计一个类方法判断两个圆是否重叠(重叠返回YES,否则返回NO)

我们可以先设计一个点作为圆心,通过圆心距离和半径和的大小来判断圆是否重叠

#import <Foundation/Foundation.h>#import <math.h>// 点@interface Point2D : NSObject{    double _x;    double _y;}// x的getter和setter- (void)setX:(double)x;- (double)x;// y的getter和setter- (void)setY:(double)y;- (double)y;//同时设置x和y- (void)setX:(double)x andY:(double)y;// 方法对象计算和其他点得距离- (double)distanceWithOther:(Point2D *)other;// 类方法计算两个点之间的距离+ (double)distanceBetweenPoint1:(Point2D *)p1 andPoint2:(Point2D *)p2;@end@implementation Point2D// x的getter和setter的实现- (void)setX:(double)x{    _x = x;}- (double)x{    return _x;}//y的getter和setter的实现- (void)setY:(double)y{    _y = y;}- (double)y{    return _y;}//同时设置x和y的实现- (void)setX:(double)x andY:(double)y{    _x = x;    _y = y;}- (double)distanceWithOther:(Point2D *)other{    return [Point2D distanceBetweenPoint1:self andPoint2:other];}+ (double)distanceBetweenPoint1:(Point2D *)p1 andPoint2:(Point2D *)p2{    // (x1-x2)的平方    double X = pow(([p1 x] - [p2 x]), 2);    // (y1-y2)的平方    double Y = pow(([p1 y] - [p2 y]), 2);    // 对两个平方和开根号    return sqrt(X + Y);}@end// 圆@interface Circle : NSObject{    double _radius;    Point2D *_point;}// 半径的getter和setter- (void)setRadius:(double)radius;- (double)radius;// 圆心的getter和setter- (void)setPoint:(Point2D *)point;- (Point2D *)point;// 对象方法判断和其他圆是否重叠- (BOOL)intersectWithOther:(Circle *)other;// 类方法判断和其他圆是否重叠+ (BOOL)isIntersectCircle1:(Circle *)c1 andCircle2:(Circle *)c2;@end@implementation Circle// 半径getter和setter的实现- (void)setRadius:(double)radius{    _radius = radius;}- (double)radius{    return _radius;}// 圆心getter和setter的实现- (void)setPoint:(Point2D *)point{    _point = point;}- (Point2D *)point{    return _point;}// 对象方法实现- (BOOL)intersectWithOther:(Circle *)other{    return [Circle isIntersectCircle1:self andCircle2:other];}// 类方法实现+ (BOOL)isIntersectCircle1:(Circle *)c1 andCircle2:(Circle *)c2{    // 两个圆心    Point2D *p1 = [c1 point];    Point2D *p2 = [c2 point];    // 两个圆心的距离    double distance = [Point2D distanceBetweenPoint1:p1 andPoint2:p2];    // 半径的和    double sumOfRadius = [c1 radius] + [c2 radius];    return distance < sumOfRadius;}@endint main(){    Circle *c1 = [Circle new];    // 设置半径    [c1 setRadius:5];    // 设置圆心    Point2D *p1 = [Point2D new];    [p1 setX:5 andY:5];    [c1 setPoint:p1];    Circle *c2 = [Circle new];    // 设置半径    [c2 setRadius:4];    // 设置圆心    Point2D *p2 = [Point2D new];    [p2 setX:8 andY:6];    [c2 setPoint:p2];    BOOL i1 = [c1 intersectWithOther:c2];    BOOL i2 = [Circle isIntersectCircle1:c1 andCircle2:c2];    NSLog(@"%d %d", i1, i2);    return 0;}

虽然我们实现了功能,但是根据程序我们可以看出来,其中写了很多重复的代码,比如各成员变量的setter和getter方法。Xcode为了让我们不需要考虑这些重复代码,腾出心思来更好的考虑功能,特意定义了@property和@synthesize。
@property:可以自动生成某个成员变量的setter和getter声明。如下:

@property int age;//- (void)setAge:(int)age;//- (int)age;@property int height;//- (void)setHeight:(int)height;//- (int)height;

而@synthesize自动生成age的setter和getter实现,并且会访问_age这个成员变量,如下:

@synthesize age = _age;@synthesize height = _height;

同时当在@synthesize中访问某个不存在的成员变量时,系统会自动生成@private类型的变量。

虽然现在非常大的简化了我们的程序,但是并不是最简单的写法,最简单的可以写成:

@property int age;

这一行虽然简单,但是它又三个作用
第一、会自动生成setter和getter的方法
第二、会自动生成setter和getter的实现
第三、可以自动生成_age成员变量
根据这些我们就可以很大的简化程序,更好的去考虑功能。

构造方法

在说构造之前我们先说一个关键字id。id是一个万能指针,能指向\操作任何OC对象,id其实就是NSObject *,它只适用于OC对象。
在程序中初始化的成员变量默认都是0,有时候我们为了让对象创建出来,成员变量就会有一些固定的值,这个时候我们就用到了构造方法。
构造方法:用来初始化对象的方法,是个对象方法,-开头。
我们先来看一下完整地创建一个可用的对象是如何进行的:
Person *p = [Person new];
1.调用+alloc分配存储空间 Person *p1 = [Person alloc];
2.调用-init进行初始化 Person *p2 = [p1 init];
这两个步骤我们也可以等价为:
Person *p4 = [[Person alloc] init];
在”.m”文件中实现初始化,也就是重写init写法:

- (id)init{    // 1.一定要调用回super的init方法:初始化父类中声明的一些成员变量和其他属性    self = [super init]; // 当前对象 self    // 2.如果对象初始化成功,才有必要进行接下来的初始化    if (self != nil)    { // 初始化成功        _age = 10;    }    // 3.返回一个已经初始化完毕的对象    return self;}

以上的代码我们可以更加简化的书写:

 - (id)init{    if(self = [super init])    {        _age = 10;    }    return self;}

可以实现同样的初始化功能,而且更加简单便于记忆。不过这里可能有些人会对super有些忘记。super是直接调用父类的某个方法,当子类需要重写父类的方法的同时想保留父类的一些行为时使用super。
注意点:
1> 先调用父类的构造方法([super init])
2> 再进行子类内部成员变量的初始化
- 自定义构造方法
自定义构造方法的规范
1.一定是对象方法,一定以 - 开头
2.返回值一般是id类型
3.方法名一般以initWith开头
如:- (id)initWithName:(NSString *)name;
当在使用自定义构造方法的时候,需要注意一点:当子类的构造方法中包含父类的属性,在这个构造方法的实现中,父类的属性交给父类方法去处理,子类方法处理子类自己的属性,如下将name、age传递到父类方法中进行初始化:

- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no{    if ( self = [super initWithName:name andAge:age])    {        _no = no;    }    return self;}

分类-Category

分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法。

  • 格式
    分类的声明
@interface 类名 (分类名称)// 方法声明@end

分类的实现

@implementation 类名 (分类名称)// 方法实现@end

分类可以将一个庞大的类可以由多个人来编写,更有利于团队合作。
分类的使用注意:
1.分类只能增加方法,不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量
3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
4.方法调用的优先级:分类(最后参与编译的分类优先) –> 原来类 –> 父类
除了上述的一些作用外,我们可以利用分类来写一些类没有的功能。
比如给NSString
增加一个类方法计算某个字符串内部英文字母的个数
增加一个对象方法计算当前字符串内部英文字母的个数
增加一个类方法比较两个字符串的长度,返回长度差
增加一个对象方法和其他字符串比较长度,返回长度差
声明:

#import <Foundation/Foundation.h>@interface NSString (NumberOfLetter)+ (int)letterNumberOfString:(NSString *)str;- (int)letterNumber;+ (int)compareLengthBetweenString1:(NSString *)str1 andString2:(NSString *)str2;- (int)compareLengthWithOther:(NSString *)other;@end

实现:

#import "NSString+NumberOfLetter.h"@implementation NSString (NumberOfLetter)+ (int)letterNumberOfString:(NSString *)str{    return [str letterNumber];}- (int)letterNumber{    int count = 0;    for(int i = 0 ; i<self.length; i++)    {        // 取出i这个位置对应的字符        unichar c = [self characterAtIndex:i];        // 如果这个字符是英文字母        if( (c>='a' && c<='z') || (c>='A' && c<='Z') )        {            count++;        }    }    return count;}+ (int)compareLengthBetweenString1:(NSString *)str1 andString2:(NSString *)str2{    // return ([str1 length]-[str2 length]);    return [str1 compareLengthWithOther:str2];}- (int)compareLengthWithOther:(NSString *)other{    return ([self length]-[other length]);}@end

类的本质

  • 类也是一个对象
    其实类也是一个对象,是Class类型的对象,简称“类对象”。Class类型的定义:typedef struct objc_class *Class;;类名就代表着类对象,每个类只有一个类对象。

  • +load和+initialize

    1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。
    2.当第一次使用某个类时,就会调用当前类的+initialize方法
    3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)
    先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)

  • 获取类对象的2种方式

Class c = [Person class]; // 类方法

或者

Person *p = [Person new];Class c2 = [p class]; // 对象方法
  • 类对象调用类方法
Class c = [Person class];Person *p2 = [c new];

description方法

  • -description方法
    使用NSLog和%@输出某个对象时,会调用对象的-description方法,并拿到返回值进行输出

  • +description方法
    使用NSLog和%@输出某个类对象时,会调用类对象+description方法,并拿到返回值进行输出

默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
1.会调用对象p的-description方法
2.拿到-description方法的返回值(NSString *)显示到屏幕上
3.-description方法默认返回的是“类名+内存地址”

如果在-description方法中使用NSLog打印self时会形成死循环:

- (NSString *)description{    // 下面代码会引发死循环    NSLog(@"%@", self);}

SEL

SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法。

  • SEL对象的创建
SEL s = @selector(test);SEL s2 = NSSelectorFromString(@"test");
  • SEL对象的其他用法
// 将SEL对象转为NSString对象NSString *str = NSStringFromSelector(@selector(test));Person *p = [Person new];// 调用对象p的test方法[p performSelector:@selector(test)];

_cmd代表着当前方法,使用时也会引发死循环:[self performSelector:_cmd];

0 0
原创粉丝点击