如何使用 ARC 在 iOS 中处理自动释放的对象
Posted
技术标签:
【中文标题】如何使用 ARC 在 iOS 中处理自动释放的对象【英文标题】:How to handle autoreleased objects in iOS with ARC 【发布时间】:2013-03-19 09:23:32 【问题描述】:我正在创建一个启用了 ARC 的 iPhone 应用程序,我遇到了这种情况。
在应用程序的每个页面中,都发生了 Web 服务调用。在这种方法中,我在从服务器添加新值之前从数组中删除所有对象。一切正常,但有时应用程序在 [self.myArray removeAllObjects]
处崩溃。
我已经为 myArray @property (strong, nonatomic) NSMutableArray myArray;
设置了@property
我在想的是,当我使用 ARC 时,对象 myArray
在某个时候被释放,我尝试从同一个数组中删除所有对象。导致崩溃的原因,我不确定,但我没有看到任何其他原因。
所以,我想在删除其中的对象之前检查数组是否有效。我写了一个示例代码来检查不同的场景。这里是:
NSMutableArray *testArray = [[NSMutableArray alloc]initWithObjects:@"1", @"2", @"3", @"4", nil];
if (testArray)
NSLog(@"i am alive");
[testArray release];
if (testArray) //here how to check whether this array is valid or not?
NSLog(@"i am alive: %@", testArray);
else
NSLog(@"I am dead");
testArray = [[NSMutableArray alloc]initWithObjects:@"5", @"6", @"7", @"8", nil];
[testArray release];
[testArray removeAllObjects];
我知道它会导致崩溃,但这只是为了检查。在这里,如何检查数组是否有效?这是一个正确的方法还是别的什么?
请指导我。 谢谢。
实际代码:
- (void)getFriendsList
BOOL netIsAvailable = [self connected];
if (netIsAvailable)
@try
NSString *accessToken = [self getAccessToken];
NSString *tokenEncoded = [accessToken stringByReplacingOccurrencesOfString:@"\"" withString:@""];
NSString *finalUrl = [NSString stringWithFormat:@"%@FriendsnSongs/%@",CommonWebServiceUrl,tokenEncoded];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:finalUrl] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *currentResult = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// NSString* responseString = [[NSString alloc] initWithData:currentResult encoding:NSUTF8StringEncoding];
if (currentResult != nil)
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:currentResult
options:kNilOptions
error:&error];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
int statusCode = [httpResponse statusCode];
NSMutableArray *match = [json valueForKey:@"FriendsnSongsResult"];
if (statusCode == 200)
if (self.userGuidArray)
[self.albumNameArray removeAllObjects]; //here got crash
[self.artistNameArray removeAllObjects];
[self.deviceNameArray removeAllObjects]; //here also got crash once
[self.nickNameArray removeAllObjects];
[self.userProfileImageArray removeAllObjects];
[self.songTitleArray removeAllObjects];
[self.songStatusArray removeAllObjects];
[self.userGuidArray removeAllObjects];
for(NSArray *player in match)
[self.albumNameArray addObject:[(NSArray *)player valueForKey:@"AlbumName"]];
[self.artistNameArray addObject:[(NSArray *)player valueForKey:@"Artist"]];
[self.nickNameArray addObject:[(NSArray *)player valueForKey:@"NickName"]];
[self.userProfileImageArray addObject:[(NSArray *)player valueForKey:@"ProfileImage"]];
[self.songStatusArray addObject:[(NSArray *)player valueForKey:@"Status"]];
[self.songTitleArray addObject:[(NSArray *)player valueForKey:@"Title"]];
[self.userGuidArray addObject:[(NSArray *)player valueForKey:@"UserGuid"]];
[self.deviceNameArray addObject:[(NSArray *)player valueForKey:@"DeviceName"]];
//Start timer for updating the friends list
if (timerActivated)
[self performSelectorOnMainThread:@selector(backgroundFriendsListUpdate) withObject:nil waitUntilDone:NO];
//Update table if the table not in search mode
if (!isSearching)
[self performSelectorOnMainThread:@selector(updateTable) withObject:nil waitUntilDone:YES];
@catch (NSException *exception)
NSLog(@"Exception: %@",exception.name);
[DejalActivityView removeView];
重要的一点是,在这个类中,每 30 秒就会运行一个后台线程。它将调用相同的方法来刷新表。
【问题讨论】:
wt 是错误的,为什么你使用 testArray 版本];即使你使用ARC ???? 一旦发布就无法测试,因为那里什么都没有了。我更倾向于找到问题的根源,而不是绕开它。 如果它只在某些时候发生,很可能您有两个线程访问同一个实例。你得到的确切崩溃是什么? 对已释放的对象执行任何操作都会导致崩溃,如果这就是您的要求? @MithuzzNSRangeException
可能意味着您正在尝试访问数组中超出数组边界的索引处的某个对象。我敢打赌,您正在使用 UITableview 来呈现您的数据,并且由于您从后台线程更新其数据源,NSRangeException
很可能。
【参考方案1】:
如果您使用的是ARC
,那么请从您的代码中删除[testArray release];
这一行,因为ARC
会处理它。否则[testArray release];
写-(void)dealloc
方法。
在删除数组值之前给出条件
if(myArray.count > 0)// because sometime it is good logic for us.
[myArray removeAllObjects];
然后在myArray
中插入数据。
【讨论】:
我也试过了,但还是崩溃了。我尝试在释放数组之后和之前 NSLog 数组的计数,我得到了相同的计数并且它不为零。所以,即使我们释放了一个数组,计数还是一样的?? 我添加了异常断点,所以错误发生在[self.myArray removeAllObjects];而且我还使用了 try 和 catch,所以在异常部分,异常是 NSRangeException。 @Mithuzz-如果你输入整个错误信息,那么我会在没有错误信息的情况下帮助你,我该如何帮助你? :) 所以请放它:) 我明白你为什么要询问错误消息,但我今天只收到一次错误消息,在控制台中我没有收到任何错误消息,只有 NSRangeException 消息。而且很难重现这个问题。 在你的方案中启用 NSZombiesEnabled,看看你的应用程序是否捕捉到任何东西。这将导致您的应用程序将对象在内存中保留更长时间,以查看是否访问了已释放的实例。如果您在数组从内存中释放后对数组进行 NSLog 记录,则它不会有计数,因为它不存在。丢失 [array release] 调用,在 ARC 中处理objective-c对象时不需要它们,并且会使事情复杂化。【参考方案2】:正如您在 cmets 中所建议的,使用临时数组是个好主意。以下代码将填充一个临时数组并使用它来替换主线程中的数组。
NSMutableArray *tempAlbumArray = [NSMutableArray array];
//fill your array
[tempAlbumArray addObject:<#some object#>];
dispatch_async(dispatch_get_main_queue(), ^
//replace your array with the new one
//this code will be executed in main thread
self.albumNameArray = tempAlbumArray;
);
【讨论】:
目前应用运行良好,希望能解决问题。谢谢。以上是关于如何使用 ARC 在 iOS 中处理自动释放的对象的主要内容,如果未能解决你的问题,请参考以下文章