Преглед изворни кода

Add ability to hide property-backing ivars+methods

Tanner Bennett пре 6 година
родитељ
комит
877a1db87b

+ 3 - 8
Classes/ExplorerInterface/FLEXExplorerViewController.m

@@ -21,8 +21,7 @@
 #import "FLEXTabsViewController.h"
 #import "FLEXTabsViewController.h"
 #import "FLEXWindowManagerController.h"
 #import "FLEXWindowManagerController.h"
 #import "FLEXViewControllersViewController.h"
 #import "FLEXViewControllersViewController.h"
-
-static NSString *const kFLEXToolbarTopMarginDefaultsKey = @"com.flex.FLEXToolbar.topMargin";
+#import "NSUserDefaults+FLEX.h"
 
 
 typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     FLEXExplorerModeDefault,
     FLEXExplorerModeDefault,
@@ -96,8 +95,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     self.explorerToolbar = [FLEXExplorerToolbar new];
     self.explorerToolbar = [FLEXExplorerToolbar new];
 
 
     // Start the toolbar off below any bars that may be at the top of the view.
     // Start the toolbar off below any bars that may be at the top of the view.
-    id toolbarOriginYDefault = [[NSUserDefaults standardUserDefaults] objectForKey:kFLEXToolbarTopMarginDefaultsKey];
-    CGFloat toolbarOriginY = toolbarOriginYDefault ? [toolbarOriginYDefault doubleValue] : 100;
+    CGFloat toolbarOriginY = NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin;
 
 
     CGRect safeArea = [self viewSafeArea];
     CGRect safeArea = [self viewSafeArea];
     CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(
     CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(
@@ -520,10 +518,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     }
     }
 
 
     self.explorerToolbar.frame = unconstrainedFrame;
     self.explorerToolbar.frame = unconstrainedFrame;
-
-    [NSUserDefaults.standardUserDefaults
-        setDouble:unconstrainedFrame.origin.y forKey:kFLEXToolbarTopMarginDefaultsKey
-    ];
+    NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin = unconstrainedFrame.origin.y;
 }
 }
 
 
 - (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR {
 - (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR {

+ 0 - 2
Classes/GlobalStateExplorers/SystemLog/FLEXOSLogController.h

@@ -10,8 +10,6 @@
 
 
 #define FLEXOSLogAvailable() (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 10)
 #define FLEXOSLogAvailable() (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 10)
 
 
-extern NSString * const kFLEXiOSPersistentOSLogKey;
-
 /// The log controller used for iOS 10 and up.
 /// The log controller used for iOS 10 and up.
 @interface FLEXOSLogController : NSObject <FLEXLogController>
 @interface FLEXOSLogController : NSObject <FLEXLogController>
 
 

+ 2 - 4
Classes/GlobalStateExplorers/SystemLog/FLEXOSLogController.m

@@ -7,11 +7,10 @@
 //
 //
 
 
 #import "FLEXOSLogController.h"
 #import "FLEXOSLogController.h"
+#import "NSUserDefaults+FLEX.h"
 #include <dlfcn.h>
 #include <dlfcn.h>
 #include "ActivityStreamAPI.h"
 #include "ActivityStreamAPI.h"
 
 
-NSString * const kFLEXiOSPersistentOSLogKey = @"com.flex.enablePersistentOSLogLogging";
-
 static os_activity_stream_for_pid_t OSActivityStreamForPID;
 static os_activity_stream_for_pid_t OSActivityStreamForPID;
 static os_activity_stream_resume_t OSActivityStreamResume;
 static os_activity_stream_resume_t OSActivityStreamResume;
 static os_activity_stream_cancel_t OSActivityStreamCancel;
 static os_activity_stream_cancel_t OSActivityStreamCancel;
@@ -41,8 +40,7 @@ static uint8_t (*OSLogGetType)(void *);
 + (void)load {
 + (void)load {
     // Persist logs when the app launches on iOS 10 if we have persistent logs turned on
     // Persist logs when the app launches on iOS 10 if we have persistent logs turned on
     if (FLEXOSLogAvailable()) {
     if (FLEXOSLogAvailable()) {
-        BOOL persistent = [[NSUserDefaults standardUserDefaults] boolForKey:kFLEXiOSPersistentOSLogKey];
-        if (persistent) {
+        if (NSUserDefaults.standardUserDefaults.flex_cacheOSLogMessages) {
             [self sharedLogController].persistent = YES;
             [self sharedLogController].persistent = YES;
             [[self sharedLogController] startMonitoring];
             [[self sharedLogController] startMonitoring];
         }
         }

+ 3 - 2
Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogViewController.m

@@ -15,6 +15,7 @@
 #import "FLEXColor.h"
 #import "FLEXColor.h"
 #import "FLEXResources.h"
 #import "FLEXResources.h"
 #import "UIBarButtonItem+FLEX.h"
 #import "UIBarButtonItem+FLEX.h"
+#import "NSUserDefaults+FLEX.h"
 #import "fishhook.h"
 #import "fishhook.h"
 #import <dlfcn.h>
 #import <dlfcn.h>
 
 
@@ -191,7 +192,7 @@ BOOL my_os_log_shim_enabled(void *addr) {
 
 
 - (void)showLogSettings {
 - (void)showLogSettings {
     FLEXOSLogController *logController = (FLEXOSLogController *)self.logController;
     FLEXOSLogController *logController = (FLEXOSLogController *)self.logController;
-    BOOL persistent = [[NSUserDefaults standardUserDefaults] boolForKey:kFLEXiOSPersistentOSLogKey];
+    BOOL persistent = NSUserDefaults.standardUserDefaults.flex_cacheOSLogMessages;
     NSString *toggle = persistent ? @"Disable" : @"Enable";
     NSString *toggle = persistent ? @"Disable" : @"Enable";
     NSString *title = [@"Persistent logging: " stringByAppendingString:persistent ? @"ON" : @"OFF"];
     NSString *title = [@"Persistent logging: " stringByAppendingString:persistent ? @"ON" : @"OFF"];
     NSString *body = @"In iOS 10 and up, ASL is gone. The OS Log API is much more limited. "
     NSString *body = @"In iOS 10 and up, ASL is gone. The OS Log API is much more limited. "
@@ -200,7 +201,7 @@ BOOL my_os_log_shim_enabled(void *addr) {
 
 
     [FLEXAlert makeAlert:^(FLEXAlert *make) {
     [FLEXAlert makeAlert:^(FLEXAlert *make) {
         make.title(title).message(body).button(toggle).handler(^(NSArray<NSString *> *strings) {
         make.title(title).message(body).button(toggle).handler(^(NSArray<NSString *> *strings) {
-            [NSUserDefaults.standardUserDefaults setBool:!persistent forKey:kFLEXiOSPersistentOSLogKey];
+            NSUserDefaults.standardUserDefaults.flex_cacheOSLogMessages = !persistent;
             logController.persistent = !persistent;
             logController.persistent = !persistent;
             [logController.messages addObjectsFromArray:self.logMessages.list];
             [logController.messages addObjectsFromArray:self.logMessages.list];
         });
         });

+ 2 - 2
Classes/Network/PonyDebugger/FLEXNetworkObserver.m

@@ -80,7 +80,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 + (void)setEnabled:(BOOL)enabled {
 + (void)setEnabled:(BOOL)enabled {
     BOOL previouslyEnabled = [self isEnabled];
     BOOL previouslyEnabled = [self isEnabled];
     
     
-    [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:kFLEXNetworkObserverEnabledDefaultsKey];
+    [NSUserDefaults.standardUserDefaults setBool:enabled forKey:kFLEXNetworkObserverEnabledDefaultsKey];
     
     
     if (enabled) {
     if (enabled) {
         // Inject if needed. This injection is protected with a dispatch_once, so we're ok calling it multiple times.
         // Inject if needed. This injection is protected with a dispatch_once, so we're ok calling it multiple times.
@@ -94,7 +94,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 }
 }
 
 
 + (BOOL)isEnabled {
 + (BOOL)isEnabled {
-    return [[[NSUserDefaults standardUserDefaults] objectForKey:kFLEXNetworkObserverEnabledDefaultsKey] boolValue];
+    return [[NSUserDefaults.standardUserDefaults objectForKey:kFLEXNetworkObserverEnabledDefaultsKey] boolValue];
 }
 }
 
 
 + (void)load {
 + (void)load {

+ 54 - 1
Classes/ObjectExplorers/FLEXObjectExplorer.m

@@ -15,6 +15,7 @@
 #import "FLEXPropertyAttributes.h"
 #import "FLEXPropertyAttributes.h"
 #import "NSObject+Reflection.h"
 #import "NSObject+Reflection.h"
 #import "FLEXMetadataSection.h"
 #import "FLEXMetadataSection.h"
+#import "NSUserDefaults+FLEX.h"
 
 
 @interface FLEXObjectExplorer () {
 @interface FLEXObjectExplorer () {
     NSMutableArray<NSArray<FLEXProperty *> *> *_allProperties;
     NSMutableArray<NSArray<FLEXProperty *> *> *_allProperties;
@@ -93,6 +94,10 @@
     _allImageNames = [NSMutableArray new];
     _allImageNames = [NSMutableArray new];
 
 
     [self reloadClassHierarchy];
     [self reloadClassHierarchy];
+    
+    NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
+    BOOL hideBackingIvars = defaults.flex_explorerHidesRedundantIvars;
+    BOOL hidePropertyMethods = defaults.flex_explorerHidesRedundantMethods;
 
 
     // Loop over each class and each superclass, collect
     // Loop over each class and each superclass, collect
     // the fresh and unique metadata in each category
     // the fresh and unique metadata in each category
@@ -147,6 +152,53 @@
     }
     }
     
     
     _classHierarchy = [FLEXStaticMetadata classHierarchy:self.classHierarchyClasses];
     _classHierarchy = [FLEXStaticMetadata classHierarchy:self.classHierarchyClasses];
+    
+    // Potentially filter property-backing ivars
+    if (hideBackingIvars) {
+        NSArray<NSArray<FLEXIvar *> *> *ivars = _allIvars.copy;
+        _allIvars = [ivars flex_mapped:^id(NSArray<FLEXIvar *> *list, NSUInteger idx) {
+            // Get a set of all backing ivar names for the current class in the hierarchy
+            NSSet *ivarNames = [NSSet setWithArray:({
+                [_allProperties[idx] flex_mapped:^id(FLEXProperty *p, NSUInteger idx) {
+                    // Nil if no ivar, and array is flatted
+                    return p.attributes.backingIvar;
+                }];
+            })];
+            
+            // Remove ivars whose name is in the ivar names list
+            return [list flex_filtered:^BOOL(FLEXIvar *ivar, NSUInteger idx) {
+                return ![ivarNames containsObject:ivar.name];
+            }];
+        }];
+    }
+    
+    // Potentially filter property-backing methods
+    if (hidePropertyMethods) {
+        NSArray<NSArray<FLEXMethod *> *> *methods = _allMethods.copy;
+        _allMethods = [methods flex_mapped:^id(NSArray<FLEXMethod *> *list, NSUInteger idx) {
+            // Get a set of all property method names for the current class in the hierarchy
+            NSSet *methodNames = [NSSet setWithArray:({
+                [_allProperties[idx] flex_flatmapped:^NSArray *(FLEXProperty *p, NSUInteger idx) {
+                    if (p.likelyGetterExists) {
+                        if (p.likelySetterExists) {
+                            return @[p.likelyGetterString, p.likelySetterString];
+                        }
+                        
+                        return @[p.likelyGetterString];
+                    } else if (p.likelySetterExists) {
+                        return @[p.likelySetterString];
+                    }
+                    
+                    return nil;
+                }];
+            })];
+            
+            // Remove ivars whose name is in the ivar names list
+            return [list flex_filtered:^BOOL(FLEXMethod *method, NSUInteger idx) {
+                return ![methodNames containsObject:method.selectorString];
+            }];
+        }];
+    }
 
 
     // Set up UIKit helper data
     // Set up UIKit helper data
     // Really, we only need to call this on properties and ivars
     // Really, we only need to call this on properties and ivars
@@ -189,7 +241,8 @@
         } else {
         } else {
             [names addObject:name];
             [names addObject:name];
 
 
-            // Skip methods and properties which are just overrides
+            // Skip methods and properties which are just overrides,
+            // potentially skip ivars and methods associated with properties
             switch (kind) {
             switch (kind) {
                 case FLEXMetadataKindProperties:
                 case FLEXMetadataKindProperties:
                     if ([superclass instancesRespondToSelector:[obj likelyGetter]]) {
                     if ([superclass instancesRespondToSelector:[obj likelyGetter]]) {

+ 69 - 12
Classes/ObjectExplorers/FLEXObjectExplorerViewController.m

@@ -18,11 +18,13 @@
 #import "FLEXTabsViewController.h"
 #import "FLEXTabsViewController.h"
 #import "FLEXBookmarkManager.h"
 #import "FLEXBookmarkManager.h"
 #import "FLEXTableView.h"
 #import "FLEXTableView.h"
+#import "FLEXResources.h"
 #import "FLEXTableViewCell.h"
 #import "FLEXTableViewCell.h"
 #import "FLEXScopeCarousel.h"
 #import "FLEXScopeCarousel.h"
 #import "FLEXMetadataSection.h"
 #import "FLEXMetadataSection.h"
 #import "FLEXSingleRowSection.h"
 #import "FLEXSingleRowSection.h"
 #import "FLEXShortcutsSection.h"
 #import "FLEXShortcutsSection.h"
+#import "NSUserDefaults+FLEX.h"
 #import <objc/runtime.h>
 #import <objc/runtime.h>
 
 
 #pragma mark - Private properties
 #pragma mark - Private properties
@@ -86,6 +88,11 @@
     self.carousel.items = [self.explorer.classHierarchyClasses flex_mapped:^id(Class cls, NSUInteger idx) {
     self.carousel.items = [self.explorer.classHierarchyClasses flex_mapped:^id(Class cls, NSUInteger idx) {
         return NSStringFromClass(cls);
         return NSStringFromClass(cls);
     }];
     }];
+    
+    // ... button for extra options
+    [self addToolbarItems:@[[UIBarButtonItem
+        itemWithImage:FLEXResources.moreIcon target:self action:@selector(moreButtonPressed)
+    ]]];
 
 
     // Swipe gestures to swipe between classes in the hierarchy
     // Swipe gestures to swipe between classes in the hierarchy
     UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]
     UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]
@@ -102,6 +109,11 @@
     [self.tableView addGestureRecognizer:rightSwipe];
     [self.tableView addGestureRecognizer:rightSwipe];
 }
 }
 
 
+- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
+    [self.navigationController setToolbarHidden:NO animated:YES];
+    return YES;
+}
+
 
 
 #pragma mark - Overrides
 #pragma mark - Overrides
 
 
@@ -168,6 +180,9 @@
     return sections.copy;
     return sections.copy;
 }
 }
 
 
+/// In our case, all this does is reload the table view,
+/// or reload the sections' data if we changed places
+/// in the class hierarchy. Doesn't refresh \c self.explorer
 - (void)reloadData {
 - (void)reloadData {
     // Check to see if class scope changed, update accordingly
     // Check to see if class scope changed, update accordingly
     if (self.explorer.classScope != self.selectedScope) {
     if (self.explorer.classScope != self.selectedScope) {
@@ -178,9 +193,31 @@
     [super reloadData];
     [super reloadData];
 }
 }
 
 
+- (void)shareButtonPressed {
+    [FLEXAlert makeSheet:^(FLEXAlert *make) {
+        make.button(@"Add to Bookmarks").handler(^(NSArray<NSString *> *strings) {
+            [FLEXBookmarkManager.bookmarks addObject:self.object];
+        });
+        make.button(@"Copy Description").handler(^(NSArray<NSString *> *strings) {
+            UIPasteboard.generalPasteboard.string = self.explorer.objectDescription;
+        });
+        make.button(@"Copy Address").handler(^(NSArray<NSString *> *strings) {
+            UIPasteboard.generalPasteboard.string = [FLEXUtility addressOfObject:self.object];
+        });
+        make.button(@"Cancel").cancelStyle();
+    } showFrom:self];
+}
+
 
 
 #pragma mark - Private
 #pragma mark - Private
 
 
+/// Unlike \c -reloadData, this refreshes everything, including the explorer.
+- (void)fullyReloadData {
+    [self.explorer reloadMetadata];
+    [self reloadSections];
+    [self reloadData];
+}
+
 - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)gesture {
 - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)gesture {
     if (gesture.state == UIGestureRecognizerStateEnded) {
     if (gesture.state == UIGestureRecognizerStateEnded) {
         switch (gesture.direction) {
         switch (gesture.direction) {
@@ -223,23 +260,43 @@
     
     
     return YES;
     return YES;
 }
 }
-
-- (void)shareButtonPressed {
+    
+- (void)moreButtonPressed {
+    NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
+    BOOL currentlyShowsRedundantIvars = !defaults.flex_explorerHidesRedundantIvars;
+    BOOL currentlyShowsRedundantMethods = !defaults.flex_explorerHidesRedundantMethods;
+    
     [FLEXAlert makeSheet:^(FLEXAlert *make) {
     [FLEXAlert makeSheet:^(FLEXAlert *make) {
-        make.button(@"Add to Bookmarks").handler(^(NSArray<NSString *> *strings) {
-            [FLEXBookmarkManager.bookmarks addObject:self.object];
-        });
-        make.button(@"Copy Description").handler(^(NSArray<NSString *> *strings) {
-            UIPasteboard.generalPasteboard.string = self.explorer.objectDescription;
-        });
-        make.button(@"Copy Address").handler(^(NSArray<NSString *> *strings) {
-            UIPasteboard.generalPasteboard.string = [FLEXUtility addressOfObject:self.object];
-        });
+        make.title(@"Options");
+        
+        if (currentlyShowsRedundantIvars) {
+            make.button(@"Hide Property-Backing Ivars").handler(^(NSArray<NSString *> *strings) {
+                defaults.flex_explorerHidesRedundantIvars = YES;
+                [self fullyReloadData];
+            });
+        } else {
+            make.button(@"Show Property-Backing Ivars").handler(^(NSArray<NSString *> *strings) {
+                defaults.flex_explorerHidesRedundantIvars = NO;
+                [self fullyReloadData];
+            });
+        }
+        
+        if (currentlyShowsRedundantMethods) {
+            make.button(@"Hide Property-Backing Methods").handler(^(NSArray<NSString *> *strings) {
+                defaults.flex_explorerHidesRedundantMethods = YES;
+                [self fullyReloadData];
+            });
+        } else {
+            make.button(@"Show Property-Backing Methods").handler(^(NSArray<NSString *> *strings) {
+                defaults.flex_explorerHidesRedundantMethods = NO;
+                [self fullyReloadData];
+            });
+        }
+        
         make.button(@"Cancel").cancelStyle();
         make.button(@"Cancel").cancelStyle();
     } showFrom:self];
     } showFrom:self];
 }
 }
 
 
-
 #pragma mark - Description
 #pragma mark - Description
 
 
 - (BOOL)shouldShowDescription {
 - (BOOL)shouldShowDescription {

+ 20 - 0
Classes/Utility/Categories/NSUserDefaults+FLEX.h

@@ -0,0 +1,20 @@
+//
+//  NSUserDefaults+FLEX.h
+//  FLEX
+//
+//  Created by Tanner on 3/10/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSUserDefaults (FLEX)
+
+@property (nonatomic) double flex_toolbarTopMargin;
+
+@property (nonatomic) BOOL flex_cacheOSLogMessages;
+
+@property (nonatomic) BOOL flex_explorerHidesRedundantIvars;
+@property (nonatomic) BOOL flex_explorerHidesRedundantMethods;
+
+@end

+ 54 - 0
Classes/Utility/Categories/NSUserDefaults+FLEX.m

@@ -0,0 +1,54 @@
+//
+//  NSUserDefaults+FLEX.m
+//  FLEX
+//
+//  Created by Tanner on 3/10/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "NSUserDefaults+FLEX.h"
+
+static NSString * const kFLEXToolbarTopMarginKey = @"com.flex.FLEXToolbar.topMargin";
+static NSString * const kFLEXHideRedundantIvarsKey = @"com.flipboard.FLEX.hide_redundant_ivars";
+static NSString * const kFLEXHideRedundantMethodsKey = @"com.flipboard.FLEX.hide_redundant_methods";
+static NSString * const kFLEXiOSPersistentOSLogKey = @"com.flipborad.flex.enable_persistent_os_log";
+
+@implementation NSUserDefaults (FLEX)
+
+- (double)flex_toolbarTopMargin {
+    if ([self objectForKey:kFLEXToolbarTopMarginKey]) {
+        return [self doubleForKey:kFLEXToolbarTopMarginKey];
+    }
+    
+    return 100;
+}
+
+- (void)setFlex_toolbarTopMargin:(double)margin {
+    [self setDouble:margin forKey:kFLEXToolbarTopMarginKey];
+}
+
+- (BOOL)flex_cacheOSLogMessages {
+    return [self boolForKey:kFLEXiOSPersistentOSLogKey];
+}
+
+- (void)setFlex_cacheOSLogMessages:(BOOL)cache {
+    [self setBool:cache forKey:kFLEXiOSPersistentOSLogKey];
+}
+
+- (BOOL)flex_explorerHidesRedundantIvars {
+    return [self boolForKey:kFLEXHideRedundantIvarsKey];
+}
+
+- (void)setFlex_explorerHidesRedundantIvars:(BOOL)hide {
+    [self setBool:hide forKey:kFLEXHideRedundantIvarsKey];
+}
+
+- (BOOL)flex_explorerHidesRedundantMethods {
+    return [self boolForKey:kFLEXHideRedundantMethodsKey];
+}
+
+- (void)setFlex_explorerHidesRedundantMethods:(BOOL)hide {
+    [self setBool:hide forKey:kFLEXHideRedundantMethodsKey];
+}
+
+@end

+ 2 - 0
Classes/Utility/Runtime/Objc/FLEXProperty.h

@@ -45,10 +45,12 @@
 /// The (likely) setter, regardless of whether the property is readonly.
 /// The (likely) setter, regardless of whether the property is readonly.
 /// For example, this might be the custom setter.
 /// For example, this might be the custom setter.
 @property (nonatomic, readonly) SEL likelySetter;
 @property (nonatomic, readonly) SEL likelySetter;
+@property (nonatomic, readonly) NSString *likelySetterString;
 /// Not valid unless initialized with the owning class.
 /// Not valid unless initialized with the owning class.
 @property (nonatomic, readonly) BOOL likelySetterExists;
 @property (nonatomic, readonly) BOOL likelySetterExists;
 /// The (likely) getter. For example, this might be the custom getter.
 /// The (likely) getter. For example, this might be the custom getter.
 @property (nonatomic, readonly) SEL likelyGetter;
 @property (nonatomic, readonly) SEL likelyGetter;
+@property (nonatomic, readonly) NSString *likelyGetterString;
 /// Not valid unless initialized with the owning class.
 /// Not valid unless initialized with the owning class.
 @property (nonatomic, readonly) BOOL likelyGetterExists;
 @property (nonatomic, readonly) BOOL likelyGetterExists;
 
 

+ 2 - 0
Classes/Utility/Runtime/Objc/FLEXProperty.m

@@ -116,6 +116,8 @@
     // or the default, regardless of whether the default exists
     // or the default, regardless of whether the default exists
     _likelyGetter = validGetter ?: defaultGetter;
     _likelyGetter = validGetter ?: defaultGetter;
     _likelySetter = validSetter ?: defaultSetter;
     _likelySetter = validSetter ?: defaultSetter;
+    _likelyGetterString = NSStringFromSelector(_likelyGetter);
+    _likelySetterString = NSStringFromSelector(_likelySetter);
 
 
     _isClassProperty = _cls ? class_isMetaClass(_cls) : NO;
     _isClassProperty = _cls ? class_isMetaClass(_cls) : NO;
 }
 }

+ 8 - 0
FLEX.xcodeproj/project.pbxproj

@@ -305,6 +305,8 @@
 		C3DFCD952416BC6500BB7084 /* FLEXFilteringTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */; };
 		C3DFCD952416BC6500BB7084 /* FLEXFilteringTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */; };
 		C3DFCD982416E7DD00BB7084 /* FLEXMutableListSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */; };
 		C3DFCD982416E7DD00BB7084 /* FLEXMutableListSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */; };
 		C3DFCD992416E7DD00BB7084 /* FLEXMutableListSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */; };
 		C3DFCD992416E7DD00BB7084 /* FLEXMutableListSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */; };
+		C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */; };
+		C3DFCDB92418336D00BB7084 /* NSUserDefaults+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */; };
 		C3E5D9FD2316E83700E655DB /* FLEXRuntime+Compare.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */; };
 		C3E5D9FD2316E83700E655DB /* FLEXRuntime+Compare.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */; };
 		C3E5D9FE2316E83700E655DB /* FLEXRuntime+Compare.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */; };
 		C3E5D9FE2316E83700E655DB /* FLEXRuntime+Compare.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */; };
 		C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */; };
 		C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */; };
@@ -650,6 +652,8 @@
 		C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXFilteringTableViewController.m; sourceTree = "<group>"; };
 		C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXFilteringTableViewController.m; sourceTree = "<group>"; };
 		C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMutableListSection.h; sourceTree = "<group>"; };
 		C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMutableListSection.h; sourceTree = "<group>"; };
 		C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXMutableListSection.m; sourceTree = "<group>"; };
 		C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXMutableListSection.m; sourceTree = "<group>"; };
+		C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+FLEX.h"; sourceTree = "<group>"; };
+		C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+FLEX.m"; sourceTree = "<group>"; };
 		C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXRuntime+Compare.h"; sourceTree = "<group>"; };
 		C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXRuntime+Compare.h"; sourceTree = "<group>"; };
 		C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXRuntime+Compare.m"; sourceTree = "<group>"; };
 		C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXRuntime+Compare.m"; sourceTree = "<group>"; };
 		C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectInfoSection.h; sourceTree = "<group>"; };
 		C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectInfoSection.h; sourceTree = "<group>"; };
@@ -1174,6 +1178,8 @@
 		C387C88022E0D22600750E58 /* Categories */ = {
 		C387C88022E0D22600750E58 /* Categories */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */,
+				C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */,
 				C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */,
 				C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */,
 				C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */,
 				C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */,
 				C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */,
 				C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */,
@@ -1439,6 +1445,7 @@
 				C398682923AC370100E9E391 /* FLEXViewShortcuts.h in Headers */,
 				C398682923AC370100E9E391 /* FLEXViewShortcuts.h in Headers */,
 				C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */,
 				C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */,
 				779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */,
 				779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */,
+				C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */,
 				3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */,
 				3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */,
 				C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */,
 				C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */,
 				C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */,
 				C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */,
@@ -1641,6 +1648,7 @@
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 				C398682823AC36EC00E9E391 /* FLEXViewShortcuts.m in Sources */,
 				C398682823AC36EC00E9E391 /* FLEXViewShortcuts.m in Sources */,
+				C3DFCDB92418336D00BB7084 /* NSUserDefaults+FLEX.m in Sources */,
 				C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */,
 				C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */,
 				C3878DBC23A749F70038FDBE /* FLEXFieldEditorViewController.m in Sources */,
 				C3878DBC23A749F70038FDBE /* FLEXFieldEditorViewController.m in Sources */,
 				942DCD871BAE0CA300DB5DC2 /* FLEXKeyboardShortcutManager.m in Sources */,
 				942DCD871BAE0CA300DB5DC2 /* FLEXKeyboardShortcutManager.m in Sources */,