lib/objcthin.rb in objcthin-0.2.0 vs lib/objcthin.rb in objcthin-0.3.0
- old
+ new
@@ -1,20 +1,23 @@
require "objcthin/version"
require 'thor'
require 'rainbow'
require 'pathname'
+require 'singleton'
module Objcthin
class Command < Thor
desc 'findsel','find unused method sel'
+ method_option :prefix, :default => '', :type => :string, :desc => 'the class prefix you want find'
def findsel(path)
- Imp::UnusedClass.find_unused_sel(path)
+ Imp::UnusedClass.instance.find_unused_sel(path, options[:prefix])
end
desc 'findclass', 'find unused class list'
+ method_option :prefix => :string, :default => '', :desc => 'the class prefix you want find'
def findclass(path)
- Imp::UnusedClass.find_unused_class(path)
+ Imp::UnusedClass.instance.find_unused_class(path, options[:prefix])
end
desc'version','print version'
def version
puts Rainbow(Objcthin::VERSION).green
@@ -22,11 +25,14 @@
end
end
module Imp
class UnusedClass
- def self.find_unused_sel(path)
+
+ include Singleton
+
+ def find_unused_sel(path, prefix)
check_file_type(path)
all_sels = find_impl_methods(path)
used_sel = reference_selectors(path)
unused_sel = []
@@ -35,16 +41,22 @@
unless used_sel.include?(sel)
unused_sel += class_and_sels
end
end
- puts Rainbow('below selector is unused:\n').red
+ puts Rainbow('below selector is unused:').red
+ if prefix
+ unused_sel.select! do |classname_selector|
+ current_prefix = classname_selector.byteslice(2, prefix.length)
+ current_prefix == prefix
+ end
+ end
puts unused_sel
end
- def self.check_file_type(path)
+ def check_file_type(path)
pathname = Pathname.new(path)
unless pathname.exist?
raise "#{path} not exit!"
end
@@ -56,59 +68,64 @@
end
puts Rainbow('will begin process...').green
pathname
end
- def self.find_impl_methods(path)
-
- app = %w[1 2]
-
+ def find_impl_methods(path)
apple_protocols = [
'tableView:canEditRowAtIndexPath:',
- 'commitEditingStyle:forRowAtIndexPath:',
- 'tableView:viewForHeaderInSection:',
- 'tableView:cellForRowAtIndexPath:',
- 'tableView:canPerformAction:forRowAtIndexPath:withSender:',
- 'tableView:performAction:forRowAtIndexPath:withSender:',
- 'tableView:accessoryButtonTappedForRowWithIndexPath:',
- 'tableView:willDisplayCell:forRowAtIndexPath:',
+ 'commitEditingStyle:forRowAtIndexPath:',
+ 'tableView:viewForHeaderInSection:',
+ 'tableView:cellForRowAtIndexPath:',
+ 'tableView:canPerformAction:forRowAtIndexPath:withSender:',
+ 'tableView:performAction:forRowAtIndexPath:withSender:',
+ 'tableView:accessoryButtonTappedForRowWithIndexPath:',
+ 'tableView:willDisplayCell:forRowAtIndexPath:',
'tableView:commitEditingStyle:forRowAtIndexPath:',
'tableView:didEndDisplayingCell:forRowAtIndexPath:',
'tableView:didEndDisplayingHeaderView:forSection:',
'tableView:heightForFooterInSection:',
'tableView:shouldHighlightRowAtIndexPath:',
'tableView:shouldShowMenuForRowAtIndexPath:',
'tableView:viewForFooterInSection:',
'tableView:willDisplayHeaderView:forSection:',
'tableView:willSelectRowAtIndexPath:',
'willMoveToSuperview:',
- 'numberOfSectionsInTableView:',
- 'actionSheet:willDismissWithButtonIndex:',
- 'gestureRecognizer:shouldReceiveTouch:',
- 'gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:',
- 'gestureRecognizer:shouldReceiveTouch:',
- 'imagePickerController:didFinishPickingMediaWithInfo:',
- 'imagePickerControllerDidCancel:',
- 'animateTransition:',
- 'animationControllerForDismissedController:',
- 'animationControllerForPresentedController:presentingController:sourceController:',
- 'navigationController:animationControllerForOperation:fromViewController:toViewController:',
- 'navigationController:interactionControllerForAnimationController:',
- 'alertView:didDismissWithButtonIndex:',
- 'URLSession:didBecomeInvalidWithError:',
- 'setDownloadTaskDidResumeBlock:',
- 'tabBarController:didSelectViewController:',
- 'tabBarController:shouldSelectViewController:',
- 'applicationDidReceiveMemoryWarning:',
- 'application:didRegisterForRemoteNotificationsWithDeviceToken:',
- 'application:didFailToRegisterForRemoteNotificationsWithError:',
- 'application:didReceiveRemoteNotification:fetchCompletionHandler:',
- 'application:didRegisterUserNotificationSettings:',
- 'application:performActionForShortcutItem:completionHandler:',
- 'application:continueUserActivity:restorationHandler:'].freeze
+ 'scrollViewDidEndScrollingAnimation:',
+ 'scrollViewDidZoom',
+ 'scrollViewWillEndDragging:withVelocity:targetContentOffset:',
+ 'searchBarTextDidEndEditing:',
+ 'searchBar:selectedScopeButtonIndexDidChange:',
+ 'shouldInvalidateLayoutForBoundsChange:',
+ 'textFieldShouldReturn:',
+ 'numberOfSectionsInTableView:',
+ 'actionSheet:willDismissWithButtonIndex:',
+ 'gestureRecognizer:shouldReceiveTouch:',
+ 'gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:',
+ 'gestureRecognizer:shouldReceiveTouch:',
+ 'imagePickerController:didFinishPickingMediaWithInfo:',
+ 'imagePickerControllerDidCancel:',
+ 'animateTransition:',
+ 'animationControllerForDismissedController:',
+ 'animationControllerForPresentedController:presentingController:sourceController:',
+ 'navigationController:animationControllerForOperation:fromViewController:toViewController:',
+ 'navigationController:interactionControllerForAnimationController:',
+ 'alertView:didDismissWithButtonIndex:',
+ 'URLSession:didBecomeInvalidWithError:',
+ 'setDownloadTaskDidResumeBlock:',
+ 'tabBarController:didSelectViewController:',
+ 'tabBarController:shouldSelectViewController:',
+ 'applicationDidReceiveMemoryWarning:',
+ 'application:didRegisterForRemoteNotificationsWithDeviceToken:',
+ 'application:didFailToRegisterForRemoteNotificationsWithError:',
+ 'application:didReceiveRemoteNotification:fetchCompletionHandler:',
+ 'application:didRegisterUserNotificationSettings:',
+ 'application:performActionForShortcutItem:completionHandler:',
+ 'application:continueUserActivity:restorationHandler:'].freeze
# imp -[class sel]
+
sub_patten = /[+|-]\[.+\s(.+)\]/
patten = /\s*imp\s*(#{sub_patten})/
sel_set_patten = /set[A-Z].*:$/
sel_get_patten = /is[A-Z].*/
@@ -137,11 +154,11 @@
end
imp.sort
end
- def self.reference_selectors(path)
+ def reference_selectors(path)
patten = /__TEXT:__objc_methname:(.+)/
output = `/usr/bin/otool -v -s __DATA __objc_selrefs #{path}`
sels = []
output.each_line do |line|
@@ -157,11 +174,13 @@
module Imp
class UnusedClass
- def self.check_file_type(path)
+ include Singleton
+
+ def check_file_type(path)
pathname = Pathname.new(path)
unless pathname.exist?
raise "#{path} not exit!"
end
@@ -169,30 +188,49 @@
output = `#{cmd}`
unless output.include?('Mach-O')
raise 'input file not mach-o file type'
end
- puts Rainbow('will begin process...').green
+ #puts Rainbow('will begin process...').green
pathname
end
- def self.split_segment_and_find(path)
- command = "/usr/bin/otool -arch arm64 -V -o #{path}"
+ def split_segment_and_find(path, prefix)
+
+ arch_command = "lipo -info #{path}"
+ arch_output = `#{arch_command}`
+
+ arch = 'arm64'
+ if arch_output.include? 'arm64'
+ arch = 'arm64'
+ elsif arch_output.include? 'x86_64'
+ arch = 'x86_64'
+ elsif arch_output.include? 'armv7'
+ arch = 'armv7'
+ end
+
+ command = "/usr/bin/otool -arch #{arch} -V -o #{path}"
output = `#{command}`
class_list_identifier = 'Contents of (__DATA,__objc_classlist) section'
class_refs_identifier = 'Contents of (__DATA,__objc_classrefs) section'
unless output.include? class_list_identifier
raise Rainbow('only support iphone target, please use iphone build...').red
end
patten = /Contents of \(.*\) section/
- class_refs_patten = /^\d*\w*\s(0x\d*\w*).*/
+ name_patten_string = '.*'
+ if prefix.length > 0
+ name_patten_string = "#{prefix}.*"
+ end
+ vmaddress_to_class_name_patten = /^(\d*\w*)\s(0x\d*\w*)\s_OBJC_CLASS_\$_(#{name_patten_string})/
+
class_list = []
class_refs = []
+ used_vmaddress_to_class_name_hash = {}
can_add_to_list = false
can_add_to_refs = false
output.each_line do |line|
@@ -211,47 +249,41 @@
if can_add_to_list
class_list << line
end
if can_add_to_refs && line
- class_refs_patten.match(line) do |m|
- class_refs << m[1]
+ vmaddress_to_class_name_patten.match(line) do |m|
+ unless used_vmaddress_to_class_name_hash[m[2]]
+ used_vmaddress_to_class_name_hash[m[2]] = m[3]
+ end
end
end
end
+ # remove cocoapods class
+ podsd_dummy = 'PodsDummy'
- class_list_address_patten = /^(\d*\w*)\s(0x\d*\w*)/
- class_name_patten = /name\s0x\d*\w*\s(.*)/
-
- current_key = nil
- class_name_address_hash = {}
-
+ vmaddress_to_class_name_hash = {}
class_list.each do |line|
- if class_list_address_patten.match?(line)
- current_key = class_list_address_patten.match(line)[2]
+ next if line.include? podsd_dummy
+ vmaddress_to_class_name_patten.match(line) do |m|
+ vmaddress_to_class_name_hash[m[2]] = m[3]
end
-
- if class_name_patten.match?(line) && current_key
- value = class_name_patten.match(line)[1]
- class_name_address_hash[current_key] = value
- current_key = nil
- end
end
- result = class_name_address_hash
- class_refs.each do |line|
- if class_name_address_hash.keys.include?(line)
- result.delete(line)
+ result = vmaddress_to_class_name_hash
+ vmaddress_to_class_name_hash.each do |key, value|
+ if used_vmaddress_to_class_name_hash.keys.include?(key)
+ result.delete(key)
end
end
result
end
- def self.find_unused_class(path)
+ def find_unused_class(path, prefix)
check_file_type(path)
- result = split_segment_and_find(path)
+ result = split_segment_and_find(path, prefix)
puts result.values
end
end
\ No newline at end of file