黑马程序员——OC基础---核心语法(id,构造方法,Category,description,SEL)

来源:互联网 发布:ip mac 编辑:程序博客网 时间:2024/05/01 18:24

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


id

1 简介

1)万能指针,能指向任何OC对象,相当于NSObject *

2)id类型的定义

typedef struct objc object {

     Class isa;

} *id;

2 使用

注意:id后面不要加上*

id p = [Person new];

3 局限性

调用一个不存在的方法,编译器会马上报错

示例:

#import <Foundation/Foundation.h>#import "Person.h"void test(id d){    }int main(int argc, const char * argv[]){    @autoreleasepool {        Person *p = [Person new];        //[p fsdfdsfd];                NSObject *o = [Person new];                        // id  == NSObject *        // 万能指针,能指向\操作任何OC对象        id d = [Person new];                [d setAge:10];                [d setObj:@"321423432"];                NSLog(@"%d", [d age]);    }    return 0;}

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@property id obj;@end

#import "Person.h"@implementation Person@end

运行结果:





构造方法


构造方法:用来初始化对象的方法,是个对象方法,-开头

重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值


 重写构造方法的注意点

1.先调用父类的构造方法([super init]

2.再进行子类内部成员变量的初始化


完整地创建一个可用的对象

1.分配存储空间  +alloc

2.初始化 -init

 1.调用+alloc分配存储空间

 Person *p1 = [Person alloc];

 2.调用-init进行初始化

 Person *p2 = [p1 init];

 调用-init进行初始化

 Person *p3 = [Person new];



 重写-init方法

- (id)init

{

   // 1.一定要调用回superinit方法:初始化父类中声明的一些成员变量和其他属性

   self = [superinit]; // 当前对象 self

    

    

   // 2.如果对象初始化成功,才有必要进行接下来的初始化

   if (self !=nil)

    { // 初始化成功

       _age = 10;

    }

    

   // 3.返回一个已经初始化完毕的对象

    returnself;

}


示例:
#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(){        Person *p4 = [[Person alloc] init];            Student *stu = [[Student alloc] init];        NSLog(@"------");        return 0;}

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@end

#import "Person.h"@implementation Person- (id)init{    if ( self = [super init] )    { // 初始化成功        _age = 10;    }        // 3.返回一个已经初始化完毕的对象    return self;}@end

#import "Person.h"@interface Student : Person@property int no;@end

#import "Student.h"@implementation Student// 学生对象初始化完毕后,年龄就是10,学号就是1- (id)init{    if ( self = [super init] )    {        _no = 1;    }    return self;}@end

运行结果:








自定义构造方法

 自定义构造方法的规范

 1.一定是对象方法,一定以 -开头

 2.返回值一般是id类型

 3.方法名一般以initWith开头


注意:

父类的属性交给父类方法去处理,子类方法处理子类自己的属性


示例:

#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *p = [[Student alloc] initWithName:@"Jim" andAge:29 andNo:10];        NSLog(@"00000");    }    return 0;}

#import <Foundation/Foundation.h>@interface Person : NSObject@property NSString *name;@property int age;- (id)initWithName:(NSString *)name;- (id)initWithAge:(int)age;// initWithName:andAge:- (id)initWithName:(NSString *)name andAge:(int)age;@end

#import "Person.h"@implementation Person- (id)init{    if ( self = [super init] )    {        _name = @"Jack";    }    return self;}- (id)initWithName:(NSString *)name{    if ( self = [super init] )    {        _name = name;    }        return self;}- (id)initWithAge:(int)age{    if ( self = [super init] )    {        _age = age;    }    return self;}- (id)initWithName:(NSString *)name andAge:(int)age{    if ( self = [super init] )    {        _name = name;        _age = age;    }    return self;}@end

#import "Person.h"@interface Student : Person@property int no;- (id)initWithNo:(int)no;- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;@end

#import "Student.h"@implementation Student- (id)initWithNo:(int)no{    if ( self = [super init] )    {        _no = no;    }    return self;}// 父类的属性交给父类方法去处理,子类方法处理子类自己的属性- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no{    // 将name、age传递到父类方法中进行初始化    if ( self = [super initWithName:name andAge:age])    {        _no = no;    }        return self;}//- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no//{//     if ( self = [super init] )//     {//         _no  = no;//         //_name = name;//         self.name = name;//         self.age = age;//         //         //[self setName:name];//         //[self setAge:age];//     }//    //    return self;//}@end

运行结果:





Category


OC提供了⼀一种与众不同的⽅方式--Catagory,可以动态的为已经存在的类添加新的⾏行为(方法)

 这样可以保证类的原始设计规模较⼩小,功能增加时再逐步扩展

 使⽤用Category对类进⾏行扩展时,不需要创建⼦子类

Category使⽤用简单的⽅方式,实现了类的相关⽅方法的模块化,把不同的类⽅方法分配到不同的分类⽂文件中 


使用方法

@interface Student: NSObject
-(void) print;

@end

这是声明⽂文件Student.h,包含⼀一个实例⽅方法print

如果想在不修改原始类、不增加⼦子类的情况下,为该类增加⼀一个play的⽅方法,只需要简单的定义两个⽂文件

Student+Play.hStudent+Play.m,在声明⽂文件和实现⽂文件中⽤用"()"Category的名称括起来即可 


使用场景:

在定义类时的某些情况下(例如需求变更),你可能想

 要为其中的某个或⼏几个类中添加新的⽅方法
 ⼀一个类中包含了许多不同种类的⽅方法需要实现,⽽而这些⽅方法需要不同团队的成员实现

在使⽤用基础类库中的类时,有可能希望这些类实现⼀一些⾃自⼰己需要的⽅方法,⽐比如写个NSString+JSON.h,

NSString这个类拓展⼀一些解析JSON的⽅方法 


使用注意:

 1.分类只能增加方法,不能增加成员变量

 2.分类方法实现中可以访问原来类中声明的成员变量

 3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用

 4.方法调用的优先级:分类(最后参与编译的分类优先) -->原来类  --> 父类


类的私有方法:

第⼀一种⽅方式:直接在.m⽂文件中写⽅方法实现,不要在.h⽂文件中进⾏行⽅方法声明

 第⼆二种⽅方式:.m⽂文件中定义⼀一个Category,Category中声明⼀一些⽅方法,然后在@implementation@end之间作⽅方法实现 


示例:

/* 分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法 */#import <Foundation/Foundation.h>#import "Person.h"#import "Person+MJ.h"#import "Person+JJ.h"int main(){    Person *p = [[Person alloc] init];    //p.age = 10;        // 优先去分类中查找,然后再去原来类中找,最后再去父类中找    [p test];   // [p study];        return 0;}

#import <Foundation/Foundation.h>@interface Person : NSObject{    int _age;}@property int age;- (void)test;@end

#import "Person.h"@implementation Person- (void)test{    NSLog(@"Person-test");}@end

#import "Person.h"@interface Person (JJ)- (void)test2;@end

#import "Person+JJ.h"@implementation Person (JJ)- (void)test2{    NSLog(@"-----test2");}- (void)test{    NSLog(@"Person (JJ)-test");}@end

#import "Person.h"@interface Person (MJ)- (void)study;@end

#import "Person+MJ.h"@implementation Person (MJ)- (void)study{    NSLog(@"学习-----%d", _age);}- (void)test{    NSLog(@"Person (MJ)-test");}@end

运行结果:





分类的使用示例:

#import <Foundation/Foundation.h>#import "NSString+Number.h"int main()// 类库:很多类的集合{    // int count = [NSString numberCountOfString:@"54d43a43s43dasd"];        int count = [@"9fdsfds543543" numberCount];        NSLog(@"%d", count);    return 0;}

/* 给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数 给NSString增加一个对象方法:计算当前字符串中阿拉伯数字的个数 */#import <Foundation/Foundation.h>@interface NSString (Number)+ (int)numberCountOfString:(NSString *)str;- (int)numberCount;@end

#import "NSString+Number.h"@implementation NSString (Number)//  @"abc434ab43"+ (int)numberCountOfString:(NSString *)str{    // 1.定义变量计算数字的个数//    int count = 0;//    //    for (int i = 0; i<str.length; i++)//    {//        unichar c = [str characterAtIndex:i];//        //        if ( c>='0' && c<='9')//        {//            count++;//        }//    }//    return count;        return [str numberCount];}- (int)numberCount{    int count = 0;        for (int i = 0; i<self.length; i++)    {        // 取出i这个位置对应的字符        unichar c = [self characterAtIndex:i];                // 如果这个字符是阿拉伯数字        if ( c>='0' && c<='9' )        {            count++;        }    }        return count;}@end

运行结果:





类的本质

1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。

 

2.当第一次使用某个类时,就会调用当前类的+initialize方法

 

3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)

  先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)



示例:

#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"#import "GoodStudent.h"int main(){    // [[GoodStudent alloc] init];        return 0;}void test1(){    Person *p = [[Person alloc] init];        //[Person test];        // 内存中的类对象    // 类对象 == 类    Class c = [p class];    [c test];        Person *p2 = [[c new] init];            NSLog(@"00000");}void test(){    // 利用Person这个类创建了2个Person类型的对象    Person *p = [[Person alloc] init];        Person *p2 = [[Person alloc] init];        Person *p3 = [[Person alloc] init];        // 获取内存中的类对象    Class c = [p class];        Class c2 = [p2 class];        // 获取内存中的类对象    Class c3 = [Person class];            NSLog(@"c=%p, c2=%p, c3=%p", c, c2, c3);        //  类本身也是一个对象,是个Class类型的对象,简称类对象        /*     利用Class 创建  Person类对象          利用 Person类对象 创建 Person类型的对象          */}

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;+ (void)test;@end

#import "Person.h"@implementation Person+ (void)test{    NSLog(@"调用了test方法");}// 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法+ (void)load{    NSLog(@"Person---load");}// 当第一次使用这个类的时候,就会调用一次+initialize方法+ (void)initialize{    NSLog(@"Person-initialize");}@end

#import <Foundation/Foundation.h>#import "Person.h"@interface Student : Person@end

#import "Student.h"@implementation Student// 在类被加载的时候调用+ (void)load{    NSLog(@"Student---load");}+ (void)initialize{    NSLog(@"Student-initialize");}@end

#import "Student.h"@interface GoodStudent : Student@en

#import "GoodStudent.h"@implementation GoodStudent+ (void)load{    NSLog(@"GoodStudent---load");}+ (void)initialize{    NSLog(@"GoodStudent-initialize");}@end

#import "Person.h"@interface Person (MJ)@end

#import "Person+MJ.h"@implementation Person (MJ)+ (void)load{    NSLog(@"Person(MJ)---load");}+ (void)initialize{    NSLog(@"Person(MJ)-initialize");}@end

运行结果:




description方法

1 -description方法

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


2 +description方法

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


3 修改NSLog的默认输出

重写-description或者+description方法即可


4.死循环陷阱

如果在-description方法中使用NSLog打印self


示例:

#import <Foundation/Foundation.h>#import "Person.h"void test9(){    // 输出当前函数名    NSLog(@"%s\n", __func__);}int main(){    // 输出行号    NSLog(@"%d", __LINE__);        // NSLog输出C语言字符串的时候,不能有中文    // NSLog(@"%s", __FILE__);        // 输出源文件的名称    printf("%s\n", __FILE__);        test9();        Person *p = [[Person alloc] init];        // 指针变量的地址    NSLog(@"%p", &p);    // 对象的地址    NSLog(@"%p", p);    // <类名:对象地址>    NSLog(@"%@", p);       return 0;}void test2(){    Class c = [Person class];        // 1.会调用类的+description方法    // 2.拿到+description方法的返回值(NSString *)显示到屏幕上    NSLog(@"%@", c);}void test1(){    Person *p = [[Person alloc] init];    p.age = 20;    p.name = @"Jack";    // 默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>        // 1.会调用对象p的-description方法    // 2.拿到-description方法的返回值(NSString *)显示到屏幕上    // 3.-description方法默认返回的是“类名+内存地址”    NSLog(@"%@", p);        //Person *p2 = [[Person alloc] init];    //NSLog(@"%@", p2);        //NSString *name = @"Rose";        //NSLog(@"我的名字是%@", name);        Person *p2 = [[Person alloc] init];    p2.age = 25;    p2.name = @"Jake";        NSLog(@"%@", p2);}

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@property NSString *name;@end

#import "Person.h"@implementation Person// 决定了实例对象的输出结果//- (NSString *)description//{//    // 下面代码会引发死循环//    // NSLog(@"%@", self);//    return [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];//    //return @"3424324";//}// 决定了类对象的输出结果+ (NSString *)description{    return @"Abc";}@end

运行结果:





SEL

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

其实消息就是SEL


1 方法的存储位置

每个类的方法列表都存储在类对象中


每个方法都有一个与之对应的SEL类型的对象


根据一个SEL对象就可以找到方法的地址,进而调用方法


SEL类型的定义

typedef struct objc selector *SEL


2 SEL对象的创建


SEL s = @selector(test)

SEL s2 = NSSelectorFromString(@”test“);


3.SEL对象的其他用法


将SEL对象转为NSString对象

NSString *str = NSStringFromSelector(@selector(test));


Person *p = [Person new];


调用对象p的test方法

[p performSelector:@selector(test)];


示例:


#import <Foundation/Foundation.h>#import "Person.h"int main(){    Person *p = [[Person alloc] init];        [p test2];    //    NSString *name = @"test2";//    //    SEL s = NSSelectorFromString(name);//    //    [p performSelector:s];            // 间接调用test2方法    //[p performSelector:@selector(test2)];        //[p test3:@"123"];        //    SEL s = @selector(test3:);//    //    [p performSelector:s withObject:@"456"];        //[p test2];        // 1.把test2包装成SEL类型的数据    // 2.根据SEL数据找到对应的方法地址    // 3.根据方法地址调用对应的方法    return 0;}

#import <Foundation/Foundation.h>@interface Person : NSObject+ (void)test;- (void)test2;- (void)test3:(NSString *)abc;@end

#import "Person.h"@implementation Person+ (void)test{    NSLog(@"test-----");}- (void)test2{    // _cmd代表着当前方法        NSString *str = NSStringFromSelector(_cmd);        // 会引发死循环    // [self performSelector:_cmd];        NSLog(@"调用了test2方法-----%@", str);}- (void)test3:(NSString *)abc{    NSLog(@"test3-----%@", abc);}@end

运行结果:






0 0
原创粉丝点击