OC入门到深入理解OC

属性,使用点表示法;

@property默认会自动生成一个_开头的私有成员变量。(在.m文件中生成)以及@gettter和@setter方法的声明和实现。如果需要对传入的数据进行过滤,必须重写@getter/@setter方法,@property则不会自动生成一个 _开头的私有成员变量。@property只能写在类的声明(@interface)中。

数组NSArray

1.不可变数组

  NSArray *array = [NSArray arrayWithObjects:@"iphone", @"123",nil];//nil不可少,代表结束的标志
//获取元素个数
        [array count];
        NSLog(@"count = %ld", [array count]);
//根据index值获取对象
NSLog(@"%@", [array objectAtIndex:0]);
或者直接引用
NSLog(@"%@", array[1]);

2.可变数组NSMutableArray

NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"zhao",@"peng",@"pengpeng", nil];
    //添加元素
    [mArr addObject:@"hahah"];
    for(int i = 0;i < [mArr count]; i ++){
        NSLog(@"%@",mArr[i]);
    }
//删除元素
[mArr removeLaseObject];
NSLog(@"%@", mArr); // 删除了最后一个;

//替换
[mArr replaceObjectAtIndex:0 withObject:@"BSD"]; // 指定位置替换掉

//交换指定位置的元素
[mArr exchangeObjectAtIndex:0 withObjectAtIndex:1];
//数值与对象转换
//因为数组和字典中存放的只能是对象类型. 所以一般的数值是不能直接存入的, 只能转化成对象元素才能存入!
//如:
int c = 100;
float f = 1.2;
NSNumber *c1 = [NSNumber numberWithDouble:c];
NSNumber *f1 = [NSNumber numbserWithDouble:f];
//这是把数值转化为了NSNumber格式存入到了字典, 当然你也可以数值转化为字符串格式存入!
NSMubleArray *mArr2 = [NSMutableArray arrayWithObjects:c, f, nil];


Array ccontainsObject : x 用来判断x是否在Array中,(内部比较的时候是判断所在地址是否相同)

其他判断方法如下:

- (BOOL)isEqual:(id)object {

    if (object == self) {

        return YES;

    }
//
    if (![object isKindOfClass:[NewsEntity class]]) {

        return NO;

    }
/
    NewsEntity *entity = (NewsEntity *)object;

    if ([entity.title isEqualToString:self.title]) {

        return YES;

    }

    else {

        return NO;

    }

}

alloc和init一般同时使用,使用之后,立即初始化并且分配空间。

init一般返回self

self和super关键字

OC提供了两个保留字self和super,用于在方法定义中引用该执行方法的对象。

OC中的self相当于C++、Java中的 this指针 。

super发送消息可以将方法实现分发给其父类(实现父类的方法)

父类的私有实例变量虽然能被子类继承,但不能被子类使用。

父类的隐藏实力变量(在.m文件中定义的),不能被子类继承。

OC中的继承是单继承。即一个类只能有一个父类。

子类可以重写父类的方法。当子类对从父类继承得到方法不满意时,可以改写之,只要在子类的.m文件定义了一个返回类型、方法名、参数都与父类原方法相同的方法。

多态的原理

动态绑定:动态类型能使程序直到运行时才确定对象所属的类型。动态类型绑定能使程序直到运行时才确定要对象调用的方法。

注意:「存在多态时,父类可以调用子类特有的方法,单要经过强制类型转换

不存在多态时,父类不可以调用子类特有的方法,即使时强制类型转换也不行。」

Category

OC特有,被翻译为:分类、类别、类目

作用:在不修改原有的类、不需要原有的类的代码的基础上增加新的方法(也只能是方法,不能增加实例变量)。

使用条件及好处:

(1)在分模块开发一个庞大的类时,有利于分工合作。

(2)替代子类的继承。因为继承可能影响原有的继承系统,而且只能单继承。而一个类可以有多个Category。

(3)一个Category中可以有多个方法,所以利用它来讲方法归类,使得更好地更新和维护。

使用方法:声明------>实现------->使用。

注意:「category 只能给类增加方法,不能增加成员变量、@property(可能编译不报错,但运行有问题)

category的@implementation…@end可以单独放在一个.m文件,也可以放在对应的.h文件中@implementation…@end之后。也可以两者都放在原有类的.h文件的@implementation…@end之后,直接声明、实现category

category 可以使用原有类的私有实例变量,但原有类的隐藏实例变量仍对categroy不可见

当有多个分类和原有类都有同名方法时,原有类的方法被覆盖,而且执行最后编译的category方法」

category可以重写原有类的方法的,但系统会报警:

不推荐category 中重写原有类的方法,因为:

(1)一旦category中重写了方法,则原有类的同名方法被覆盖掉,再也不能访问,所以也不能像子类那样发送消息给super就可以调用父类的同名方法,所以不能在原有方法发基础上增加功能,除非你想重复些那些功能。

(2)从上面的例子我们可以知道当多个类拥有同名的方法时,调用这个方法的结果貌似不是我们能控制的。

(3)在category中重写的不仅影响到原有的类,而且会影响原有类的子类,因为那些子类很可能继承并使用了这个方法,后果不言而喻。

了解了category的应用和优缺点,我们再来看下它的原理:

在Objective-C Runtime Reference中的简介:

Category
An opaque type that represents a category.

即Category是一种类型,这个类型代表了一种分类。

而在runtime.h中的定义:

typedef struct objc_category *Category;

所以Category是指向一个objc_category结构体的指针。而objc_category结构体的声明为:

struct objc_category{
		char 	*category_name                                                       OBJC2_UNAVAILABL
		char *class_nameO                                                          OBJC2_UNAVAILABL
		struct objc_method_list *instnace_methods                                 OBJC2_UNAVAILABLE
		struct objc_method_list *class_methods                                    OBJC2_UNAVAILABLE
		struct objc_protocol_list *protocols                                      OBJC2_UNAVAILABLE
}                                                                               OBJC2_UNAVAILAB

分别存储了指向分类名、原有类的名称、对象方法列表的结构体、类方法的结构体、遵守的协议列表的结构体的五个指针。

Extension

也是一个特殊的category,特殊在与它没有名字!所以也叫匿名分类。

而它不仅可以给原有类的增加方法,还可以增加成员变量。

img

Protocal

protocal(协议)类似java中的接口,定义了一些类需要公用到的方法,只要遵守这个协议,就可以拥有这些方法并可以实现它们,这样可以避免许多重复的代码。

协议的定义:

//协议的定义:
@protocol 协议名称<NSObject>//默认遵守NSObect名称
  //方法声明
@end
  
  //协议的采纳
  //创建类的时候遵守某个或多个协议
  @interface 类名: 父类<协议名称1,协议名称2>
  @end
  
  //某个协议也可以遵守其他协议
  @proptocal 协议名称<协议名称1.协议名称2>
  //方法声明
  @end
  //proptocal中的@require和@optionl的使用:
  //@required:表示这个方法必须要实现(默认)
  //@optional:表示这个方法不是必须要实现的
  
  //protocal类型限制:
  //增加<hoiseHold>以后,表示obj只能赋值遵守了houseHold协议的对象
  id<houseHold> obj = mm;
  //表示obj2赋值的时候,必须是Girl对象,并其遵守了houseHold的协议
  Girl<houseHold> *obj2 = mm;
  //让定义的成员属性(对象)遵守某个协议
    @property (nonatomic,strong) Dog<协议名称> * dog;
  

@property的概念

@property是声明属性的语法,作为Objective-C的一项特性,主要的作用就在于封装对象中的数据。它可以快速方便的创建实例变量,并未实例创建存取器,并允许我们通过点语法使用存取器。(setter,getter)

@synthesize合成实例变量的规则

1.如果指定了成员变量的名称,会生成一个指定的成员变量

2.如果这个成员已经存在了就不再生成了

3.如果实现了@synthesize,但没有指定成员变量的名称,会自动生成一个与属性同名的成员变量。

@dynamic。需要自己生成setter和getter

@property的特性的关键字

原子性

atomic(默认):原子的,线程安全的,系统会自动加上同步锁(多个线程在写入原子属性时,能够保证同一时间只有一个线程执行写入操作,但是同一个时间多个线程都可以取值),影响效率,使用少。

nonatomic:非原子的,线程不安全的,效率高,使用广泛。

存取器控制

readwrite(默认):readwrite是默认值,表示属性同时拥有setter和getter。

readonly:表示只有getter。

内存管理

assign(默认):用于基本数据类型,如int、float、double、NSInteger、CGFloat等

strong:强引用,对传入对象拥有所有权。

weak:弱引用,对传入对象没有所有权。「在ARC中,有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,定义IBout控件属性一般使用weak,因为控件已经被控制器的子视图强引用,没有必要再让控制器引用一次」

copy与strong类似。

用@property声明NSString、NSArray、NSDictionary经常使用copy关键字,是因为它们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份,拷贝的对象是不可变的。

创建对象的两种方式

new

alloc + init

构造方法概念:用来初始化对象的方法,是个对象方法,以-开头。例如init方法就是一个构造方法。

重写父类的构造方法或为类添加新的初始化构造方法的几个必不可少的条件:

(1)一定要调用父类super的初始化方法。一般直接继承NSObject的类都要调用super的init方法,[super init]返回的是当前的OC对象,调用super的init方法的目的就是初始化从父类继承而来的一些成员变量和其他属性。例如对象内部的isa指针是从父类NSObject继承而来的isa指针。继承自其他类的更容易理解,直接把子类初始化方法中的形参----赋给的父类的初始化方法内对应的参数来初始化从父类继承来的成员及属性。

切记:每个对象在初始化的时候都是先初始化从父类继承而来的成员,然后再初始化自身扩展的成员。

(2)调用[super init]或者[super 别的父类初始化参数:参数…]后返回的对象(实际上是对象指针)id一定要赋给self;

即self = [self init]或者self = [super other Parent Init]

(3)定义返回类型是id,最后添加语句return self;语句返回自身对象指针。

4.构造方法的几种格式:

(1)重写init方法。一般用于直接继承NSObject的类。

(2)为类新增别的构造方法。

切记:只要是构造方法,返回类型都是id,最后都返回self,都要初始化从父类来的成员。即if(self = [super parent init]){//初始化子类扩展成员的语句}注意;可以为一个类同时提供多个初始化方法。

5.重写构造方法init方法的目的:为了让对象一创建出来其创建出来去成员变量就有一些固定值。

6.一个子类出发方法的调用过程:

与递归类似,层层调用直到NSObject的init方法调用后,再层层返回按栈的顺序初始化父类的成员变量。例如当调用子类的构造方法初始化之类的对象时,首先会执行if(self = [super parent init])这样的语句进入到直接继承的父类的初始化方法中…层层初始化,层层返回id,级级赋值初始化,最后进入的最先初始化值并进行id返回。最后返回一个id赋值给self。

注意:「不要将从父类继承而来的实例变量放到子类的初始化方法中单独去初始化,为每个类提供的初始化方法都应该把这个类所有的实例变量都一一赋值初始化,当有子类继承自这个类时只需super调用即可,然后子类只需初始化自身扩展成员。遵循各自的事情各自做的原则。」

Block

概念:Block对象是一个C级别的语法和运行机制。它允许你写一些函数语句,这些函数语句可以传到API中,可以有选择性的储存,可以用于多线程中,而且还可以引用局部变量和保存对局部变量的存取。…Block可以同时用在C,C++,或者Objective-C,使得程序更有效率和更好的维护。

基本用法

声明------>创建------->使用

int (^sumBlock)(int, int);

即:返回类型 (^Block名称)(参数列表) =^ 返回类型(参数列表){//代码实现};

声明了一个Block对象sumBlock,sumBlock,sumBlock指向了一个代码块,代码块需要两个int型参数(int,int),返回类型是int。

Block表达式以“^”开始,以“;”结束,使用Block方式也调用函数类似,如:

当Block无参数、无返回值时,则是Block的最简单形式:

格式:Void(^block名)() = ^{代码块;}

使用:block名();

无参数时,参数列表可省略。也可以有参数无返回值,

block的typedef

格式如下:

Typedef 返回值类型 (^类型别名) (参数类型列表);

typedef int(^BLOCk) (int, int);

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        BLOCk sumBlock2;
        sumBlock2 = ^(int num1, int num2)
        {
            NSLog(@"我是sumBlock2");
            return num1 + num2;
        };
        
        sumBlock2(2, 3);
        
    }
    return 0;
}

当函数的参数不好设计或者返回值比较复杂时,block显得比较有用。

Block作函数的返回值

selector

1.SEL

它是@selector()在OC中的表示,可以说是一种数据类型,在OC中的定义:typedef struct objc_selector *SEL;

2.@selector()

它是selector方法选择器

方法的selector用于表示运行时方法的名字。OC在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。两个类之间,只要方法名相同,那么方法的SEL就是一样的,每一个方法都对应着一个SEL。所以在OC同一个类(及类的继承体)中,不能存在2个同名的方法,即使参数类型不同也不行。

当然,不同的类可以拥有相同的selector,这个没有问题。不同类的实例对象执行相同的selector时,会在各自的方法列表中去根据selector去寻找自己对应的IMP。工程中的所有SEL组成一个set集合,如果我们想到这个方法集合中查找某个方法时,只需要去找到这个方法对应的SEL就行了,SEL实际上就是根据方法名hash化了一个字符串,而对于字符串的比较仅仅需要比较他们的地址就可以了,可以说速度上无语伦比!本质上,SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法),它的存在只是为了快加方法的查询速度。

通过下面三种方法可以获取SEL:
a  sel_registerName函数
b  Objective-C编译器提供的@selector()方法
c  NSSelectorFromString()方法

2、IMP

IMP实际上是一个函数指针,指向方法实现的地址。其定义如下:

id (* IMP)(id, SEL,...)

第一个参数:是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针)第二个参数:是方法选择器(selector)接下来的参数:方法的参数列表。

SEL就是为了查找方法的最终实现IMP的。由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的IMP。取得IMP后,我们就获得了执行这个方法代码的入口点,此时,我们就可以像调用普通的C语言函数一样来使用这个函数指针了。

3、Method

Method用于表示类定义中的方法,其结构体中包含一个SEL和IMP,实际相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法来实现代码。

4、消息

Objc中发送消息是用中括号(【】)把接收者和消息括起来,而直到运行时才会把消息与方法实现绑定。

eg:[receiver message]会被编译转化:

objc_msgSend(receiver, selector)

如果消息有参数,则为:

objc_msgSend(receiver, selector, arg1, arg2, ...)

如果消息的接收者能够找到对应的selector,那么就相当于直接执行了接收者这个对象的特定方法;否则,消息要么被转发,或是临时向接收者动态添加这个selector对应的实现内容,那么就干脆玩完崩溃掉。

简单使用:

@selector是查找当前类的方法,而[object @selector(方法名:参数名…)];是取object对应类的相应方法

查找类方法时,除了方法名,方法参数也查询条件之一。

可以用字符串来找方法SEL变量反向查处方法名字字符串

tips:

当父类的便利构造器想要被子类继承的话,在父类的便利构造器中要使用[self alloc]来创建对象,这样才能使子类调用父类的便利构造器.

assign 设置方法 只会执行针对 纯量类型(scalar type, 例如 CGFloat或NSInteger等)的简单赋值操作;

strong 此特质表明该属性定义了一种“拥有关系”(owning relationship)。为这种设置新值时,设置方法会先保留新值,并释放旧值,然后将新值设置上去。

weak 此特质表明该属性定义了一种“非拥有关系”(nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值,此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空。

unsafe_unretained:此特质的语义同assign相同,但是它适用于“对象类型”(object type),该特质表达一种“非拥有的关系”(不保留,unretained),当目标对象遭到摧毁时,属性值不会自动清空(“不安全”,unsafe),这一点与weak有区别。

copy:此特质所表达的所属关系与strong类似,然后设置方法并不保留新值。而是将其“拷贝”(copy)。

__weak

1.若附有__weak修饰符的变量所引用的对象被释放,则将nil值赋值给该变量;

2.使用__weak修饰符的变量,就是使用注册到autoreleasepool中的对象

tips:如果有大量地使用附有____weak修饰符的变量时(多线程的情况下),最好先暂时赋值给附有____strong修饰符的变量后再使用。

使用__weak修饰符可以很好的避免循环引用的出现,这也是我们blocj里面使用weakSelf而不是用self的原因,因为block会捕获self,而self又持有block,导致两个互相指向从而发生内存泄漏,代理也是一样也要用weak修饰来避免循环引用。

__strong

strong修饰符是oc对象的默认修饰符,一般我们在生成一个对象的时候就默认添加了strong修饰

id arr = [[NSArray alloc] init];  //等同于 id __strong arr = [[NSArray alloc] init];

strong修饰符符表示对对象的强引用,那什么是强引用,什么是弱引用,其实都是编译器层面的特性,与实际的内层方面没有太大的关系,编译器在处理使用strong修饰符的变量和在处理使用weak修饰符修饰的变量的机制是不一样的。持有强引用的变量在超出其作用域时会被释放掉,强引用失效,那么其引用的对象也会随之废弃掉。

  {
        //自己生成的对象自己持有
        id __strong arr = [[NSArray alloc] init];
        NSLog(@"%@",arr);//输出对象内存地址
    }//变量arr超出其作用域,强引用失效,所以其引用的对象[[NSArray alloc] init] 被废弃
   
    {
        id arr = [[NSArray alloc] init];
        [arr release];//调用release方法直接废弃对象
        NSLog(@"%@",arr); //输出nil
    }

如果用strong去修饰的是非自己生成生成并持有的对象,只要超出变量的作用域其结果就跟上面的一样。

在strong修饰符修饰的对象之间可以互相进行赋值操作,赋值之后新变量也是strong类型,原变量被废弃。原变量所引用的对象也随之被废弃,原变量与新变量指向同一块内存空间。

__strong修饰的变量不仅仅只在变量作用域中,在赋值上一样的可以管理其对象的所有者。

apple:通过strong修饰符,人们就可以不用去输入retain和release代码也可以正确的管理内存。这就是使用引用计数来管理内存的思考方式:

1.自己生成的对象自己所持有,

2.非自己生成的对象自己也能持有,

3.不再需要自己持有的对象时释放,

4.非自己持有的对象无法释放。

只需要通过对带strong修饰符的变量赋值就可以达到 前两项自己生成的对象自己持有和非自己生成的对象自己也能持有的效果,通过废弃带strong修饰符的变量(变量作用域结束或是成员变量所属对象废弃)或者对变量赋值都可以做到 不再需要自己持有的对象时释放。最后一项非自己持有的对象无法释放,因为 不必再次键入release,所以原本就不会执行,这些都满足引用计数式内存管理的思考方式。

instancetype

instancetype 是clang 3.5开始提供的一个关键字,表示某个方法返回的未知类型的OC对象。 == id?

Cocoa的命名规则

1.类方法中,以alloc或new开头

2.实例方法中,以autorelease,int, retain 或者self开头

会返回一个方法所在类类型的对象,这些方法就被称为是关联返回类型的方法。换句话说,这些方法的返回结果以方法所在的类为类型,eg:

@interface NSObject
+ (id)alloc;
- (id)init;
@end
 
@interface NSArray : NSObject
@end
NSArray *array = [[NSArray alloc] init];

按照Cocoa的命名规则,语句[NSArray alloc]的类型就是NSArray* 因为alloc的返回类型属于关联返回类型。同样,[[NSArray alloc] init]的返回结果也是NSArray* 。

instancetype

1.作用

如果一个不是关联返回类型的方法,如下:

@interface NSArray
+ (id)constructAnArray;
@end

当我们使用如下方式初始化NSArray时:

[NSArray constructAnArray];

根据Cocoa的方法命名规范,得到的返回类型就和方法声明的返回类型一样,是id。但是如果使用instancetype作为返回类型,如下:

@interface NSArray
+ (instancetype)constructAnArray;
@end

当使用相同的方式初始化NSArray时:

[NSArray constructAnArray];

得到的返回类型和方法所在类的类型相同,是NSArray*!;

总结一下:instancetype的作用:就是使那些非关联返回类型的方法返回所在类的类型!

2.好处

能够确定对象的类型,能够帮助编译器更好的为我们定位代码的书写问题,比如:

[[[NSArray alloc] init] mediaPlaybackAllowsAirPlay]; //  "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
 
[[NSArray array] mediaPlaybackAllowsAirPlay]; // (No error)

上例中的第一行代码,由于[[NSArray alloc] init]的结果是NSArray* ,这样编译器就能根据返回的数据类型检测出NSArray是否实现mediaPlaybackAllowsAirPlay方法。有利于开发者在编译阶段发现错误。

第二行代码,由于array不属于关联返回类型方法,[NSArray array]返回的是id类型,编译器不知道id类型的对象是否实现了mediaPlaybackAllowsAirPlay方法,也就不能够替开发者忌食发现错误。

4.instancetype和id的异同

​ 相同点:都可以作为方法的返回类型

​ 不同点:

​ 1.instancetype可以返回和方法所在类相同的对象,id只能返回未知类型的对象;

​ 2.instancetype只能作为返回值,不能像id那样作为参数

dispatch_once 能保证任务只会被执行一次,即使同时多线程调用也是线程安全的。常用于创建单例、swizzeld method等功能。它的功能比较简单,接下来看使用方法和具体的原理。

dispatch_once 用原子性操作block执行完成标记为,同时用信号量确保只有一个线程执行block,等block执行完成后再唤醒当前等待的线程。

extern(只能用来修饰全局变量)。

1.与.h文件的关系

extern为了实现变量和函数的跨文件引用。

在.h中声明

#import <Foundation/Foundation.h>
@interface ExternModel : NSObject
     extern NSString *lhString;//这里由于带有extern所以会被认为是全局变量
@end

在.m文件中赋值

#import "ExternModel.h"
@implementation ExternModel
       NSString *lhString=@"hello";
@end

调用的时候,可以引入"ExternModel.h",否则无法识别全局变量。当然也可以通过不导入头文件的方式进行调用(通过extern调用)

2.extern引用变量

​ extern具备与.h文件很相似的跨文件访问的功能,但是extern则必须是全局变量(静态+非静态)。

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
      [super viewDidLoad];
       extern NSString *lhString;
       NSLog(@"%@",lhString);
}
@end

3.extern声明

​ extern声明,仅适用于修饰全局变量,不能修饰其他的变量。

const是用来修饰常量的,与宏的区别:

​ 编译时刻:宏是预先编译(编译之前处理),const是编译阶段处理。

​ 编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。

​ 宏的好处:宏能定义一些函数,方法。const不能。

​ 宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。

const的介绍:

​ const仅仅用来修饰右边的变量(基本数据变量p, 指针变量*p)

​ 被const修饰的变量是只读的。

  - (void)viewDidLoad { 
          [super viewDidLoad]; 
          // 定义变量 
          int a = 1; 
          // 允许修改值 
          a = 20; 

          // const两种用法 
          // const:修饰基本变量p 
          // 这两种写法是一样的,const只修饰右边的基本变量b 
          const int b = 20; // b:只读变量 
          int const b = 20; // b:只读变量 
          // 不允许修改值 
          b = 1; 
          // const:修饰指针变量*p,带*的变量,就是指针变量. 
          // 定义一个指向int类型的指针变量,指向a的地址 
          int *p = &a; 
          int c = 10; 
          p = &c; 

          // 允许修改p指向的地址 
          // 允许修改p访问内存空间的值 
          *p = 20; 
          // const修饰指针变量访问的内存空间,修饰的是右边*p1,
          // 两种方式一样 
          const int *p1; // *p1:常量 p1:变量 
          int const *p1; // *p1:常量 p1:变量 
          // const修饰指针变量p1 
          int * const p1; // *p1:变量 p1:常量 
          // 第一个const修饰*p1 第二个const修饰 p1 
          // 两种方式一样 
          const int * const p1; // *p1:常量 p1:常量 
          int const * const p1; // *p1:常量 p1:常量
 }

​ static修饰全局变量

​ 在全局变量前加static,全局变量就被定义成一个全局静态变量(全局变量和静态全局变量的生命周期是一样的,都是在堆中的静态区,在中哥工程执行期间内一直存在)

特点:

​ 存储区:静态存储区没变(静态存储区在整个程序运行期间都存在);

​ 作用域:全局静态变量在声明他的文件之外是不可见的。

​ 非静态全局 变量的作用域是整个源程序(多个源文件可以共同使用);

​ static修饰局部变量

​ 在局部变量之前加上关键字static,局部变量就被定义成为局部静态变量。

​ 特点:

​ 存储区:由栈变为静态存储区rw data ,生存期为整个源程序,只能在定义该变量的函数内使用。退出函数后,变量继续存在,但不能使用它。

​ 作用域:作用域仍然为为局部作用域,当定义它的函数或者愉快结束的时候,作用域随之结束。

static修饰函数

在函数的返回类型前加上关键字static,函数就被定义成为静态函数。

函数的定义和声明类型默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。只能被本文件中的函数调用,而不能被同一程序其他文件中的函数调用。

好处

​ 其他文件中可以定义相同名字的函数,不会发生冲突

​ 静态函数不能被其他文件所用

类方法

1.类方法可以调用类方法。

2.类方法不可以调用实例方法,但是类方法可以通过创建对象来访问实例方法。

3.类方法不可以使用实例变量(成员变量)。类方法可以使用,因为self不是实例变量。

4.类方法作为消息,可以被发送到类或者对象里面去(实际上,就是可以通过类或者对象调用类方法)

​ 好处

​ 1.不依赖于对象,执行效率高;(直接用类名调用)

​ 2.能用类方法尽量使用

​ 3.场合:当方法内部不需要成员变量是,就可以使用类方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值