iOS上Delegate的悬垂指针问题

文章有点长,写的过程很有收获,但读的过程不一定有收获,慎入

【摘要】

悬垂指针(dangling pointer)引起的crash问题,是我们在iOS开发过程当中经常会遇到的。其中由delegate引发的此类问题更是常见。本文由一个UIActionSheet引发的delegate悬垂指针问题开始,逐步思索和尝试解决这类问题的几种方案并进行比较。

【正文】

UIActionSheet是一个常用的iOS系统控件,用法很简单,实现UIActionDelegate协议方法,然后通过showInView:等方法弹出。我们来看一段代码:(如无特殊说明,本文中的代码均在ARC条件下书写)

 - (void)popUpActionSheet
{
    UIActionSheet* sheet = [[UIActionSheet alloc] initWithTitle:nil
                                                       delegate:self
                                              cancelButtonTitle:NSLocalizedString(@"str_cancel", @"")
                                         destructiveButtonTitle:NSLocalizedString(@"str_delete", @"")
                                               otherButtonTitles:nil];
    [sheet showInView:self.view];
}

像这样用一个局部变量弹出actionsheet的代码喜闻乐见。

那么这样做是否有问题呢?

来看某项目中的一个bug:页面X,按住区域Y点击按钮B,再点击按钮C(关闭),松开后页面X退出,但actionsheet A弹出,点击其中的按钮,程序crash。

从描述中不难看出问题所在:这是个dangling pointer的问题。点击按钮B,本应弹出actionsheet A,但由于某些特殊操作(具体原因各不相同,这里是按住区域Y),这个actionsheet的弹出被延迟了,当它弹出的时候,其delegate(通常是一个UIViewController或者一个UIView,这里是页面的ViewController X)已经被销毁了,于是delegate成了一个dangling pointer,点击按钮,向delegate发送消息的时候,就出现了crash。

为了防止retain cycle,iOS中大部分的delegate都是不增加对象(X)的引用计数的(弱引用),因而容易出现dangling pointer的问题。对于此类问题,解决方向通常有两个:

其一,在向delegate发送消息之前,判断delegate是否仍然有效;

其二,使对象X在dealloc的时候,主动设置所有指向X的delegate为nil。

对于方向一,看上去很美,如果能够在发消息前判断一个指针是否是dangling pointer,那么我们就有了最后一道防线,从此再不会发生此类crash问题。但是,当dangling pointer真出现的时候,我们更应反思一下代码设计上是否出现了不合理的地方,而不是简单以这种方式捕获并丢弃。

相比之下,方向二“销毁时置空”这个方案显得更治本,亦是一种良好的编程习惯。推而广之,不局限于delegate,所有弱引用指针都可以如此处理。

这正是ARC中引入的weak指针的概念,它会在所指对象dealloc的时候自动置为nil。也就是说,只要所有delegate都是weak类型的,此类dangling pointer问题就不复存在了。本文也可以到此结束了。

但是,现实总是残酷的。首先,weak指针只有在iOS 5.0及以上的版本中的ARC条件下才能使用,而目前很多项目依然需要支持iOS4.3。当然,随着iOS7的发布,这种情况会有所好转。但即使所有的用户都是5.0+,问题仍然没有解决。为何?

我们自定义的delegate,可以全部采用weak类型。但是系统控件是什么情况呢?比如UIActionSheet,看看iOS7.0版本SDK下的UIActionSheet.h:

 @property(nonatomic,assign) id delegate;    // weak reference

即使是7.0,这些系统控件的delegate仍然不是weak,而是assign,大约是为了兼容非ARC环境的原因吧。 也就是说,weak指针并不能解决系统控件delegate的dangling pointer问题。这下肿么办?

花开两朵,各表一支。

我们先回过头来看另外一个问题:为什么actionsheet会出现这个dangling pointer的问题?

直接原因是作为delegate的ViewController X被销毁了,而此时actionsheet A本身还在显示。但这个A明明是show在self.view上的,为什么self.view都没了,它还会存在呢?

我们来看下面一段代码:

 -(void)viewDidAppear:(BOOL)animated
{
    UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"abcde" delegate:self cancelButtonTitle:@"cancel" destructiveButtonTitle:nil otherButtonTitles:nil];

    NSLog(@"application windows:%@", [UIApplication sharedApplication].windows);

    [sheet showInView:self.view];

    NSLog(@"self.window:%@", self.view.window);
    NSLog(@"sheet.window:%@", sheet.window);

    NSLog(@"application windows:%@", [UIApplication sharedApplication].windows);
}

主要运行结果(为iphone上的,情况在iPad上略有不同)如下:

application windows:(
    UIWindow ...
) // actionsheet弹出前,只有1个UIWindow

self.window: UIWindow ...
sheet.window: _UIAlertOverlayWindow ...

application windows:(
    UIWindow ...
    UITextEffectsWindow ...
    _UIAlertOverlayWindow ...
 ) // actionsheet弹出后,有3个UIWindow

原来iOS的application并非只有一个window,actionsheet是弹在另外一个内部的window上的(iPad上情况不同,只有一个window,actionsheet是在showInView的superview的一个叫UIDimmingView的subview上),与showInView:方法中指定的view并没有持有的关系,所以能在完全不依赖于后者生命周期的情况下存在,于是出现dangling pointer delegate一点也不奇怪了。

那么知道了来龙去脉以后,我们可以开始着手解决文章开始时的那个bug了。按照“在一个对象X dealloc的时候,设置所有指向X的delegate为空”这个“方向二”的中心思想,weak指针是派不上用场了,我们只能另想办法。

通过分析,我们知道落实这个“中心思想”的要点就是:

怎样在X dealloc的时候获取到所有delegate指向X的actionsheet A?

由于文章开始时喜闻乐见的代码中,actionsheet是局部变量弹出的,在ViewController X dealloc的时候,我们已经访问不到那个局部变量,怎么办呢?

思路1:

改用一个实例变量V保存actionsheet。在X的dealloc方法里置V.delegate = nil。

这毫无疑问是最容易想到的方法,无须赘述。只是要注意一个问题:actionsheet是可以同时(或相继)弹出多个的(我们会看到背景的黑色蒙板随着弹出actionsheet的数量而叠加,越来越深。)这样一来,我们要么改用一个数组来保存actionsheet的指针(们),要么就要在每弹出一个新的时候,就把旧的处理掉(或者delegate置空,或者干脆dismiss掉)。

这种思路,优点有二:

一、思路简单,代码添加量少;

二、如果你是在写一个iPad app,那反正应付转屏重新布局actionsheet也是需要这个实例变量的,一举多得。

其缺点也有二:

一、这种方式通用性差,我们需要针对每一个这样的X都写一遍这样的代码,如果这是一个已经存在的项目,而这个项目里几乎所有的actionsheet都是这样用局部变量弹出的,怎么办?我们需要修改多少代码?

二、actionsheet作为一个系统控件,ViewController多数情况下只是控制弹出和实现delegate方法,并不做其他任何操作,这也就是为什么会出现前述喜闻乐见的代码,因为其他地方用不着引用这个actionsheet。只为解决dangling pointer的问题而在类中添加一个实例变量保存指针,甚至要保存一个指针数组,并且这部分代码还和类本身逻辑的代码耦合在一起,有洁癖的人看起来总觉得刺眼。

理想中解决dangling pointer问题的方法,应该是一个通用的基础方法,与类的业务逻辑无关,代码相对独立。

思路2:

不用实例变量,想办法在delegate dealloc的时候获得actionsheet的指针。

系统的view树一定是保存了actionsheet的指针的,第一反应是想在actionsheet上打tag,然后利用viewWithTag:方法来获取。或者,在dealloc的时候遍历整个view树来寻找当前存在的actionsheet,这两种方法本质上是相同的。我们暂且不讨论遍历view树的开销是否值得,只讨论方法可行性。刚才我们说过,iphone上的actionsheet是从属于一个内部window的,并不在我们程序可控的window中,所以上述方法根结点的选取是关键。

 UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"sheet" delegate:self cancelButtonTitle:@"cancel" destructiveButtonTitle:nil otherButtonTitles:nil];
    [sheet showInView:self.view];

    [sheet setTag:kSheetTag];

    NSLog(@"root(self.view.window):%@", [self.view.window viewWithTag:kSheetTag]);  // null
    NSLog(@"root(internal window):%@", [[UIApplication sharedApplication].windows[2] viewWithTag:kSheetTag]); // actionsheet found!

结果情理之中,我们在当前的window上是遍历不到这个actionsheet的,需要在之前说的_UIAlertOverlayWindow上遍历才行。于是我们可以先在actionsheet创建时打个tag,然后在X dealloc方法里这样写:(不能应付多个actionsheet弹出的情况)

 [[UIApplication sharedApplication].windows enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if (strcmp(class_getName([obj class]),"_UIAlertOverlayWindow") == 0)
        {
            UIActionSheet *theSheet = (UIActionSheet *)[obj viewWithTag:kSheetTag];
            [theSheet setDelegate:nil];
        }
    }];

也可以不打tag,直接采用遍历view树的方式。(如果是在ipad上,不用使用内部window,直接遍历自己的self.view.superview的subviews就行了,可自行实验)

 [[UIApplication sharedApplication].windows enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
         [self traverseView:obj];
    }];

// 遍历view树
- (void)traverseView:(UIView *)root
{
    [[root subviews] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[UIActionSheet class]])
        {
            if (((UIActionSheet *)obj).delegate == self)
            {
                ((UIActionSheet *)obj).delegate = nil;
                NSLog(@"enemy spotted!");
            }
        }
        else
        {
            [self traverseView:obj];
        }
    }];
}

这样也解决了问题,其优点有一:

一、不用改动类X的业务逻辑部分的代码,修改范围缩小到X的dealloc方法中,相对来讲便于移植到其他类中,也可以通过一些runtime的手段实现自动化。

二、遍历的方法,可以轻易应对弹出多个actionsheet的情况。

其缺点有二:

一、提起遍历view树,开销问题是肯定要考虑的。以某项目为例,一vc在dealloc的时候,view树中共有320个view,遍历寻找UIAcionSheet并置delegate空需要约0.002s,而正常的dealloc方法只需要不到0.0001s,时间提升20倍。实话说,这个开销并不是大到无法忍受,是否划算视具体情况而定。

二、如果想节省这个开销,那么就需要利用一些“潜规则”,例如像上面viewWithTag的方法代码那样,利用_UIAlertOverlayWindow这个类名来缩小遍历范围。潜规则这个东西,如果用,请慎用。它们是毫无文档保障的,可能下一代iOS,这个实现就被改掉了,那时我们的代码就会出问题。譬如通过hack系统imagePicker的方式实现多图选择框,算是一个比较常见且“合理”的利用“潜规则”的例子(因为常规用assetslibrary实现的多图选择框在iOS5上会有定位权限的提示问题,这是很多产品不愿意接受的事情),但是iOS7中,imagePicker的内部ViewController的名字就被改掉了,原来用这种方式实现多图选择框的代码,就需要跟进修改。

思路3:

从思路1和2中我们可以得到这样的启发,如果有一个集合,里面存放了所有delegate指向X的actionsheet A(甚至其它对象实例),那么,我们就能在dealloc时遍历这个集合来置A.delegate = nil。

上述这种集合S有如下特征:

1、S能够与一个对象X实现1对1的绑定或对应,并在X dealloc的时候能被访问到。

2、在合适的时机(比如设置delegate时),能够对S添加或删除元素

我们先按1和2抽象出一个通用的包含集合S的类结构,取名为parasite:

 @interface DelegateBaseParasite : NSObject
{
    NSMutableSet *sanctuarySet_; // 集合S
}

// 创建并将自己(parasite)绑定(对应)到hostObj X 上
+ (DelegateBaseParasite *)parasitizeIn:(id)hostObj;

// 返回已经绑定(对应)到hostObj X上的parasite对象(或nil若未绑定)
+ (DelegateBaseParasite *)getParasiteFromHost:(id)hostObj;

// 添加一个对象object到此parasite的集合S中,当object.delegate = hostObj X的时候
- (void)addToSanctuary:(id)object;

// 从此parasite的集合S中移除object,当object.delegate不再=X的时候
- (void)removeFromSanctuary:(id)object;

// 将所有sanctuary中对象的delegate(此时都指向hostObj)置为nil
- (void)redemptionAll;
@end

大意是:如果每一个X都与一个这样的DelegateBaseParasite P绑定(对应),在设置A.delegate = X的时候,调用addToSanctuary将A添加到P的集合S中(同时通过removeFromSanctuary方法将A从旧delegate绑定parasite的集合S中移除),并且在X dealloc的时候执行redemptionAll方法来清空集合S里的所有对象的delegate属性,那么问题就解决了。

对集合S操作的方法没有什么复杂的。重点关注的是如何实现对象X与parasite P一对一的绑定。

我们发现这个parasite对象有如下特点:

1、与宿主的类型和实现完全无关,没有调用宿主的任何方法或访问任何实例变量。

2、只需要在宿主dealloc的时候调用自己的一个方法,并且自己也被销毁。

这让我们不禁想到了一个叫做associate object(关联对象文档)的东西!不妨将DelegateBaseParasite作为一个associate object,绑定到X上。按这个思路派生一个DelegateAssociativeParasite类,实现一下绑定相关方法:

 #define kDelegateAssociativeParasiteSanctuaryKey "kDelegateAssociativeParasiteSanctuaryKey"

@implementation DelegateAssociativeParasite

#pragma mark -
#pragma public interface
+ (DelegateAssociativeParasite *)parasitizeIn:(id)hostObj
{
    DelegateAssociativeParasite *parasite = [[DelegateAssociativeParasite alloc] init];

    objc_setAssociatedObject(hostObj, &kDelegateAssociativeParasiteSanctuaryKey, parasite, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    return parasite;
}

+ (DelegateAssociativeParasite *)getParasiteFromHost:(id)hostObj
{
    return objc_getAssociatedObject(hostObj, &kDelegateAssociativeParasiteSanctuaryKey);
}

- (void)Dealloc
{
    [self redemptionAll];
}

@end

不知不觉,我们已经成功了一半。也就是说,还有另一半的问题需要我们解决:

即我们需要在actionsheet A的setDelegate:方法中删除绑定于旧delegate的集合S中的元素A,并添加A到绑定于新delegate X的集合S中。还要在A的dealloc方法中调用[self setDelegate:nil]

每次调用setDelegate的时候添加代码手动修改么?这显然不是一个好办法,并且,actionsheet的dealloc时机,并不由我们控制,想手动添加代码都办不到。那么有没有办法能修改这两个方法的实现,并且这种修改还能够调用到其原有的方法实现呢?

继承一个DelegateAutoUnregisteredUIActionSheet出来当然可以办到,但是将所有UIActionSheet替换掉,仍然要做不少工程,而且,功能上只是在UIActionSheet上打个自动注销delegate的补丁,没必要也不应该采用继承的方式。

能不能用category呢?category复写主类同名方法会产生warning,属于apple强烈不推荐的方式,而且就算强行复写了主类同名方法,也无法调用原来的实现。

那么怎么办呢?可以用objc的runtime提供的一些方法。先用class_addMethod为类添加一个新方法,在新方法中调用原有实现,再用method_exchangeImplementation将其与原有实现做交换。(objc runtime文档

按这个思路我们可以写一个辅助类DelegateAutoUnregisterHelper类(代码见附件示例工程)。

这样一来,另一半问题也解决了,现在只需在main.m里简单调用:

 [DelegateAutoUnregisterHelper registerDelegateAutoUnregisterTo:[UIActionSheet class]];

就可以实现actionsheet的delegate自动置空功能了。

这个利用associate object和runtime相结合的解法,也有其优缺点。其优点有二:

一、向工程中添加的DelegateAssociativeParasite和DelegateAutoUnregisterHelper两个类是完全与其它类独立的代码,与业务逻辑无关,逻辑清晰。

二、使用简单,只需要在main.m中调用一个方法对目标类(UIActionSheet)进行注册。项目之前“喜闻乐见”的代码完全不用做任何修改。

其缺点有一:

一、广义的dangling pointer delegate出现最多的场景其实是多线程。一个线程释放了delegate对象,而另外一个线程恰好在使用它。反观我们刚才写的代码,却完全没有考虑任何线程安全的问题。

我们不禁要问两个问题:

1. 解决UIActionSheet的delegate问题为什么可以不考虑线程安全?

2. 这种利用associate object的思路,能否通过锁/信号量等方式解决线程安全的问题?

问题1是由UIActionSheet的使用场景决定的,作为一个系统的UI控件,在大多数情况下,其setDelegate、dealloc、showInView等方法,都是在UI线程中调用的。而其delegate一般都是一个UIView或者UIViewController,这两种对象的销毁通常也是发生在UI线程里(实际上,假如我们发现我们的某些View或者ViewController的最后一次释放以致销毁跑到了非UI线程,我们应该停下来思考一下是不是设计上出了问题,因为View和VC的释放很有可能会涉及到一些在UI线程才能进行的操作。)当然,我说的是大多数情况,而并非绝对。因而通常正常使用actionsheet并不会涉及线程安全问题。

那么来到问题2,这种以associate object为核心的绑定方式,究竟有没有可能解决线程安全问题呢?

一推敲,天然的缺陷就暴露出来了。

之前我们一直刻意模糊了一个概念,即“当X dealloc的时候”。dealloc的时候是什么时候?是dealloc前还是dealloc后?

对于associate object,其dealloc方法,是在其宿主X的dealloc方法调用完毕以后,也就是宿主X已经被销毁之后,才调用的。也就是说,delegate的置空是在delegate被销毁之后。无论之间间隔多么短,总是有那么一瞬间,X已经被销毁了,delegate还没有被置空,dangling pointer出现,如果是在多线程的场景下,就有可能有另外的线程在此时访问到了这个dangling pointer,程序依然会crash。

所以,基于associate object的解决方案,归根结底是无法解决线程安全的问题的。

那么怎样才能做出一个线程安全的dangling pointer delegate问题的解决方案呢?

思路4:

既然问题出在associate object上,那我们就不用它,想想有没有其它实现X与P一对一绑定(对应)的方法。这时我们又想起了weak指针。系统是怎么做到将object与指向其的weak指针集合绑定(对应)在一起的呢?

关于weak指针的实现,我们可以在llvm.org上看到相关的文档内容(http://clang.llvm.org/docs/AutomaticReferenceCounting.html),但是不够详细。更直接的方式是阅读http://www.opensource.apple.com/source/objc4/里面的NSObject和runtime实现的源码。

简而言之,编译器实现的weak指针与我们的中心思想是一致的,即用一种方法绑定对象X和一个指向X的需要监视的指针集合,并在X dealloc之时自动将集合内元素置空。只不过与associate object的方法相比,有两点不同:

1. 绑定对象,用的是一个全局的hash table(SideTable),而非associate object。hash table的key对应一个对象X,value为指针集合。

2. dealloc之时,指的是X的dealloc方法调用过程之中,而非最终销毁以后,这样就不存在天然的缺陷,其线程安全问题是可以通过在hash table上加锁来解决的。

按照这个思路,我们来派生一个新的DelegateDictParasite类,实现另一种利用CFDictionary的绑定(对应)的方法:

 @implementation DelegateDictParasite

+ (DelegateDictParasite *)parasitizeIn:(id)hostObj
{
    if (!class_getInstanceMethod([hostObj class], @selector(myHostObjDealloc)))
    {
        [DelegateDictParasite addNewMethodToHost:[hostObj class]];
        [DelegateAutoUnregisterHelper mergeOldSEL:[DelegateAutoUnregisterHelper deallocSelector] NewSEL:@selector(myHostObjDealloc) ForClass:[hostObj class]];
        [DelegateAutoUnregisterHelper mergeOldSEL:[DelegateAutoUnregisterHelper releaseSelector] NewSEL:@selector(myHostObjRelease) ForClass:[hostObj class]];
    }

    DelegateDictParasite *parasite;

    @synchronized(kDelegateAssociativeParasiteLock)
    {
        if (!delegateHostParasiteHashTable)
        {
            delegateHostParasiteHashTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        }

        parasite = [[DelegateDictParasite alloc] init];

        CFDictionarySetValue(delegateHostParasiteHashTable, (__bridge const void *)(hostObj), (__bridge const void *)(parasite));
    }

    return parasite;
}

+ (DelegateDictParasite *)getParasiteFromHost:(id)hostObj
{
    DelegateDictParasite *parasite;

    @synchronized(kDelegateAssociativeParasiteLock)
    {
        if (!delegateHostParasiteHashTable)
        {
            return nil;
        }

        parasite = CFDictionaryGetValue(delegateHostParasiteHashTable, (__bridge const void *)(hostObj));
    }

    return parasite;
}

@end

这里,由于没有了associate object的帮助,X dealloc与parasite dealloc的联动需要我们自己触发,同样利用runtime,我们可以改写每一个X的dealloc方法来完成这种联动,解除hash table中对X的绑定,从而引发自动置空。

另外,通过锁,我们可以解决线程安全问题。从而解决多线程下delegate的dangling pointer问题。(完整代码见附录)

这种思路,其优点有二:

一、没有了先天缺陷,解决了线程安全问题,从而可以推广到广义的dangling pointer delegate问题上。

二、用法与思路三一样,比较简单。

其缺点有二:

一、用了全局的一个hash table。一般有洁癖的人看到全局变量会不舒服。

二、对每一个成为delegate的对象X的类,都会修改其dealloc方法,不像associate object的联动那么自然,有点不干净。

思路5:

GitHub上有一个mikeash写的开源项目MAZeroingWeakRef,目的是在不支持weak的情况下提供一个weak指针的实现,其实现思想也是与系统weak指针类似,即利用全局hash table来做。与思路4不同的是,它修改X的dealloc方法,是通过动态继承出X的一个子类,然后在子类上addMethod的方式,而不是利用method_exchangeImplementation。

这个项目考虑了更多的情况,比如说对于KVO的支持以及toll-free的CF对象的处理(不过用到了私有API)等等,大家有兴趣和时间的话可以研究一下,不再赘述。

其优点有二:

一、考虑了KVO/CF等情况的支持,更加严谨。

二、动态继承的方式把dealloc方法修改的范围缩小到只是使用weak的实例而不是此类的所有实例,解决了思路4的缺点二。

其缺点有二:

一、动态继承的方式修改了类的名字。

二、只是用来在weak不能使用的条件下实现weak指针,可以解决自定义的delegate的dangling pointer问题,并不能解决文中已经被指定为assign类型的系统控件delegate的问题。

注:本文由于篇幅所限,实现过程中一些坑和有意思的地方并未一一提及。例如修改方法实现的时候,需要注意修改的是父类方法还是子类方法;一些方法实现只能放在非ARC(添加-fno-objc-arc标志)文件中;等等。

【总结】

本文逐步思考并总结的几种解决dangling pointer问题的思路各有优缺点,并不存在哪种一定最好,要具体情况具体分析。相比之下,思路4是解决多线程delegate的dangling pointer的较为完整的解决方案。思考和实现过程当中还有很多不成熟的地方,欢迎大家一起讨论、不正确的地方也欢迎批评指正。

时间: 2024-11-05 13:47:58

iOS上Delegate的悬垂指针问题的相关文章

转iOS中delegate、protocol的关系

iOS中delegate.protocol的关系 分类: iOS Development2014-02-12 10:47 277人阅读 评论(0) 收藏 举报 delegateiosprocotolcategoryobject-c 刚开始接触iOS,对delegate.protocol这两个概念比较模糊.参考了一些资料,记录下来体会. 1.protocol protocol和interface的概念类似,是object-c语法的一部分.protocol就是一系列不属于任何类的方法的列表.其中声明

指针辨析:悬垂指针、哑指针、野指针、智能指针

悬垂指针: 1:提出的原因: 请看下面的代码片段: [cpp] view plaincopyprint? int *p=NULL; void main() { int i=10;p=&i; cout<<"第一次:*p = "<<*p<<endl; cout<<"第二次:*p = "<<*p<<endl; } [cpp] view plaincopyprint? int *p=NULL;

iOS上获取iTunes音乐权限

方法一: MPMediaQuery *mysongsQuery = [MPMediaQuery songsQuery]; _songsArray = [mysongsQuery items]; for (MPMediaItem *song in _songsArray) { NSString *songTitle = [song valueForProperty:MPMediaItemPropertyTitle]; NSString *assetUrl = [song valueForPrope

iOS 上的 WebSocket 框架 Starscream

原文:WebSockets on iOS with Starscream 作者:Aaron Douglas 译者:kmyhy 传统的网络技术 (也就是 Berkeley sockets) 被认为是可靠和稳定的.但是 Berkeley socket 在某些 web 技术,比如代理和防火墙下不太好使.WebSocket 出现于 2011 年,是一种在客户端和服务端之间建立双向通讯的新技术.WebSocket 比起多个 HTTP 请求来说更有效率并允许长连接. 在 iOS 上使用 WebSocket

Dangling pointer(悬垂指针、迷途指针)和 Wild pointer(野指针)

一.迷途指针(悬垂指针) 在计算机编程领域中,迷途指针与野指针指的是不指向任何合法的对象的指针. 当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称迷途指针(悬垂指针).若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,则将产生无法预料的后果.因为此时迷途指针所指向的内存现在包含的已经完全是不同的数据.通常来说,若原来的程序继续往迷途指针所指向的内存地址写入数据,这些和原来程序不相关的数

IOS上传头像

//1   ASI post传图片 NSURL *url=[NSURL URLWithString:[BASEURL stringByAppendingString:@"/user/post_face.php"]]; ASIFormDataRequest *request=[ASIFormDataRequest requestWithURL:url]; request.delegate=self; request.timeOutSeconds=120; [email protected

【转】iOS 上常用的两个功能:点击屏幕和return退出隐藏键盘和解决虚拟键盘挡住UITextField的方法

iOS上面对键盘的处理很不人性化,所以这些功能都需要自己来实现, 首先是点击return和屏幕隐藏键盘 这个首先引用双子座的博客 http://my.oschina.net/plumsoft/blog/42545,他的文章写的很好,对大家的理解很有好处. 在 iOS 程序中当想要在文本框中输入数据,轻触文本框会打开键盘.对于 iPad 程序,其键盘有一个按钮可以用来关闭键盘,但是 iPhone 程序中的键盘却没有这样的按钮,不过我们可以采取一些方法关闭它.例如,我们可以实现按下 Rerun (有

iOS开发Delegate,Notification,Block使用心得

(一)简要介绍 1.Delegate(代理.委托) 代理几乎是iOS开发中最常用的传值方式,在项目中的AppDelegate就是使用的这种设计模式,不仅如此,还有很多原生的控件也使用的这种设计模式,比如:UITextFiled,UITableView等等.官方给出的解释如下: Delegation is a simple and powerful pattern in which one object in a program 1 acts on behalf of, or in coordin

关于iOS上使用WWW引起的内存泄漏的临时解决方案

原地址:http://www.unity蛮牛.com/thread-16493-1-1.html 目前,在的4.3.3.和4.3.4版本中存在一个iOS平台上的内存泄漏问题,即当使用WWW来下载和加载Assetbundle文件时,如果按照以下方式来进行,则会造成Assetbundle卸载后内存不能完全释放的情况. 对此,Unity官方已经就该问题进行了修复,并在下一个版本中进行更新.但对于目前仍在使用4.3.3和4.3.4两个版本上进行开发的项目,建议使用以下的临时解决方案,具体步骤如下: 目前