ios 关键字
目录
01 static
02 extern
03 const
04 @property、@synthesize、@dynamic
01 static
- 修饰局部变量:只会初始化一次,值存储一份;只在当前作用域内被使用;可延长局部变量的生命周期。
- 修饰全局变量:在类外部不使用static修饰属性为全局变量,在项目工程全局范围内有效,即使没有引用头文件,也可以使用extern 访问声明全局变量(并没有分配内存),不能用于实现;使用static修饰全局变量,只在当前文件内有效,外部类不可以访问。
- 警告: static 写在interface外面编译是没有错误的,但是编译器会报警告,这么说这样的写法是不被编辑器认可的。(XCode11.4.1未发现警告)
错误:static 写在interface里面会直接报错,显然这样的语法是不被认可的。
正确:static关键字声明的变量必须放在implementation外面,或者方法中,如果不为它赋值默认为0, - 全局变量是不安全的,因为它可能会被外部修改,所以在定义全局变量时推荐使用static关键字修饰。
- 和const使用代替宏:如果使用static和const组合使用,不可以修改变量的值,否则编译器报错:
// .h文件
NSString *IsCanRun;
@interface BenzCar : NSObject
//NSString *name;// Cannot declare variable inside @interface or @protocol
@end
// .m文件
@interface BenzCar ()
//NSString *name;// Cannot declare variable inside @interface or @protocol
@end
NSString *BrandName; // Mercedes-Benz
static NSString *EngineTechnology;
@implementation BenzCar
- (NSInteger) manufactureCar {
static NSInteger allCarNum = 0;
allCarNum ++;
return allCarNum;
}
@end
// 使用
#import "BenzCar.h"
IsCanRun = @"YES";
extern NSString *BrandName;
BrandName = @"Mercedes-Benz";
// Undefined symbol: _EngineTechnology
extern NSString *EngineTechnology;
EngineTechnology = @"发动机核心技术";
02 extern
- 想要访问全局变量可以使用extern关键字(extern修饰的全局变量默认是有外部链接的,作用域是整个工程,全局变量定义不能有static修饰)。
- 在一个文件内定义的全局变量,在另一个文件中,通过extern声明全局变量,就可以使用全局变量。OC常用场景,在.m文件中声明初始化全局变量,在.h文件使用extern对外扩展(一般只引用.h文件)。
// .h文件
extern NSString * const SEX_M;
extern NSString * const SEX_F;
@interface Person : NSObject
@end
// .m文件
NSString * const SEX_M = @"male";
NSString * const SEX_F = @"female";
@implementation Person
@end
03 const
- 修饰常量
- 和static使用代替宏:如果使用static和const组合使用,不可以修改变量的值,否则编译器报错:
- const修饰基本数据类型和指针型数据类型的总结
// const 修饰基本数据类型
int const a = 100;
const NSInteger b = 100;
int c = 101;
a = 101;// Cannot assign to variable 'a' with const-qualified type 'const int'
b = 101;// Cannot assign to variable 'b' with const-qualified type 'const NSInteger' (aka 'const long')
a = c;// Cannot assign to variable 'b' with const-qualified type 'const NSInteger' (aka 'const long')
b = c;// Cannot assign to variable 'b' with const-qualified type 'const NSInteger' (aka 'const long')
// const 修饰基本数据指针类型
int a1 = 1;
int a2 = 2;
int * const c1 = &a1;
int const * c2 = &a1;
const int * c3 = &a1;
const int const * c4 = &a1;//Duplicate 'const' declaration specifier
const int * const c5 = &a1;
c1 = &a2;// Cannot assign to variable 'str1' with const-qualified type 'int *const'
*c1 = 10;
c2 = &a2;
*c2 = 101;// Read-only variable is not assignable
c3 = &a2;
*c3 = 101;// Read-only variable is not assignable
c4 = &a2;
*c4 = 101;// Read-only variable is not assignable
c5 = &a2;// Cannot assign to variable 'str1' with const-qualified type 'int *const'
*c5 = 101;// Read-only variable is not assignable
// const 修饰指针型数据类型
NSString *b1 = @"";
NSString *b2 = @"";
NSString * const str1 = @"1";
NSString const * str2 = @"2";
const NSString * str3 = @"2";
const NSString const * str4 = @"2";//Duplicate 'const' declaration specifier
const NSString * const str5 = @"3";
str1 = b2;// Cannot assign to variable 'str1' with const-qualified type 'NSString *const __strong'
str1 = @"12";// Cannot assign to variable 'str1' with const-qualified type 'NSString *const __strong'
str2 = b2;
str2 = @"12";
str3 = b2;
str3 = @"12";
str4 = b2;
str4 = @"12";
str5 = b2;// Cannot assign to variable 'str5' with const-qualified type 'const NSString *const __strong'
str5 = @"12";// Cannot assign to variable 'str5' with const-qualified type 'const NSString *const __strong'
// const 修饰可变指针型数据类型
NSMutableString * const mstr1 = [@"12" mutableCopy];
NSMutableString const * mstr2 = [@"12" mutableCopy];
const NSMutableString * mstr3 = [@"12" mutableCopy];
const NSMutableString const * mstr4 = [@"12" mutableCopy];// Duplicate 'const' declaration specifier
const NSMutableString * const mstr5 = [@"12" mutableCopy];
[mstr1 appendString:@"345"];
mstr1 = [@"abc" mutableCopy];// Cannot assign to variable 'mstr1' with const-qualified type 'NSMutableString *const __strong'
[mstr2 appendString:@"345"];
mstr2 = [@"abc" mutableCopy];
[mstr3 appendString:@"345"];
mstr3 = [@"abc" mutableCopy];
[mstr4 appendString:@"345"];
mstr4 = [@"abc" mutableCopy];
[mstr5 appendString:@"345"];
mstr5 = [@"abc" mutableCopy];// Cannot assign to variable 'mstr5' with const-qualified type 'const NSMutableString *const __strong'
【总结】:
int const a = 100;
不可以修改常量a的值
const NSInteger b = 100;
不可以修改常量b的值
指针相关:const与*的位置,及const在*的左边还是右边
int * const c1 = &a1;
可以修改常量指针指向的原内存中的内容,常量指针不可以指向其他的内存
int const * c2 = &a1;
不可以修改常量指针指向的原内存中的内容,常量指针可以指向其他的内存
NSString *const str1
不可以修改常量指针指向的原内存中的内容,常量指针不可以指向其他的内存
NSString const *str2"
不可以修改常量指针指向的原内存中的内容,常量指针可以指向其他的内存
NSMutableString const *mstr1
可以修改常量指针指向的原内存中的内容,常量指针可以指向其他的内存
NSMutableString *const mstr2
可以修改常量指针指向的原内存中的内容,常量指针不可以指向其他的内存
04 @property、@synthesize、@dynamic
在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正因为有了这种严格的命名规范,所以 Objective-C 这门语言才能根据名称自动创建出存取方法。
*完成属性定义后(@property(x, x, x,) classs var;ios6+),编译器会自动编写访问这些属性所需的方法,此过程叫做【自动合成(autosynthesis)】。【强调:这个过程由编译器在编译期执行,所以编辑器里看不到这些【合成方法(synthesized method】的源代码。】除了生成方法代码 getter/setter 之外,编译器还要自动向类中添加适当类型的【实例变量(var)】,并且在属性名【var】前面加下划线【】,以此作为实例变量的名字。
我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 getter/setter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 getter/setter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
@property的本质:@property = ivar(实例变量) + getter/setter(存取方法)
@property有两个对应的词,即 @synthesize和 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
@synthesize Xcode6以后省略, 默认在 .m的@implementation中添加这个@synthesize var = _var; 编译器期间,让编译器自动生成 getter/setter 方法;当有自定义的 getter/setter 方法时,会屏蔽自动生成该方法。
- 使用@synthesize 给属性添加【别名】后,将会覆盖自动合成的别名,只能使用新的别名。
- 在protocol中声明了属性(其实只有getter/setter方法,并没有属性实例),那么在使用它之前,需要使用@synthesize来合成实例变量。
@dynamic Xcode6以后省略, 默认在.m的@implementation中添加这个@synthesize var; 告诉编译器,不自动生成 getter/setter 方法,避免编译期间产生警告,然后由自己实现存取方法或在运行时动态创建绑定存取方法。例如:一个属性被声明为 @dynamic var,而且实际没有提供 getter/setter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
// 1
@implementation Person
- (void) printDes {
NSLog(@" name = %@", _name);
}
@end
// 2
@implementation Person
@synthesize name = name_New;// 别名
- (void) printDes {
NSLog(@" name = %@", _name);// Use of undeclared identifier '_name'
NSLog(@" name = %@", name_New);
}
@end
// 3
@implementation Person
@dynamic name;
@end
Person *p = [Person new];
p.name = @"J.K.";// [Person setName:]: unrecognized selector sent to instance
// 解决方案
// 3.1 - 直接注释
@implementation Person
//@dynamic name;
@end
// 3.2 - 显式提供实例变量,手动添加 getter/setter 方法
@interface Person ()
{
NSString *_name;
}
@end
@implementation Person
@dynamic name;
- (void)setName:(NSString *)name {
_name = [name copy];
_name = name;
}
- (NSString *)name {
return _name;
}
@end
// 3.3 - 显式提供实例变量,通过runtime机制在运行时添加属性的存取方法.
// 参考:在C函数中不能直接使用实例变量,需要将Objc对象self转成C中的结构体,
// 因此在Person类同样需要显式声明实例变量而且访问级别是@public
#import <objc/runtime.h>
@interface Person ()
{
@public // 可注释
NSString *_name;
}
@end
@implementation Person
@dynamic name;
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(setName:)) {
class_addMethod([self class], sel, (IMP)setName, "v@:@");
return YES;
}else if (sel == @selector(name)){
class_addMethod([self class], sel, (IMP)getName, "@@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void setName(id self, SEL _cmd, NSString* name)
{
if (((Person*)self)->_name != name) {
((Person *)self)->_name = [name copy]; // 点语法会导致死循环
}
}
NSString* getName(id self, SEL _cmd)
{
return ((Person *)self)->_name; // 点语法会导致死循环
}
@end