Skip to content

Commit

Permalink
Fixed memory leak found during analyzing and profiling phases.
Browse files Browse the repository at this point in the history
Fixed GC ARC issue which may lead to application crash while retrieving GC released bundle seed identifier string value.
For some reasons GC releases the value of string variable on the next method invocation.
As a result it becomes a zombie object referencing to an unallocated memory.
Assigning its value on the first method call to a static variable by explicit object copy resolves the issue.
More info can be found on the following link, however the issue is still very specific for this case:
http://stackoverflow.com/questions/6316723/why-retain-a-static-variable
  • Loading branch information
oleksinski committed Nov 18, 2015
1 parent fde8ba8 commit 286d3e0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 28 deletions.
4 changes: 2 additions & 2 deletions Example/Podfile.lock
@@ -1,6 +1,6 @@
PODS:
- Expecta (0.3.1)
- JNKeychain (0.1.2)
- JNKeychain (0.1.3)
- Specta (0.2.1)

DEPENDENCIES:
Expand All @@ -14,7 +14,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
Expecta: a354d4633409dd9fe8c4f5ff5130426adbe31628
JNKeychain: 3428e123958aca6672b55d615a8a45cdf7b82c71
JNKeychain: 4e3d300a03c6d747309af66edaf161b9b772165f
Specta: 15a276a6343867b426d5ed135d5aa4d04123a573

COCOAPODS: 0.39.0
65 changes: 39 additions & 26 deletions Pod/Classes/JNKeychain.m
Expand Up @@ -36,7 +36,7 @@ + (NSMutableDictionary *)getKeychainQuery:(NSString *)key forAccessGroup:(NSStri
[keychainQuery setObject:[self getFullAppleIdentifier:group] forKey:(__bridge id)kSecAttrAccessGroup];
}

return [keychainQuery mutableCopy];
return keychainQuery;
}

/**
Expand Down Expand Up @@ -84,7 +84,7 @@ + (BOOL)deleteValueForKey:(NSString *)key forAccessGroup:(NSString *)group

+ (BOOL)deleteValueForKey:(NSString *)key
{
return[self deleteValueForKey:key forAccessGroup:nil];
return [self deleteValueForKey:key forAccessGroup:nil];
}

#pragma mark -
Expand Down Expand Up @@ -125,31 +125,44 @@ + (id)loadValueForKey:(NSString *)key

+ (NSString *)getBundleSeedIdentifier
{
static NSString *bundleSeedID = nil;
static dispatch_once_t _once;
dispatch_once(&_once, ^{
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
(__bridge id)kSecAttrAccount: @"bundleSeedID",
(__bridge id)kSecAttrService: @"",
(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound)
{
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
}
if (status == errSecSuccess)
{
NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:@"."];
bundleSeedID = [[components objectEnumerator] nextObject];
CFRelease(result);
static __strong NSString *bundleSeedIdentifier = nil;

if (bundleSeedIdentifier == nil)
{
@synchronized(self) {
if (bundleSeedIdentifier == nil)
{
NSString *_bundleSeedIdentifier = nil;
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
(__bridge id)kSecAttrAccount: @"bundleSeedID",
(__bridge id)kSecAttrService: @"",
(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound)
{
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
}
if (status == errSecSuccess)
{
NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:@"."];
_bundleSeedIdentifier = [[components objectEnumerator] nextObject];
CFRelease(result);
}
//
// For some reasons GC releases the value of _bundleSeedIdentifier string variable on the next method invocation.
// As a result it becomes a zombie object referencing to an unallocated memory.
// Assigning its value on the first method call to a static variable by explicit object copy resolves the issue.
//
bundleSeedIdentifier = [_bundleSeedIdentifier copy];
}
}
});
}

return bundleSeedID;
return bundleSeedIdentifier;
}

@end
@end

0 comments on commit 286d3e0

Please sign in to comment.