OC基础复习(四)之激动人心的self用法

---恢复内容开始---

OC 也好,UI 也罢,甚至于swift,几乎都会涉及self,super用法,并且通常来说不太好理解,总会迷迷糊糊的用,所以必须要搞清楚

补充知识:OC中全局变量和局部变量

变量显示存储空间生命周期特点
成员变量(实例变量、属性)在类的声明中定义,在@interface CLASS ( ) 和@end 中间创建对象时候有效,对象销毁(释放)结束1.定义的时候不能初始化2.只能通过对象访问,不能离开类单独存在
局部变量函数体内或代码块中函数体内作用域: 从定义的那一行开始,直到大括号结束或者遇到break return为止
全局变量函数外部静态区整个文件(从定义变量的那一行直到文件结束)程序一启动就会自动分配内存空间,直到程序释放才结束

简言之:全局变量是放在函数体外(或者说方法外),局部变量是放在函数体内(方法内),全局变量作用域是整个文件,(这个整个文件的含义是当我写在.h文件中,在.m文件也是可以访问的,.h和.m文件是一体的,创建的时候就同时会生成)

突然脑子死机冒出了一个问题:引入头文件的目的?还有和继承的区别?

首先:引入头文件,一般是只引入.h文件就可以,引入头文件,引入了这个文件的类,类型,方法的声明以及实现(相当于引入了这个类,但又不是引入这个类),为什么这么说,

加入A文件引入了B文件的头文件,不是说A文件就可以访问B文件的属性和方法了,我们知道,属性和方法只能由A文件中的类或者类对象去掉用,所以我们引入了一个文件

要想在B 文件访问A文件属性和调用方法,只能在B文件实例化A对象,用A对象去访问和调用.

这里又可以延伸一个东西:就是我们狭隘的理解全局变量(也说全局属性)作用域为只适用于当前.h.m文件,那么引入头文件后,是不是同样有作用,这个问题其实很简单,

引入头文件,就是当前文件多了个文件,那么显然,多出的文件在当前文件中同样有作用,也就是说,属性和方法写到哪无所谓,只要是该文件中的类的对象,就可以访问属性,

调用方法.

-----------进入正题:

1.self用法:

1)

谁调用了当前方法,self就指的是谁,

self出现在对象方法,self就代表对象,self出现在类方法中,self就代表类,

这么一说很笼统:理解后也就是:对象方法是由对象去掉用,类方法是由类去掉用,而self是出现在方法内部的,谁调用谁就替代self(也就是说self可以是类也可以是对象)

2)

self修饰变量,表示访问的是一个全局的实例变量(只是这么一说,肯定被读者忽略,那么为啥么要强调这一句话?)

举例如下:

>> 1. 声明的时候age没有下划线,导致在set方法中会出现问题

>> 2. 在Set方法中实现的时候,因为形参跟成员变量同名,形参属于局部变量,所以导致一个结果。局部变量跟全局变量同名的时候,会暂时屏蔽全局变量的作用域。解决这个问题就引入self

>> 3. 所以在main函数中调用时,结果是0.

>> 4. 在set方法中加入self ->age = age, self在对象方法中就代表当前的对象,self –>age 访问的是全局的实例变量。所以问题就解决了。

------------------------------第一步类的声明--------------------------

#import <Foundation/Foundation.h>
@interface Person : NSObject
{
    int age; //实例变量没有下划线时
}
//封装对实例变量的读写的方法
-(void)setAge:(int)age;
-(int)age;
@end

------------------------------第二步类的实现--------------------------

age = age

局部变量 和 全局变量同名会屏蔽全局变量的作用域:

所以等号左边的age就是指-(void)setAge:(int)age中的age,注意看:我为什么说这个age是局部变量?冒号右边有个(int)age其实也就是在方法内部定义了一个

局部变量age,所以在方法内部age = age都是指(int)age中的age,此时,才到了self大显神通的时候了,正因为self在修饰变量的时候是访问的全局的实例变量,所以体现了

self的价值和用处.

#import "Person.h"
@implementation Person
-(void)setAge:(int)age{
    //age = 88
    //当局部变量和全局变量同名的时候,局部变量会暂时屏蔽全局变量的作用域
    age = age;
}  //局部变量age的生命周期是到此处结束
-(int)age{
    return age;
}
@end

------------------------------第三步main函数中--------------------------

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建对象
        Person *p = [Person new];
        //设置年龄
        [p setAge:88];
        //打印输出年龄
        NSLog(@"_age = %d",[p age]);
    }
    return 0;
}

--------------------------第四步修改的set方法实现-----------------------

#import "Person.h"
@implementation Person
-(void)setAge:(int)age{
   // 此时就可以赋值了  self  == p
self->age = age;     // self ->age 访问的是全局的实例变量} 
-(int)age{
    return age;
}
@end

2.类的继承与派生:

子类继承父类便拥有父类所有的属性和方法(注意是拥有不是简单的引入(引入头文件),引入的还是别人的,拥有的才是自己的)

注意:父类的私有属性是不能被子类继承的(私有属性就是写在.m文件中的属性,要和实例变量修饰符private修饰的变量区分开)

提一下:private修饰的实例变量不是完全私有的,是可以被子类继承的,但不能被子类所使用(就像父亲给了儿子一个加了锁的宝箱,儿子虽然拥有但是打不开的)

举个例子:就像抄别人的东西始终是别人的,只有自己想出来的,自己总结出来的才是自己的,就像我的博客是劳资一个字一个字打的,

那就是我的.(怎么有种亲儿子和干儿子的区别)

继承:是单继承,什么意思?就是子类只能继承一个父类,父类可以拥有多个子类(你只能有一个亲爹,但你亲爹可以生好几个儿子)

OC 没有多继承但可以多层继承(就是子类可以继承父类,父类可以继承父父类,但是子类不能继承父父类,不要问我为什么,你爷爷能生出你么)

  派生类(其实也就是子类)的概念其实就是说:子类继承了父类,仍然可以添加自己的属性和方法,(儿子继承老子的财产,当然可以自己去创造财富)

2.1继承的注意事项:

只有一条:子类是不能定义和父类相同的实例变量的,例如父类声明 了一个int _age,子类就不能重复声明了(这句话的前提肯定是子类已经继承父类了)

也就是说,属性没有重写这一说(子类继承父类,已经拥有了父类的属性,那么子类如果再定义一个相同名称的属性,就会造成变量的重复定义,会出错),

而方法是可以重写的

3.方法的重写:(还有后面会提到的重写构造方法)

定义:子类重写父类的方法就是方法的重写.

子类重写父类的方法,假如父类Dog有一个方法是 – (void)eat; 子类重写了这个方法,子类对象调用的时候会先调用子类的这个eat方法,

子类有这个方法,就不再调用父类,但是如果想掉就在eat方法的实现中加入[super eat];即可

衍生出方法的调用顺序:当子类对象去掉用一个方法的时候,会先从子类中找是否有这个方法,如果没有就去父类中找,依次往上,

直到找到最顶层的NSObject为止,如果最顶层也没有就会报错.

------(走了会神儿...写到这里突然想到工作中的事儿,工作中做东西目的是实现功能和高效,因为老板要的是结果,所以,有些新人出去总想着

亲力亲为,其实是过于偏执,你会写这很好,但作用不大,提升也不大,你用别人的在别人的基础上优化,既可以学习也可以提高效率,还能提升自己优化代码的能力)

4.实例变量修饰符

@public(公开的)在当前类和任何别的类中,都能被实例变量所属的对象访问,或者使用,(这里所指的任何别的类,是泛指通过头文件的引入,也可以用extern修饰)

@protected(受保护的)只能在本类和子类(OC中实例变量默认是被他修饰的)

@private(私有的)只能在本类,相对私有,能被子类继承,但不能被子类访问

@package (包)用的很少,可以在本类和其他类中使用,但和public是有区别的,package是指当前类中,就是用package修饰的,子类继承后不能访问

5.OC中私有属性和私有方法

1)

私有属性:上文已经多次提到,就是在.m文件中声明一个属性,不能被继承也不能被子类访问

相对私有属性:是指被实例变量修饰符@private修饰的属性,可以被子类继承,但不能被子类访问

2)

私有方法:

什么是私有方法?私有方法是干吗用的?我为啥呢么要用私有方法?

 私有方法就是在.m文件中声明的一个方法,在.h文件中没有提供该方法的接口.

 没有接口,我在main函数(或者别的文件)中肯定不能通过对象去调用,这就隐藏了这个方法,

如下所示

work方法是不能在main函数中访问的

---------------------------第一步在.h里面声明一个方法------------------------

#import <Foundation/Foundation.h>
@interface Person : NSObject
//声明一个对象方法
-(void)run;   //一个接口
@end

---------------------------第二步在.m里面实现方法并添加一个不在.h文件中声明的方法------------------------

#import "Person.h"
@implementation Person 
-(void)run{
    NSLog(@"班长在跑");
}
//私有方法(.m中定义的,.h中并没有声明)
-(void)work{
    NSLog(@"班长工作,月薪28k");
}
@end

 

 

那么重点来了,有吊用?

举个例子:同样我写一个run方法,但是我的run方法写的很高级,功能很多,他一边跑还一边统计时速,油量,车胎热度等等运行中的指数,如果我都把这些指数提供接口

,那么代码就不好维护,可读性也不高,而且这些参数我只是希望客户能看到就可以,那么我把这些参数方法的实现写到.m文件中的run方法中,我只需要在main函数中,用

对象去调用run方法就会得到了这些参数,这就是私有方法的运用

6.description方法的介绍和重写:

什么是description方法?

description方法其实就是当我以%@方式打印对象时系统会自动调用这个方法(打印类对象调用类方法,打印对象调用对象方法)

打印结果如下图所示:打印类结果是类名,打印对象默认会返回<类名:内存地址>(示例是重写了该方法)

虽然字符串也是对象,但字符串在使用@%打印时情况特殊

 

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

- (NSString *)description {

    // 下面代码会引发死循环

    // NSLog(@"%@",self);

    //return @"abc";

}

延伸:

字符串对象的length方法:计算的是字符串的字数,而不是像strlen函数那样,计算的是字符数。如“哈ha123”length得出的结果是6,返回unsigned long类型,而strlen函数得出的结果是8,因为一个汉字占3个字节。

提示:字数也包括空格。

7.多态:

多态:父类指针指向子类(肯定要有继承)

举例:Animal 和 Dog 都有eat的方法

Animal *anl = [Dog new];

[anl eat];

调用的是子类Dog的eat方法

为什么呢? 

对象调用方法,实际上是通过isa指针找到代码区的eat方法,而多态就像是把儿子的名字改成了他爹的名字,但方法还是儿子的方法

8.SEL:全称Selector 表示方法的存储位置。

Person *p=[[Person alloc] init];

[p test];

寻找方法的过程:

(1)首先把test这个方法名包装成sel类型的数据;

(2)根据SEL数据找到对应的方法地址;

(3)根据方法地址调用相应的方法。

(4)注意:在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。

关于_cmd:每个方法的内部都有一个-cmd,代表着当前方法。

 

注意:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去寻找对应的方法地址,找到方法地址后就可以调用方法。这些都是运行时特性,发消息就是发送SEL,然后根据SEL找到地址,调用方法.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/yiyuanchenfeng/p/5967359.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值