关于OC中变量相关知识点

本文详细介绍了编程中变量的分类(基本数据类型、非基本数据类型)、作用区域(全局、局部、静态),以及变量的访问范围和属性、成员变量、实例变量的区别。还涵盖了自动注册、外部链接、内存管理策略(strong、weak、assign、copy)和ARC时代的变化等内容。
摘要由CSDN通过智能技术生成

众所周知,变量是用来存储数据的
围绕着变量,有很多知识点,总结归纳一下

  • 变量的类型
  • 变量的作用区域
  • 局部变量
  • 全局变量
  • 静态变量
  • 变量的访问范围
  • 属性
  • 成员变量
  • 实例变量
  • synthesize
  • dynamic

变量的类型

变量大致分为两大类型:

  • 基本数据类型
  • 非基本数据类型(指针/对象/Class类型)

基本数据类型包括:

int/float/double/bool/enum/struct

NSInteger:typedef long NSInteger;
CGFloat: typedef CGFLOAT_TYPE CGFloat;
而CGFLOAT_TYPE的定义是:

 #if defined(__LP64__) && __LP64__
 # define CGFLOAT_TYPE double
 #else
 # define CGFLOAT_TYPE float
 #endif

也就是,CGFloat就是float或double,依然是基本数据类型

bool、BOOL、boolean

bool:其实就是C语言中的bool
BOOL: typedef bool BOOL; 也就是BOOL和bool没有任何区别
apple官方文档关于BOOL的定义

Boolean:typedef unsigned char Boolean;(进入Xcode,可以看到)

非基本数据类型

存储的指针类型的变量类型,也就是存储的是地址的变量类型

变量的作用区域

根据变量写在的位置不同,有不同的作用区域

全局变量:在全局写的变量,整个文件都可以访问该变量
局部变量:在函数内部的变量,只有该函数内部才能访问,出了作用域不可访问

以上,默认都是auto修饰

静态变量:使用static修饰的全局变量或局部变量

静态局部变量:

  • 可以延迟变量的生命周期,本来是在大括号就回收的变量,其生命周期可以延迟至程序结束
  • 只初始化一次

静态全局变量:只有当前类可以访问该变量

//测试static
- (void)testStatic
{
    int c = 1;
    static int d = 1;
    for(int i = 0; i < 3; i++)
    {
        int a = 1;
        printf("int a变量为 %d \n",a);
        a++;
        
        static int b = 1;//因为static变量只初始化一次,所以第二次for循环及以后都不执行这一句代码
        printf("static int b变量为 %d\n",b);
        b++;
        
        printf("int c变量为 %d \n",c);
        c++;
        
        printf("static int d变量为 %d \n",d);
        d++;
        
    }
}

打印结果:
int a变量为 1
static int b变量为 1
int c变量为 1
static int d变量为 1
int a变量为 1
static int b变量为 2
int c变量为 2
static int d变量为 2
int a变量为 1
static int b变量为 3
int c变量为 3
static int d变量为 3

auto register static extern

auto

auto: 表明变量具有自动存储类型
auto说明符只能用在具有代码块作用域的变量的声明中, 但是由于这类变量本身就具有自动存储类型(存储于运行时堆栈中), 所以auto通常只是起显式说明的作用.

register

register: 表明变量具有硬件寄存器存储类型
register也只能用在具有代码块作用域的变量的声明中, 表示程序员希望将该变量放在CPU的寄存器中, 从而可以比普通变量更快的访问和操作该变量. 但是无法获得寄存器存储类型的变量的地址, 并且具体是否会将register声明的变量存放于寄存器中由编译器决定

register声明的变量常称为寄存器变量

static

static: 表明变量具有静态存储类型或则标识符具有内部链接属性

extern

extern: 表明标识符具有外部链接属性或者该变量在别处定义

static修饰函数

static修饰的函数是一个内部函数,只能在本文件中调用,其他文件不能调用

变量的访问范围

变量根据访问访问,可以分为:public、protected、private、package

public

声明为 @public 的实例变量是访问控制中开放范围最广的,其允许外界可以直接访问(当然,前提是引入包含该声明的头文件)。

protected

声明为 @protected 的实例变量只能在本类、本类的分类以及子类中使用。注意,当不使用任何访问控制修饰符时,类中实例变量默认即为 @protected(注意:类扩展中是个例外,详见「类扩展」一节)

private

声明为 @private 的实例变量是访问控制中开放范围最小的,只能被本类和本类的分类访问到,子类中也无法访问。在类声明中的属性(@property),系统会自动为我们创建一个 _ 开头的实例变量,这个实例变量的可见程度默认也是 @private。

package

同一个“体系内(框架)"可以访问,介于@private和@public之间

属性、成员变量、实例变量

属性

属性,property,是指的右@property建立的
例如:@property (copy, nonatomic) NSString *postId;
@property负责三个事情:

  1. set,get方法的声明
  2. set,get方法的实现
  3. 生成_postId的实例变量

成员变量

成员变量指的是

@interface
{
	int age;
	NSObject *obj1;
}
@end

大括号中间的内容

实例变量

实例变量ivar(instance variables),指的是{}中,是对象的一类,也就是有指针的,非基本数据类型。例如NSObject *obj1;

也就是说,成员变量 = 实例变量 + 基本数据类型变量

需要注意⚠️的是:如果既有成员变量,又有属性,则先写成员变量,再写属性

参考:OC中属性和成员变量(一)概念篇

也有资料说,成员变量 = 实例变量
属性property
实例变量ivar
成员变量的英文单词是?

synthesize

synthesize: 合成

Xcode4时,@property只能在.h中生成getter、setter方法的声明, 需要在.m中手动加上@synthesize,才会有setter\getter的实现,以及对应的变量_property;

Xcode4之前 :
property = setter方法声明 + getter方法声明
synthesize = ivar + setter实现 + getter实现

在之前的OC中,写一个@property,还需要对应写一个@synthesize
@synthesize age = _age;
其作用是,将你写的age属性,和_age成员变量联系起来(@synthesize 合成访问器方法)

既是生成成员变量+方法实现,也是将成员变量和属性关联起来

现在,Xcode不需要写@synthesize age = _age;,在写@property的时候,会自动给加上

从Xcode5开始, 编译器有了自动合成机制(Auto property synthesis),只写@property就可以自动生成_property成员变量和getter、setter方法的声明和实现, 不需要写synthesize了。

Xcode5之后, 其实property和synthesize的职责没变,只是编译器会默认添加synthesize, 真实的情况还是 :
property = setter方法声明 + getter方法声明
默认添加的synthesize = ivar + setter实现 + getter实现
---->看起来变成了 property = ivar + setter(声明+实现) + getter(声明+实现)

自动合成机制(Auto property synthesis) :如果我们既没有写synthesize也没有写dynamic,那编译器默认会为我们添加:@synthesize property = _property;
如果不存在_property,则会创建一个_property成员变量
如果存在,则不会添加成员变量

因此在类内部我们可以使用 _property 来进行赋值、取值操作。

@synthesize到底对属性干了什么, 使用场景总结

但,自动合成机制有时候会失效

什么情况下自动合成会失效 ?

  • 同时重写了属性的setter和getter时;
  • 重写了只读属性的getter时;
  • 使用了@dynamic时;
  • 在 @protocol 中定义的所有属性;
  • 在category 中定义的所有属性;
  • 父类已有的属性, 子类重载的属性不会自动合成;

现在,synthesize的作用是:

  1. 需要给属性起个别名
  2. 手动添加了 setter/getter 方法
  3. 实现了带有peoperty属性的protocol

dynamic

@dynamic告诉编译器: 属性的setter,getter方法由用户自己实现, 不自动生成

使用@dynamic age;就不会自动生成age的setter/getter方法的实现,也不会自动生成成员变量(ivar)。
需要注意的是,age的setter/getter方法的声明是不受影响的。

属性特质

@property (nonatomic, readwrite, copy) NSString *firstName;
属性拥有四类特质:
原子性、读写权限、内存管理语义、方法名

原子性:atomic(默认)、nonatomic
读写权限:readonly、readwrite(默认)
引用计数
setter\getter

原子性

默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomic)
但,开销比较大,因此,异步不使用atomic
atomic可以保证获取和赋值是线程安全的
但是,并不能保证在使用过程中是线程安全的

nonatomic,不使用同步锁

读写权限

readwrite,默认方法,拥有getter和setter方法
readonly,只有getter方法

内存管理语义

属性用于封装数据,而数据则要有“具体的所有权语义”

  • assign: 针对“纯量类型”(CGFloat 或 NSInteger等)的简单赋值
  • strong: 此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去
- (void)setFoo:(id)foo {
	[foo retain];//保留新值
	[_foo release];//释放旧值
	_foo = foo;//更新实例变量
}
  • weak: 此特质表明该属性定义了一种“非拥有关系”。在属性所指的对象遭到摧毁时,属性值也会清空(nil out)
  • copy: 与strong类似。然而设置方法并不保留新值,而是将其copy
    copy顾名思义,就是复制。
    copy的目的:产生一个副本对象,跟原对象互不影响。
    修改了原对象,不会影响副本对象;
    修改了副本对象,不会影响原对象。

NSString、NSDictionary、NSArray等Foundation框架中的某些对象具备了copy能力。
iOS提供了两种拷贝方法:
copy:不可变拷贝,产生不可变的副本;
mutableCopy:可变拷贝,产生可变的副本。

深拷贝和浅拷贝
深拷贝:内容拷贝,产生新的对象;
浅拷贝:指针拷贝,没有产生新的对象,只是引用计数器+1.
在这里插入图片描述

不存在使用mutableCopy修饰的属性。
@property (mutableCopy, nonatomic) NSString *name;

问:assign和weak的区别有哪些?

assign
可以修饰基本数据类型,如int、BOOL等
可以修饰对象类型,但不改变其引用计数器
当使用assign修饰对象,在对象释放的时候,会产生悬垂指针

weak
不可以修饰基本数据类型
修饰对象类型,但不改变其引用计数器
当使用weak修饰对象,在对象释放的时候,会自动置为nil

问:strong和copy的区别有哪些?
strong和copy对对象的修饰:

“strong” 是一种引用计数的属性修饰符,当声明一个对象的属性为 “strong” 时,意味着这个属性会对对象进行强引用

“copy” 是一种值复制的属性修饰符,当声明一个对象的属性为 “copy” ,那意味着你希望这个对象的值能被复制到这个属性所指向的新对象中。这个修饰符常常被用在NSString、NSArray、NSDictionary等不可变对象上。
"copy"意味着当你把一个新的字符串赋值给这个属性,那么这个属性会拷贝一份新的字符串,而不是直接引用这个字符串。

strong和copy对block的修饰:

在没有ARC(Automatic Reference Counting)的时代,Block应该使用copy修饰。因为Block在没有使用copy的情况下,默认是分配在栈上的(局部Block),而栈上的对象会在函数返回的时候销毁。如果尝试在函数外部对这个Block进行访问,可能会导致程序崩溃。使用copy可以把Block从栈复制到堆(全局Block)上,从而延长了Block的生命周期。

然而,在开启了ARC的环境中,编译器做了一些智能处理。这部分包括当你将Block赋值到"__strong"修饰的属性时,编译器会自动的进行一次copy操作,将Block从栈复制到堆上。因此,你可以在ARC环境下使用"strong"来修饰Block,并且安全地获得Block的所有权

因此,现在开启ARC的项目中,你可以选择用“strong"来修饰block,但为了表达语意,更多的是使用“copy"来修饰block。

方法名

@property (nonatomic, getter = isOn) BOOL on;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值