• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Effective Objective-C 52个有效方法个人笔记

武飞扬头像
菜枫
帮助1

1. 了解Objective-C语言的起源

2. 在类的头文件中尽量少引入其他头文件

  • 目的:1. 解决两个类相互引用的问题 2. 减少编译时间

  • 使用向前声明@class

  • 必须使用#import的情况:

    1. 该类继承自某个超类
    2. 该类遵从某个协议
  • 当遵循某个协议的时候,应该把协议单独放在一个头文件中,对于不适合放在单独文件的协议,比如委托协议,可以用分类实现该协议,然后引入分类的文件。

3. 多用字面量语法,少用与之等价的方法

  • 字面量创建,下标直接访问数组更加简洁

例子:

/// 创建
NSNumber *numObj = @(1)
NSArray *arr = @[@"cat", @"dog"]
NSString *str = @"sdfsd"
NSDictionary *dic = @{@"first": @"tom"}
/// 取下标
NSString *dog = arr[1]
NSString *name = dic[@"first"]

4. 多用类型常量,少用#define预处理指令

  • #define不含有类型信息, 即使有人重新定义了,也不会报错,但是会导致常量值不一致
  • static const定义 static限制作用域仅在当前文件,外部不可以通过extern引用 const使常量不可以改变
  • 当需要公开某个变量的时候。
// header file
extern NSString *const 变量名

// implementation file
NSString *const 变量名 = @"";

5. 用枚举表示状态,选项,状态码

  • 可以使用Foundation的宏 typedef NS_ENUM{*type*, *name*} typedef NS_OPTIONS{*type*, *name*}

6. 理解属性这一概念

  • @synthesize指定实例变量的名字。将属性生成的实例变量改成自定义的名字。
  • @dynamic告诉编译器不要自动合成存取方法,访问属性的代码也不会报错,因为它相信这些方法可以在运行期被找到
  • 属性的所有权语义
    1. assign只针对值类型,简单的赋值操作
    2. strong表明定义了一种拥有关系,设置新值时,设置方法会先保留新值,并释放旧值,然后将新值设置上去。
    3. weak非拥有关系。设置新值时,既不保留新值,也不释放旧值,同assign,所指对象遭到摧毁时,属性值也会晴空。
    4. unsafe_unretained,同assign,适用于对象类型,但是对象遭到摧毁时,属性值不清空,也就是保留原来的地址,访问会出现问题。
    5. copy同strong,但是设置方法并不保留新值,而是将其拷贝(copy)。
  • 若是自己来实现存取方法的时候,应该保证其具备相关属性申明的特质。比如在自定义的构造方法中。
- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
  if((self = [super init])) {
    _firstName = [firstName copy];
    _lastName = [lastName copy];
    /// 这里不能使用属性所对应的设置方法 原因见第七条
  }
  return self;
}

7. 在对象内部尽量直接访问实例变量

  • self.属性名和直接访问实例变量的区别
    1. 直接访问不经过OC的方法派发的步骤,速度更快,编译器会直接访问保存对象实例变量的那一块内存。
    2. 直接访问实例变量时,不会调用其设置方法,绕过了属性定义的内存管理语义。比如访问copy属性,并不会拷贝,而是直接赋值。
    3. 直接访问实例变量,不会触发键值观测(KVO)
  • 对象内部读取数据时,应该使用实例变量来读,写入数据是,应该通过属性来写
  • 初始化方法和delloc中,使用实例变量,原因时因为子类可能会重写属性的设置方法,在初始化时,父类的初始化方法中,会调用子类的设置方法。

8. 理解“对象等同性”这一概念

  • == 操作符比较的是两个指针
  • NSObject两个判断等同性的方法,isEqual,hash,isEqual默认实现是当其指针完全相等时返回true。但是当isEqual判断两个对象相等时,hash必须返回同一个值
  • 重写hash方法的一种思路,将所有属性的hash值按位异或
  • 可以根据需求重写isEqual方法,比如根据唯一标识判断
  • 在容器中放入可变对象的时候,就不应该再改变其内容了。
NSMutableSet *set = [NSMutableSet new];
NSMuatableArray *mutaArrA = [@[@(1), @(2)] mutableCopy];
NSMuatableArray *mutaArrB = [@[@(1), @(2)] mutableCopy];
NSMuatableArray *mutaArrC = [@[@(1) mutableCopy];
[set addObject: mutaArrA];
[set addObject: mutaArrB];  // 此时set中只会有一个元素 因为AB两个数组在等通行判断上返回true
[set addObject: mutaArrC];  // 此时set中含有两个元素
[mutaArrC addObject: @(2)]; // 现在set中含有两个相同的两个元素
NSSet *setB = [NSMutableSet copy];  // setB又只会含有一个元素了

9.以“类族模式”隐藏实现细节

  • 例子,-(UIButton)buttonWithType:(UIButtonType)type通过type返回不同的UIButton的子类,但是子类对外使用统一的接口
  • Cocoa里的类族,大部分的容器类都是类族
  • 向类族中增加子类实体需要遵循的规则如下:
    1. 子类应当继承自类族中的抽象基类。
    2. 子类应当定义自己的数据储存方式。 比如NSArray的子类,NSArray这个类并不会保存需要储存的对象。而是由子类储存
    3. 子类应当覆盖超类文档中指明需要覆写的方法。

10. 在既有类中使用关联对象存放自定义数据

  • 对象关联类型
    关联类型 等效的@property属性
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
    OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
    OBJC_ASSOCIATION_RETAIN retain
    OBJC_ASSOCIATION_COPY copy
    • objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)此方法以给定的键和策略为某对象设置关联对象值。
    • objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)此方法根据给定的键从某对象中获取相应的关联对象值。
    • objc_removeAssociatedObjects(id _Nonnull object)此方法移除指定对象的全部关联对象
    • 键的等同性判断只会看两个指针的地址,而不是isEqual方法。
  • 例子,在创建UIKAlertView的时候,将该弹窗的处理事件的block和该弹窗关联,然后在弹窗的代理方法里面取出Block执行,实现弹窗处理逻辑写在弹窗哪一块

11. 理解objc_msgSend的作用

  • OC发送消息 id returnValue = [someObject messageName: parameter]; 编译器会转变为 id returnValue = objc_msgSend(id Self, SEL cmd, ...)该方法需要在接收者所属的的类中搜寻其方法列表,匹配成功后将该方法缓存在快速映射表里面,并跳转到具体执行。

12. 理解消息转发机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O8DMWib4-1662015588609)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/20220629161133.png “消息转发”)]

  • 对象在收到无法解读的消息后,调用 (BOOL)resolceInstanceMethod:(SEL)selector,可以在这个方法可以新增一个处理此选择子的方法
  • 备援接收者 当前接收者还有第二次机会可以处理未知的选择子,-(id)forwardingTargetForSelector:(SEL)selector,可以经由此方法,将能够处理这个选择子的其他对象返回。在外界看来,好想是对象亲自处理了这些消息。
  • 完整的消息转发
    首先创建NSInvocation对象,把与尚未处理的那条消息的有关细节全部都封于其中,包含选择子,目标对象及参数。消息派发系统将消息指派给目标对象。此步骤会调用-(void)forwardInvocation:(NSInvocation*)invocation,过程中可以改变消息内容,比如追加另外一个参数,或者该换选择子
  • 为了说明消息转发机制的意义,下面示范如何以动态方法解析来实现@dynamic属性。假设要编写一个类似于“字典”的对象,它里面可以容纳其他对象,只不过开发者要直接通过属性来存取其中的数据。这个类的设计思路是:由开发者来添加属性定义,并将其声明为@dynamic,而类会自动处理相关

13. 用“方法调配技术”调试“黑盒方法”

  • 在运行期间,可以向类中新增洪战辉替换选择子所对应的方法实现
  • 使用另一份实现来替换原有的方法实现,叫“方法掉配”,可以通过这种方式向原有实现中添加新功能。
    /// 给NSStirng的lowercaseString添加日志功能
    @implemention NSString(EOCMyAddtions)
    /// 在NSString中自己新添加方法 实现日志功能
    - (NSStirng)eco_myLowercaseString {
    NSString *lowercase = [self eco_myLowercaseString];
    NSLog(@"%@ => %@", self, lowercase)
    return lowercase;
    }
    Method originaMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swapped = class_getInstanceMethod([NSString class], @selector(eco_myLowercaseString));
    /// 交换两个方法实现
    method_exchangeImplementations(originaMethod, swapped)
    /// 现在调用[NSString lowercaseString]就会实现在原有方法上添加日志的效果
    

14. 理解“类对象”的用意

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X3HLwuQx-1662015588609)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/111.png “objc_class的结构体”)]

  • 在继承体系中查询类型信息
    1. isMemberOfCalss,判断对象是否是某个特定类的实例。isKindOfClass判断随想是否是某个类或其派生类的实例。
    2. 也可以使用 == 判断类对象是否等同的方式(不建议)。因为类对象都是单例,在应用程序范围内,每个类的Class仅有一个实例。即便能这样做,我们也应该尽量使用类型信息查询方法,而不应该直接比较两个类对象是否等同,因为前者可以正确处理那些使用了消息传递机制(参见第12条)的对象。比方说,某个对象可能会把其收到的所有选择子都转发给另外一个对象。这样的对象叫做“代理”(proxy),此种对象均以NSProxy为根类。通常情况下,如果在此种代理对象上调用class方法,那么返回的是代理对象本身(此类是NSProxy的子类),而非接受的代理的对象所属的类。然而,若是改用“isKindOfClass:”这样的类型信息查询方法,那么代理对象就会把这条消息转给“接受代理的对象”(proxiedobject)。也就是说,这条消息的返回值与直接在接受代理的对象上面查询其类型所得的结果相同。因此,这样查出来的类对象与通过class方法所返回的那个类对象不同,class方法所返回的类表示发起代理的对象,而非接受代理的对象。
  • isa指针所指的对象是另外一个类,叫做元类,用来表述类对象本身所具备的元数据。“类方法”就定义于此处,可以理解成类对象的实例方法。每个类仅有一个“类对象”,而每个类对象,仅有一个与之相关的“元类”。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vXKDIkbY-1662015588610)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/sjdhfvdjshfvjdshvfhjds.png “某个实例的继承体系”)]
  • isKindOfClassobj.class == SomeClass.class的区别
    如果某个对象将其所有的选择子都转发给另外一个对象,isKindofClass会将被代理的对象返回。class方法只会将自己的类型值返回,推荐用isKindOfClass.

15. 用前缀避免命名空间冲突

16. 提供全能初始化方法

  • 在类中提供一个全能初始化方法,其他初始化方法均应该调用此方法
  • 若全能初始化方法与超类不同,则需要覆写超类中的对应方法
  • 如果超类的初始化方法不适用于子类,那么也应该覆写这个超类方法,并在其中抛出异常

17. 实现description方法

  • 可以使用%p self, 把属性添加到NSDictionary的方式,打印
  • 想要调试的时候使用po命令打印更详细的信息,则应该实现debugDescription

18. 尽量使用不可变对象

  • 尽量把对外公布的属性设置为只读,而且只在确有必要的时候才将属性对外公布。
  • 可哟在对象内部将readonly属性重新声明为readwrite。这样会有线程安全问题,比如内部在写入某属性时,外部在读取数据。
  • 不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection。

19. 使用清晰而协调的命名方式

  • 如果方法的返回值是新创建的,那么方法名的首个词应是返回值都得类型,比如intValue,属性的存取方法除外
  • 应该把表示参数类型的名词放在参数前面
  • 如果方法要在当前对象上执行操作,那么就应该包含动词,若执行操作时还需要参数,则应该把在动词后面加上一个或多个名词
  • 不要使用str这种简称,应该使用string这样的全称
  • Boolean属性应加is前缀,如果某个方法返回非属性的Boolean值,那么应该根据其功能,选用has或is当前缀
  • 将get这个前缀留给那些借由“输出函数”来保存返回值的方法

20. 为私有方法名加前缀

  • 给私有方法的名称加上前缀,用来区分公共方法
  • 不要单独用一个下划线做私有方法的前缀,这种做法是预留给苹果公司的

21. 理解Objective-C错误模型

  • 只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常
  • NSError封装的信息
    1. Error domain错误发生的范围,也就是产生错误的根源,比如URL解析错误会使用NSURLErrorDomain来表示错误范围。
    2. Error code错误码,用以指明在某个范围内发生了何种错误。比如HTTP请求出错时,可能会把HTTP状态码设为错误码
    3. User info用户信息,类型为字典,有关此错误的额外信息,其中或许包含一段“本地化描述”,或许还含有导致该错误发生的另外一个错误,经由此种信息,可将错误串成一条错误链
  • NSError的用法
    1. 通过委托协议来传递此错误
    2. 将NSError作为方法的输入参数,然后返回给调用者
    NSError *error = nil;
    BOOL ret = [object doSomething: &error]; // 传递该指针的地址 方法里面给error的指针赋值,实现功能
    if(error) {// 处理错误    }
    
    - (BOOL)doSomething:(NSError **)error {
      if(/*该条件为true会由错误*/) {
        if(error) {
          *error = [NSErrorWithDomain: domain code: code userInfo: userInfo];
        }
        return NO;
      } else {
    
      }
      return YES;
    }
    

22.理解NSCopying协议

  • 拷贝对象通过copy方法完成,需要实现NSCopying协议。真正需要实现的是copyWithZone,Copy方法由NSobject实现,内容是以默认区为参数调用copyWithZone。
    例:
    -(id)copywithZone:(NSZone*)zone {
      EOCPerson *copy = [[[self class] allocWithZone:zone] initwithFirstName:firstName andLastName:lastName];
      return copy;
    }
    
    • 有的时候,除了拷贝对象,还需要完成一些其他操作。比如EOCPerson中含有一个数组,这时候需要把数组也拷贝过来。
    • NSMutableCopying协议,需要实现 - (id)mutableCopyWithZone:(NSZone *)zone方法
    • Copy返回的一定是不可变版本。即使原版本是可变的。MutableCopy同理。
    • 系统框架一般是默认浅拷贝的。

23.通过委托与数据源协议进行对象间通信

  • 调用代理的方法时候需要判断代理是否实现了该方法。
  • 判断代理是否实现了某个方法,只有第一次的检测结果有用。将方法响应能力缓存起来的最佳途径是使用“位段”。我们可以把结构体中的某个字段所占用的二进制位数设为特定的值。
    例:
    struct data {
      unsigned int fieldA : 8; // 8个二进制位
      unsigned int fieldB : 4;
      unsigned int fieldC : 2;
      unsigned int fieldD : 1;
    }
    
    可以创建每个位段的大小为1的结构提作为实例变量。在setDelegate方法里面缓存代理是否实现。

24.将类的实现方法分散到便于管理的各个分类之中

25.总是为第三方类的分类名称加前缀

  • 如果类中本来就有这个方法,而分类又实现了一次,那么分类中的方法会覆盖原来那一份实现代码。

26.勿在分类中声明属性

  • 除了class-continuation分类,其他分类都无法向类中新增实例变量。

27.使用“class-continuation分类”隐藏实现细节

  • 必须定义在其所续接的那个类的实现文件里面。是唯一能声明实例变量的分类。
  • 可以使用此方式将只读的属性扩展为可读写。
  • 该类所遵循的协议也可以在这里申明

28.通过协议提供匿名对象

29.理解引用计数

  • NSObject协议声明了下面三个方法用于操作计数器,以递增或递减其值。
    ** retain 递增保留计数
    ** release递减保留计数
    ** autorelease 待稍后清理“自动释放池”时,再递减保留计数。
    学新通

30.以ARC简化引用计数

  • ARC之后,程序员就无需担心内存管理问题了,其管理对象生命周期的方法基本上就是在合适的地方插入“保留”及“释放”操作。

31.在delloc方法中只释放引用并解除监听

  • 在delloc方法中,应该做的事就是释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO),或NSNotificationCenter等通知,不要做其他事情。
  • 如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其使用者约定:用完资源后 必须调用close方法。
  • 执行异步的方法不应该在delloc里面调用。

32.编写“异常安全代码”时留意内存管理问题

  • 在默认情况下,ARC不生成安全处理异常所需的清理代码。开启编译器标志后,可生成这种代码,会导致应用程序变大,而且会降低运行效率。
  • 没有开启的情况下,一定要注意将try块内所创立的对象清理干净。

33.以弱引用避免保留环

34.以“自动释放池”降低内存峰值

  • 自动释放池排布在栈中,对象收到autorelease消息后,系统将其放入最顶端的池里。
  • 合理运用自动释放池,可降低应用程序的内存峰值。

35.用“僵尸对象”调试内存管理问题

  • 将NSZombieEnable环境变量设为YES,即可开启“僵尸对象”功能。运行期系统会把所有已经回收的实例转换成特殊的僵尸对象。而不会真正的回收它们。这种对象所在的核心内存无法重用。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。
  • 也可以在XCode里打开此选项。
  • 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子。

36.不要使用retainCount

  • retainCount 用来查询对象当前的保留计数。ARC下已经废弃。

37.理解“块”这一概念

  • 块的内部结构
    学新通
    invoke是一个函数指针,指向块的实现代码。
    descriptor变量是指向结构体的指针,每个块都包含此结构体,其中申明了块对象的总体大小,还申明了copy与dispose这两个辅助函数所对应的函数指针。
  • 块可以分配在栈和堆上,也可以是全局的,分配在栈上的块可以拷贝到堆里。这样的话就和标准的Objective-C对象一样,具备引用计数了。

38.为常用块类型创建typedef

39.用handler块降低代码分散程度

40.用块引用其所属对象时不要出现保留环

  • 找适当的时机解除保留环,比如在不再需要调用块的时候,放弃块的持有。

41.多用派发队列,少用同步锁

  • 使用串行同步队列代替同步块或锁对象。
_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);
- (NSString *)someString {
  __block NSString *localSomeString;
  dispatch_sync(_syncQueue, ^{
    localSomestring = somestring;
  });
  return localsomestring;
}

-(void)setSomeString:(NSString *)somestrin {
  dispatch_sync(syncQueue, ^{
    _someString = somestring;
  });
}
** 把设置操作与获取操作安排在序列化的队列里执行,这样的话,所有针对属性的访问操作都同步了。
  • 也可以改用并发队列,单纯把上面的串行队列改为并发队列并不能实现正确同步,因为读取与写入操作可以随时执行。可以使用GCD中的栅栏(barrier)解决。
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

** 在队列中,栅栏块必读单独执行,不能与其他块并行。只对并发队列有意义。并发队列如果发现接下来要处理的块是个栅栏块,那么就一直要等当前的所有并发块都执行完毕,才会单独执行这个栅栏块。可以使用栅栏块来实现属性的Set方法。对属性的读取操作依然可以并行执行。

42.多用GCD,少用performSelector系列方法

  • performSelector系列方法在内存管理方面容易有缺失,它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。

43.掌握GCD及操作队列的使用时机

  • 使用NSOperation及NSOperationQueue

44.通过Diapatch Group机制,根据系统资源状况来执行任务

  • long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); timeout参数标识函数在等待dispatch group执行完毕时,应该阻塞多久。如果执行dispatch group所需时间小于timeout,则返回0,否则返回非0值。此参与也可以去DISPATCH_FOREVER,这表示函数会一直等待dispatch group执行完。
  • void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);等dispatch group执行完毕之后,块会在特定的线程上执行。
  • void dispatch_apply(size_t iterations, dispatch_queue_t queue, void(^block)(size_t));该函数会反复执行一定的次数,每次传给块的参数值都会递增,从0开始,直至iterations-1。
    dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);
    dispatch_apply(10, queue, ^(size_t i){
      //perform task
    });
    
    使用的队列可以是并发队列。

45.使用dispatch_once来执行只需运行一次的线程安全代码。

46.不要经常使用dispatch_get_current_queue

  • dispatch_get_current_queue已经废弃,应当只用做调试用。
  • 派发队列是按层级关系来组织的,所以无法单用某个队列对象来描述“当前队列”这一概念。

47.熟悉系统框架

  • Foundation框架,所有Objective-C应用程序的基础。CoreFoundation,Foundation中的许多功能和对象,可以在CoreFoundtion无缝衔接。
  • CFNetwork,此框架提供了C语言级别的网络通信能力。
  • CoreAudio,此框架提供的C语言API可用来操作硬件上的音频硬件。
  • AVFoundtion,此框架所提供的Objecti-C可用来回放并录制音频以及视频,比如可以在UI视图类里播放视频。
  • CoreData,此框架所提供的Objective-C接口,可将对象放入数据库中,便于持久保存。
  • CoreText,此框架提供的C语言接口可以高效执行文字排版及渲染操作。

48.多用块枚举,少用for循环

  • NSEnumerator是个抽象基类,其中只定义了两个方法,供具体子类来实现。
    - (NSArray *)allObjects
    - (id)nextObject
    
    其中nextObject,返回枚举的下个对象,Foundation框架中内建的collection类都实现了这种遍历方式。
    NSArray *anArr = /*...*/
    NSEnumerator *enumerator = [anArr objectEnumerator];
    id object;
    while((object = [enumerator nextObject]) != nil) {
      // Do somethimg
    }
    
  • 快速遍历 for in
  • 基于块的遍历方式
    - (void)enumerateObjectsUsingBlock:(void(^)(id object, NSUInteger idx, BOOL *stop))block
    /// *stop可以用来停止遍历
    
    块遍历可以获取更多的信息,能够修改块的方法签名,本身可以通过GCD来并发执行。

49.对自定义其内存管理语义的collection使用无缝桥接

50.构建缓存时选用NSCache而非NSDictionary

* NSCache可以提供优雅的自动删减功能,而且时线程安全的。
* NSPurgeableData,是NSMutableData的子类,这个类实现了NSDiscardableContent协议。系统资源紧张时,可以把这个对象的那一块内存释放掉。可以访问isContentDiscarded查询相关内存是否已释放。
* NSPurgeableData加入NSCache,那么该对象为系统所丢弃时,也会自动从缓存中移除。

51.精简initialize与load的实现代码

* 对于加入运行期系统中的每个类(class)及分类来说,必定会调用此方法,而且仅调用一次。
* initialize和load的区别是initialize是惰性调用。initialize是线程安全的。
* 如果某个类没有实现initialize,但是其超类实现了,就会调用超类的initialize方法。load方法则不然。
* 无法在编译器预设的全局变量,可以放在initialize方法里初始化。比如数组。

52.别忘了NSTimer会保留其目标对象

* NSTimer对象对保留其目标,知道计时器本身失效为止。调用invalidate方法可令计时器失效。
* 注意保留环。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhjkkf
系列文章
更多 icon
同类精品
更多 icon
继续加载