oc基础(部分)

本文详细介绍了iOS开发中const关键字的使用方法及其与宏定义的区别,同时深入探讨了属性property的概念、声明与实现,包括公共属性与专有属性的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内容转载自 宏创学院

3-const关键字

如果不想让某些变量的值改变,可以使用const关键字来修饰这些变量,如果添加const关键字,这些变量的值从头到尾都不会改变了。在iOS开发中,经常把字符串常量添加const关键字,从而替代宏(#define),因为const的执行性能要比宏定义要高。

1、const介绍

给变量添加const关键字,主要目的是为了防止定义的对象被修改。在定义有const关键字的对象时,要设置初始值。

在iOS开发中,有关const最常用的就是用来修饰NSString类型的字符串常量,这种使用方法在苹果提供的系统框架中随处可见。如下:

 
  1. UIKIT_EXTERN NSString *const UIViewControllerHierarchyInconsistencyException NS_AVAILABLE_IOS(5_0);
  2. UIKIT_EXTERN NSString *const UIViewControllerShowDetailTargetDidChangeNotification NS_AVAILABLE_IOS(8_0);

对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。

如下面的代码所示:

  • 当const修饰的是*x时,即指向字符串@”www.hcios.com”的指针指向是不能改变的,但是字符串的内容是可以改变的;
  • 当const修饰的是y时,即指向的字符串内容@”宏创学院”是不能改变的,但是指向的地址是可以改变的。
 
  1. void constString () {
  2. //const修饰的是指针变量*x
  3. NSString const *x = @"www.hcios.com";
  4. x = @"宏创学院";
  5. //const修饰的是字符串
  6. NSString * const y = @"宏创学院";
  7. /*不能修改
  8. y = @"www.hcios.com";
  9. */
  10. NSLog(@"x = %@",x);
  11. NSLog(@"y = %@",y);
  12.  
  13. }

2、const使用方法

在实际的iOS开发中,const最常用于定义字符串常量,并且为了维护方便,会把工程中所有的字符串常量都统一放在一个const类中。具体的实现方法如下:

  • 新建一个MyConst类;
  • 在.h文件中,声明所有的常量,需要注意一点:每个常量前面都加上extern关键字,否则在编译时会报声明重复错误。如下所示:
 
  1. #import <Foundation/Foundation.h>
  2.  
  3. /**高度*/
  4. extern int const height;
  5.  
  6. /**启动成功通知*/
  7. extern NSString * const launchSuccessNotification;
  • 在.m文件中,为每个常量赋值。如下所示:
 
  1. #import "MyConst.h"
  2.  
  3. int const height = 44;
  4.  
  5. NSString * const launchSuccessNotification = @"launchSuccessNotification";
  • 当需要使用const修饰的常量时,引入MyConst类即可。如下所示:
 
  1. #import <Foundation/Foundation.h>
  2. #import "MyConst.h"
  3. int main(int argc, const char * argv[]) {
  4. @autoreleasepool {
  5. /*const使用*/
  6. NSLog(@"my const height:%d",height);
  7. NSLog(@"my const notification string: %@",launchSuccessNotification);
  8. }
  9. return 0;
  10. }

运行结果:

QQ20160215-0

3、const与宏#define的区别

在开发过程中,如果涉及到字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来【替换】,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。

对于const和宏的使用,只要把握一个要点即可:凡是涉及到常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。

属性property介绍:1-属性定义

属性property在类定义中使用非常普遍,属性定义后,会创建一个与该属性名称同名且带下划线的实例变量,与此同时,编译器会根据属性的特性,自动合成该属性对应实例变量的存取方法。

1、属性中的一些概念

  • 属性property:通常指的是由对象封装或者存储的数据,例如对于UIView视图类对象,可以具有多个描述该视图类的属性,例如:大小、位置、颜色、是否可以响应用户交互等等。
  • 存取方法setter/getter:设置/获取该类的对象的属性值,执行设置/获取操作的方法称为存取方法;
  • getter方法:返回属性的值,名称与属性名相同。在实际开发中,常说的懒加载(lazy loading)就是对应了getter方法;
  • setter方法:设定属性的值,setter方法的形式为“setPropertyName:”,其中属性名称的第一个字母大写。

2、属性的声明

当声明一个属性时,需要在类的@interface代码部分编写,格式如下:

 
  1. @property (attributes) type name;

其中:

  • @property: 属性定义关键字;
  • attribute:属性的特性,提供了该属性的存储方式以及属性行为的说明,常见的关键字有: weak/strong, assign, copy, atomic/nonatomic;
  • type: 属性的类型说明,如:NSString, NSNumber,int, CGfloat以及一些自定义类;
  • name:属性的名称。

示例:

 
  1. #import <Foundation/Foundation.h>
  2. #import "MyClass.h"
  3.  
  4. @interface MyPropertyClass : NSObject
  5.  
  6. @property (nonatomic, copy) NSString *name;
  7. @property (nonatomic,assign) int age;
  8. @property (nonatomic,strong) MyClass *myClass;
  9. @property (nonatomic,weak) id delegate;
  10.  
  11. @end

3、属性的实现

声明的属性,编译器会自动生成一个与该属性同名且带下划线的一个实例变量,同时自动生成该实例变量的存取方法。只有在该对象的初始化方法中(懒加载lazy loading),应用程序才直接访问实例变量。一般情况下,当需要使用到对象的属性时,都需要使用“点语法”来直接获取属性的值或者设置属性的值。

  • 懒加载lazy loading:其实就是属性的getter方法,在iOS开发过程中,经常会使用属性的懒加载,所谓懒加载就是当需要获取某个属性的值时,再对该属性对象的实例变量进行初始化,从而提升内存的使用效率。如下为使用懒加载初始化name属性,其中_name是name属性对应的实例变量;
 
  1. -(NSString *)name{
  2. if (_name == nil) {
  3. _name = [NSString stringWithFormat:@"www.hcios.com"];
  4. }
  5. return _name;
  6. }
  • 获取属性的值:可以使用点语法,即.properyname来获取属性的值,如下所示;
  • 设置属性的值:可以使用点语法,来给属性直接设值(注意一点,属性不能是readonly的),如下所示。
 
  1. #import "MyPropertyClass.h"
  2.  
  3. int main(int argc, const char * argv[]) {
  4. @autoreleasepool {
  5.  
  6. MyPropertyClass *properyClass = [[MyPropertyClass alloc] init];
  7. //获取属性的值,getter方法
  8. NSLog(@"初始值:%@",properyClass.name);
  9. //设置属性的值,setter方法
  10. properyClass.name = @"宏创学院";
  11. NSLog(@"最新值:%@",properyClass.name);
  12. }
  13. return 0;
  14. }

运行结果:

QQ20160210-0@2x



属性property介绍:2-公共属性与专有属性

在自定义类中,既可以在.h文件中声明属性,也可以在.m文件中声明属性。在.h文件中声明的属性可以供外部调用,称为公共属性;在.m文件中声明的属性,即该类扩展中声明的属性,只能在该类的实现部分使用,称为专有属性。

1、定义专有属性

在类实现的.m文件中,可以定义供类内部使用的专用属性。

 
  1. #import "MyPropertyClass.h"
  2.  
  3. @interface MyPropertyClass ()
  4.  
  5. @property (nonatomic, copy) NSString *firstName;
  6. @property (nonatomic, copy) NSString *lastName;
  7.  
  8. @end

2、专有属性懒加载

使用懒加载的方式,初始化两个属性。

 
  1. /**
  2. * firstName懒加载
  3. *
  4. * @return firstName
  5. */
  6. -(NSString *)firstName{
  7. if (_firstName == nil) {
  8. _firstName = [NSString stringWithFormat:@"www."];
  9. }
  10. return _firstName;
  11. }
  12.  
  13. /**
  14. * lastName懒加载
  15. *
  16. * @return lastName
  17. */
  18. -(NSString *)lastName{
  19. if (_lastName == nil) {
  20. _lastName = [NSString stringWithFormat:@"hcios.com"];
  21. }
  22. return _lastName;
  23. }

3、定义公共属性

在类定义的.h文件中,定义供外部调用的公共属性。

 
  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface MyPropertyClass : NSObject
  4.  
  5. @property (nonatomic, copy) NSString *name;
  6.  
  7. @end

4、公共属性懒加载

在.m文件中,使用懒加载初始化公共属性。在懒加载中,使用点语法获取firstName以及lastName属性的值,并进行拼接。

 
  1. /**
  2. * name公共属性懒加载,拼接firstName与lastName
  3. *
  4. * @return name
  5. */
  6. -(NSString *)name{
  7. if (_name == nil) {
  8. self.firstName = [self.firstName stringByAppendingString:self.lastName];
  9. _name = self.firstName;
  10. }
  11. return _name;
  12. }

5、main()中打印公共属性

在外部只能打印公共属性,不能打印专用属性。

 
  1. #import <Foundation/Foundation.h>
  2. #import "MyClass.h"
  3. #import "MyConst.h"
  4. #import "MyPropertyClass.h"
  5.  
  6. int main(int argc, const char * argv[]) {
  7. @autoreleasepool {
  8.  
  9. MyPropertyClass *properyClass = [[MyPropertyClass alloc] init];
  10. NSLog(@"初始值:%@",properyClass.name);
  11.  
  12. properyClass.name = @"宏创学院";
  13. NSLog(@"最新值:%@",properyClass.name);
  14.  
  15. }
  16. return 0;
  17. }

运行结果:

QQ20160210-0@2x

属性property介绍:3-属性关键字

在定义属性的时候,需要在括号()内说明该属性的特性(attribute)。属性的特性决定了属性在原子性、存取方法以及内存管理三个方面的特性。常用的特性关键字有8个,分别为nonatomic/atomic、readonly/readwrite、strong/weak/assign/copy。

1、原子性(nonatomic、atomic)

atomic(默认):atomic意为操作是原子的,意味着只有一个线程访问实例变量。atomic是线程安全的,至少在当前的存取器上是安全的。它是一个默认的特性,但是很少使用,因为比较影响效率,这跟ARM平台和内部锁机制有关。

  • 当一个变量声明为atomic时,意味着在多线程中只能有一个线程能对它进行访问
  • 当一个变量声明为atomic时,该变量为线程安全型,但是会影响访问速度,
  • 当一个变量声明为atomic时,在非ARC编译环境下,需要设置访问锁来保证对该变量进行正确的getter/setter。

nonatomic(常用):nonatomic跟atomic刚好相反。表示非原子的,可以被多个线程访问。它的效率比atomic快,但不能保证在多线程环境下的安全性,在单线程和明确只有一个线程访问的情况下广泛使用。

  • 当一个变量声明为nonatomic时,意味着多个线程可以同时对其进行访问
  • 当一个变量声明为nonatomic时,它是非线程安全型,访问速度快;
  • 当一个变量声明为nonatomic时,当两个不同的线程对其访问时,容易失控。

2、存取方法(readonly、readwrite)

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

readonly: readonly表示只有getter没有setter。

有时,在声明属性时,可以指定存取方法的自定义名称,通常在Bool类型的属性的getter方法会采用这种方式,例如:UIView的hidden和opaque属性的getter方法名为:isHidden和isOpaque。

 
  1. @property(nonatomic,getter=isHidden) BOOL hidden;
  2. @property(nonatomic,getter=isOpaque) BOOL opaque;

3、内存管理(strong、weak、assign、copy)

由于苹果自iOS5开始引入了ARC(Automatic Reference Counting) ,且经过几年发展ARC已然成为主流,因此在管理属性有关内存管理的特性时,我们都假设在ARC环境下使用,不考虑MRC的情况。

  • strong(ARC中为默认):强引用,表示实例变量对传入的对象要有所有权关系,引用计数+1;
  • weak:弱引用,在setter方法中,需要对传入的对象不进行引用计数加1的操作。简单来说,就是对传入的对象没有所有权,当该对象引用计数为0时,即该对象被释放后,用weak声明的实例变量指向nil,即实例变量的值为0。
  • assign: 简单赋值,不更改索引计数,适用简单数据类型。如int、float、double和NSInteger,CGFloat等。
  • copy: 用于希望保持一份传入值的拷贝,而不是值自身的情况,即把原来的对象完整的赋值到另外一地方,重新加载一内存区,一个地方变了不影响另一个地方的对象。copy与strong类似,但区别在于实例变量是对传入对象的副本拥有所有权,而非对象本身。

示例代码:

  • 创建一个ClassA类,并添加3个NSMutableString类型的属性,但关键词不同,如下所示:
 
  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface ClassA : NSObject
  4.  
  5. @property (nonatomic, copy) NSMutableString * name;
  6.  
  7. @property (nonatomic, strong) NSMutableString *strongName;
  8.  
  9. @property (nonatomic, weak) NSMutableString *weakName;
  10.  
  11. @end
  • 在main()中,添加如下代码,通过属性对应的实例变量的内存地址,可以对strong/weak/copy有所理解。其中strong/weak直接指向string的地址,而copy会创建一个新的副本,故内存地址也不一样。
 
  1. #import <Foundation/Foundation.h>
  2.  
  3. #import "ClassA.h"
  4.  
  5. int main(int argc, const char * argv[]) {
  6. @autoreleasepool {
  7. NSMutableString *string = [[NSMutableString alloc] initWithFormat:@"normal"];
  8. ClassA *classA = [[ClassA alloc] init];
  9. classA.name = string;
  10. classA.strongName = string;
  11. classA.weakName = string;
  12. NSLog(@"string : %@ address: %p", string ,string);
  13. NSLog(@"copy Property : %@ address: %p", classA.name ,classA.name);
  14. NSLog(@"strong Property : %@ address: %p", classA.strongName,classA.strongName);
  15. NSLog(@"weak Property : %@ address: %p", classA.weakName ,classA.weakName);
  16. }
  17. return 0;
  18. }
  • 运行结果:

QQ20160217-0@2x

4、速记法

如果在开始学习阶段难以理解有关内存管理相关参数的使用,可以记住如下要点,虽然不是完全规范,但使用起来问题不大。

  • strong:自定义对象,控制器对象;
  • weak:代理对象,IBOutlet;
  • assign:基本数据类型,int、float、double和NSInteger,CGFloat;
  • copy:NSString,NSArray,NSDictionary及其可变子类。

示例代码:

 
  1. @property (nonatomic,copy) NSString *name;
  2. @property (nonatomic,copy) NSArray *courseResults;
  3. @property (nonatomic,assign) int age;
  4. @property (nonatomic,strong) MyClass *myClass;
  5. @property (nonatomic,weak) id delegate;

对象Object介绍:2-对象操作

本节主要介绍对象的一些常见操作,例如判断对象的类型、判断对象是否响应消息、对象间的比较以及对象复制。

1、判断对象的类型

可以让对象调用isKindOfClass:方法,来判断某个对象的类型。isKindOfClass:方法是NSObject类的方法,将类型为Class的对象aClass作为参数传入,返回一个BOOL类型的返回值。

 
  1. - (BOOL)isKindOfClass:(Class)aClass;

例如,下面的代码用来判断str是否是NSString类型,如果是NSString类型,则打印一段日志。

 
  1. //使用类方法创建字符串对象
  2. NSString *str = [NSString stringWithFormat:@"www.hcios.com"];
  3. //判断对象的类
  4. if ([str isKindOfClass:[NSString class]]) {
  5. NSLog(@"str class is NSString");
  6. }

另外,isKindOfClass:方法还可以用于判断子类对象的类型,如下面的代码所示,NSMutableString是NSString的子类,代码执行后,isKindOfClass:方法也会认定mutableStr对象也是属于NSString类型。

 
  1. NSMutableString *mutableStr = [NSMutableString string];
  2. if ([mutableStr isKindOfClass:[NSString class]]) {
  3. NSLog(@"mutableStr class is NSString");
  4. }

2、判断对象是否响应消息

要判断一个对象是否响应一则消息,则可以调用respondsToSelector:方法。应用程序通常在验证一个对象响应一则消息后,才将消息发送给该对象。

这种机制,常常用于代理中,先判断代理对象是否响应代理方法,如果响应则代表代理中已经实现了代理方法,然后再通知代理执行代理方法。

示例代码:

  • 在自定义类.h文件中,添加一个属性
 
  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface HCClass : NSObject
  4.  
  5. @property (nonatomic, copy) NSString * myName;
  6.  
  7. @end
  • 在自定义类.m文件中,对属性进行懒加载(getter方法)
 
  1. -(NSString *)myName{
  2. if (_myName == nil) {
  3. _myName = @"this is my name!";
  4. }
  5. return _myName;
  6. }
  • 在main()中,判断自定义类是否实现了属性的getter方法
 
  1. //判断对象是否响应消息
  2. HCClass *myClass = [[HCClass alloc] init];
  3. if ([myClass respondsToSelector:@selector(myName)]) {
  4. NSLog(@"MyName method exist!");
  5. }

3、对象比较

可以使用isEqual:方法对比两个对象,如果相同,则该方法返回YES。

在最常用的Foundation框架中,也提供了isEqualToString:或者isEqualToDictionary:等方法,在实际开发中使用比较广泛。

下方的示例代码中,对字符串进行了比较操作,使用了isEqual:方法以及isEqualToString:方法。

 
  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3.  
  4. NSString *str = [NSString stringWithFormat:@"www.hcios.com"];
  5. NSString *str2 = @"宏创学院";
  6. //对象比较
  7. if ([str isEqual:str2]) {
  8. NSLog(@"str is equal str2!");
  9. }else {
  10. NSLog(@"str is NOT equal str2!");
  11. }
  12. if ([str isEqualToString:@"www.hcios.com"]) {
  13. NSLog(@"str is equal to string: \"www.hcios.com\"!");
  14. }
  15. }
  16. return 0;
  17. }

4、对象复制

通过调用copy方法,可以创建对象的副本。调用copy方法,有个前提条件,即接收的对象的类必须遵守NSCopying协议。

下面的示例代码中,对NSString类型的字符串对象进行复制,NSString类在定义中遵守了NSCopying协议。

 
  1. @interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>//遵守了NSCopying协议
 
  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3.  
  4. NSString *str = [NSString stringWithFormat:@"www.hcios.com"];
  5. //对象复制
  6. NSString *str3 = [str copy];
  7. NSLog(@"str3 is :%@ A",str3);
  8. }
  9. }
  10. return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值